66from pulp_python .tests .functional .constants import (
77 PYPI_SIMPLE_V1_HTML ,
88 PYPI_SIMPLE_V1_JSON ,
9+ PYPI_TEXT_HTML ,
910 PYTHON_SM_PROJECT_SPECIFIER ,
1011)
1112
@@ -59,7 +60,7 @@ def test_simple_cache_hit_miss_and_headers(synced_distro):
5960@pytest .mark .parallel
6061def test_simple_cache_separate_accept_headers (synced_distro ):
6162 """
62- HTML and JSON responses are cached separately.
63+ HTML and JSON responses are cached separately by negotiated media type .
6364 """
6465 url = urljoin (synced_distro .base_url , "simple/" )
6566
@@ -74,6 +75,47 @@ def test_simple_cache_separate_accept_headers(synced_distro):
7475 assert r .headers ["X-PULP-CACHE" ] == "HIT"
7576
7677
78+ @pytest .mark .parallel
79+ def test_simple_cache_format_json_does_not_poison_html (synced_distro ):
80+ """
81+ A ?format=json response must not poison a later request with the same Accept.
82+
83+ Clients like uv/pip send an Accept that allows both JSON and HTML. DRF's
84+ ?format=json overrides negotiation to JSON, while the same Accept without
85+ that query param selects HTML. Caching must key on the negotiated type so
86+ the JSON entry is not served (and re-rendered) for the HTML request.
87+ """
88+ url = f"{ urljoin (synced_distro .base_url , 'simple/' )} aiohttp"
89+ # pip/uv-style Accept: JSON preferred, HTML still acceptable
90+ headers = {
91+ "Accept" : (
92+ f"{ PYPI_SIMPLE_V1_JSON } , { PYPI_SIMPLE_V1_HTML } ;q=0.1, { PYPI_TEXT_HTML } ;q=0.01"
93+ )
94+ }
95+
96+ r_json = requests .get (url , headers = headers , params = {"format" : "json" })
97+ assert r_json .status_code == 200
98+ assert PYPI_SIMPLE_V1_JSON in r_json .headers ["Content-Type" ]
99+ assert r_json .headers ["X-PULP-CACHE" ] == "MISS"
100+ assert r_json .json ()["name" ] == "aiohttp"
101+
102+ r_html = requests .get (url , headers = headers )
103+ assert r_html .status_code == 200
104+ assert PYPI_TEXT_HTML in r_html .headers ["Content-Type" ]
105+ assert r_html .headers ["X-PULP-CACHE" ] == "MISS"
106+ assert b"<a href=" in r_html .content
107+
108+ r_html_hit = requests .get (url , headers = headers )
109+ assert r_html_hit .status_code == 200
110+ assert r_html_hit .headers ["X-PULP-CACHE" ] == "HIT"
111+ assert PYPI_TEXT_HTML in r_html_hit .headers ["Content-Type" ]
112+
113+ r_json_hit = requests .get (url , headers = headers , params = {"format" : "json" })
114+ assert r_json_hit .status_code == 200
115+ assert r_json_hit .headers ["X-PULP-CACHE" ] == "HIT"
116+ assert PYPI_SIMPLE_V1_JSON in r_json_hit .headers ["Content-Type" ]
117+
118+
77119@pytest .mark .parallel
78120def test_simple_cache_etag_conditional_request (synced_distro ):
79121 """
0 commit comments