Skip to content

Commit e4b8616

Browse files
committed
Add data-dist-info-metadata to Simple HTML API
1 parent 6772a14 commit e4b8616

File tree

4 files changed

+94
-23
lines changed

4 files changed

+94
-23
lines changed

pulp_python/app/utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
</html>
4242
"""
4343

44+
# TODO in the future: data-requires-python (PEP 503)
45+
# TODO now: strip empty lines
4446
simple_detail_template = """<!DOCTYPE html>
4547
<html>
4648
<head>
@@ -50,7 +52,11 @@
5052
<body>
5153
<h1>Links for {{ project_name }}</h1>
5254
{% for pkg in project_packages %}
53-
<a href="{{ pkg.url }}#sha256={{ pkg.sha256 }}" rel="internal" {% if pkg.provenance -%}
55+
<a href="{{ pkg.url }}#sha256={{ pkg.sha256 }}"
56+
{% if pkg.metadata_sha256 %}
57+
data-dist-info-metadata="sha256={{ pkg.metadata_sha256 }}"
58+
{% endif %}
59+
rel="internal" {% if pkg.provenance -%}
5460
data-provenance="{{ pkg.provenance }}"{% endif %}>{{ pkg.filename }}</a><br/>
5561
{% endfor %}
5662
</body>

pulp_python/tests/functional/api/test_pypi_apis.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66

77
from pulp_python.tests.functional.constants import (
88
PYPI_SERIAL_CONSTANT,
9-
PYTHON_SM_PROJECT_SPECIFIER,
10-
PYTHON_SM_FIXTURE_RELEASES,
11-
PYTHON_SM_FIXTURE_CHECKSUMS,
129
PYTHON_MD_PROJECT_SPECIFIER,
1310
PYTHON_MD_PYPI_SUMMARY,
1411
PYTHON_EGG_FILENAME,
@@ -17,8 +14,6 @@
1714
SHELF_PYTHON_JSON,
1815
)
1916

20-
from pulp_python.tests.functional.utils import ensure_simple
21-
2217

2318
PYPI_LAST_SERIAL = "X-PYPI-LAST-SERIAL"
2419

@@ -213,22 +208,6 @@ def test_simple_redirect_with_publications(
213208
assert response.url == str(urljoin(pulp_content_url, f"{distro.base_path}/simple/"))
214209

215210

216-
@pytest.mark.parallel
217-
def test_simple_correctness_live(
218-
python_remote_factory, python_repo_with_sync, python_distribution_factory
219-
):
220-
"""Checks that the simple api on live distributions are correct."""
221-
remote = python_remote_factory(includes=PYTHON_SM_PROJECT_SPECIFIER)
222-
repo = python_repo_with_sync(remote)
223-
distro = python_distribution_factory(repository=repo)
224-
proper, msgs = ensure_simple(
225-
urljoin(distro.base_url, "simple/"),
226-
PYTHON_SM_FIXTURE_RELEASES,
227-
sha_digests=PYTHON_SM_FIXTURE_CHECKSUMS,
228-
)
229-
assert proper is True, msgs
230-
231-
232211
@pytest.mark.parallel
233212
def test_pypi_json(python_remote_factory, python_repo_with_sync, python_distribution_factory):
234213
"""Checks the data of `pypi/{package_name}/json` endpoint."""

pulp_python/tests/functional/api/test_pypi_simple_api.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
PYTHON_EGG_FILENAME,
99
PYTHON_EGG_SHA256,
1010
PYTHON_EGG_URL,
11+
PYTHON_SM_FIXTURE_CHECKSUMS,
12+
PYTHON_SM_FIXTURE_RELEASES,
1113
PYTHON_SM_PROJECT_SPECIFIER,
1214
PYTHON_WHEEL_FILENAME,
1315
PYTHON_WHEEL_METADATA_SHA256,
1416
PYTHON_WHEEL_SHA256,
1517
PYTHON_WHEEL_URL,
18+
PYTHON_XS_FIXTURE_CHECKSUMS,
1619
)
20+
from pulp_python.tests.functional.utils import ensure_simple
1721

1822
API_VERSION = "1.1"
1923

@@ -22,6 +26,63 @@
2226
PYPI_SIMPLE_V1_JSON = "application/vnd.pypi.simple.v1+json"
2327

2428

29+
@pytest.mark.parallel
30+
def test_simple_html_index_api(
31+
python_remote_factory, python_repo_with_sync, python_distribution_factory
32+
):
33+
remote = python_remote_factory(includes=PYTHON_SM_PROJECT_SPECIFIER)
34+
repo = python_repo_with_sync(remote)
35+
distro = python_distribution_factory(repository=repo)
36+
37+
url = urljoin(distro.base_url, "simple/")
38+
headers = {"Accept": PYPI_SIMPLE_V1_HTML}
39+
40+
response = requests.get(url, headers=headers)
41+
assert response.headers["Content-Type"] == PYPI_SIMPLE_V1_HTML
42+
assert response.headers["X-PyPI-Last-Serial"] == str(PYPI_SERIAL_CONSTANT)
43+
44+
proper, msgs = ensure_simple(
45+
url, PYTHON_SM_FIXTURE_RELEASES, sha_digests=PYTHON_SM_FIXTURE_CHECKSUMS
46+
)
47+
assert proper, f"Simple API validation failed: {msgs}"
48+
49+
50+
def test_simple_html_detail_api(
51+
delete_orphans_pre,
52+
monitor_task,
53+
python_bindings,
54+
python_content_factory,
55+
python_distribution_factory,
56+
python_repo_factory,
57+
):
58+
content_1 = python_content_factory(PYTHON_WHEEL_FILENAME, url=PYTHON_WHEEL_URL)
59+
content_2 = python_content_factory(PYTHON_EGG_FILENAME, url=PYTHON_EGG_URL)
60+
body = {"add_content_units": [content_1.pulp_href, content_2.pulp_href]}
61+
62+
repo = python_repo_factory()
63+
monitor_task(python_bindings.RepositoriesPythonApi.modify(repo.pulp_href, body).task)
64+
distro = python_distribution_factory(repository=repo)
65+
66+
url = f'{urljoin(distro.base_url, "simple/")}shelf-reader'
67+
headers = {"Accept": PYPI_SIMPLE_V1_HTML}
68+
69+
response = requests.get(url, headers=headers)
70+
assert response.headers["Content-Type"] == PYPI_SIMPLE_V1_HTML
71+
assert response.headers["X-PyPI-Last-Serial"] == str(PYPI_SERIAL_CONSTANT)
72+
73+
metadata_sha_digests = {
74+
PYTHON_WHEEL_FILENAME: PYTHON_WHEEL_METADATA_SHA256,
75+
PYTHON_EGG_FILENAME: None, # egg files should not have metadata
76+
}
77+
proper, msgs = ensure_simple(
78+
urljoin(distro.base_url, "simple/"),
79+
{"shelf-reader": [PYTHON_WHEEL_FILENAME, PYTHON_EGG_FILENAME]},
80+
sha_digests=PYTHON_XS_FIXTURE_CHECKSUMS,
81+
metadata_sha_digests=metadata_sha_digests,
82+
)
83+
assert proper, f"Simple API validation failed: {msgs}"
84+
85+
2586
@pytest.mark.parallel
2687
def test_simple_json_index_api(
2788
python_remote_factory, python_repo_with_sync, python_distribution_factory

pulp_python/tests/functional/utils.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,29 @@
44
from lxml import html
55

66

7-
def ensure_simple(simple_url, packages, sha_digests=None):
7+
def _validate_metadata_sha_digest(link, filename, metadata_sha_digests):
8+
"""
9+
Validate data-dist-info-metadata attribute for a release link.
10+
"""
11+
data_dist_info_metadata = link.get("data-dist-info-metadata")
12+
13+
if expected_metadata_sha := metadata_sha_digests.get(filename):
14+
expected_attr = f"sha256={expected_metadata_sha}"
15+
if data_dist_info_metadata != expected_attr:
16+
return (
17+
f"\nFile {filename} has incorrect data-dist-info-metadata: "
18+
f"expected '{expected_attr}', got '{data_dist_info_metadata}'"
19+
)
20+
else:
21+
if data_dist_info_metadata:
22+
return (
23+
f"\nFile {filename} should not have data-dist-info-metadata "
24+
f"but has '{data_dist_info_metadata}'"
25+
)
26+
return ""
27+
28+
29+
def ensure_simple(simple_url, packages, sha_digests=None, metadata_sha_digests=None):
830
"""
931
Tests that the simple api at `url` matches the packages supplied.
1032
`packages`: dictionary of form {package_name: [release_filenames]}
@@ -28,6 +50,9 @@ def explore_links(page_url, page_name, links_found, msgs):
2850
links_found[link.text] = True
2951
if link.get("href"):
3052
legit_found_links.append(link.get("href"))
53+
# Check metadata SHA digest if provided
54+
if metadata_sha_digests and page_name == "release":
55+
msgs += _validate_metadata_sha_digest(link, link.text, metadata_sha_digests)
3156
else:
3257
msgs += f"\nFound {page_name} link without href {link.text}"
3358
else:

0 commit comments

Comments
 (0)