Skip to content

Commit 0d87c1c

Browse files
cosmicexplorerfridex
authored andcommitted
add mock server to test that each dist is downloaded exactly once
1 parent 4eb9afe commit 0d87c1c

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

tests/conftest.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,3 +943,51 @@ def html_index_for_packages(
943943
f.write(pkg_index_content)
944944

945945
return html_dir
946+
947+
948+
class OneTimeDownloadHandler(http.server.SimpleHTTPRequestHandler):
949+
"""Serve files from the current directory, but error if a file is downloaded more
950+
than once."""
951+
952+
_seen_paths: ClassVar[Set[str]] = set()
953+
954+
def do_GET(self) -> None:
955+
if self.path in self._seen_paths:
956+
self.send_error(
957+
http.HTTPStatus.NOT_FOUND,
958+
f"File {self.path} not available more than once!",
959+
)
960+
return
961+
super().do_GET()
962+
if not (self.path.endswith("/") or self.path.endswith(".metadata")):
963+
self._seen_paths.add(self.path)
964+
965+
966+
@pytest.fixture(scope="function")
967+
def html_index_with_onetime_server(
968+
html_index_for_packages: Path,
969+
) -> Iterator[http.server.ThreadingHTTPServer]:
970+
"""Serve files from a generated pypi index, erroring if a file is downloaded more
971+
than once.
972+
973+
Provide `-i http://localhost:8000` to pip invocations to point them at this server.
974+
"""
975+
976+
class InDirectoryServer(http.server.ThreadingHTTPServer):
977+
def finish_request(self, request: Any, client_address: Any) -> None:
978+
self.RequestHandlerClass(
979+
request, client_address, self, directory=str(html_index_for_packages) # type: ignore[call-arg] # noqa: E501
980+
)
981+
982+
class Handler(OneTimeDownloadHandler):
983+
_seen_paths: ClassVar[Set[str]] = set()
984+
985+
with InDirectoryServer(("", 8000), Handler) as httpd:
986+
server_thread = threading.Thread(target=httpd.serve_forever)
987+
server_thread.start()
988+
989+
try:
990+
yield httpd
991+
finally:
992+
httpd.shutdown()
993+
server_thread.join()

tests/functional/test_download.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,57 @@ def test_download_metadata(
13241324
assert sorted(os.listdir(download_dir)) == expected_outputs
13251325

13261326

1327+
@pytest.mark.parametrize(
1328+
"requirement_to_download, expected_outputs, doubled_path",
1329+
[
1330+
(
1331+
"simple2==1.0",
1332+
["simple-1.0.tar.gz", "simple2-1.0.tar.gz"],
1333+
"/simple2/simple2-1.0.tar.gz",
1334+
),
1335+
("simple==2.0", ["simple-2.0.tar.gz"], "/simple/simple-2.0.tar.gz"),
1336+
(
1337+
"colander",
1338+
["colander-0.9.9-py2.py3-none-any.whl", "translationstring-1.1.tar.gz"],
1339+
"/colander/colander-0.9.9-py2.py3-none-any.whl",
1340+
),
1341+
(
1342+
"compilewheel",
1343+
[
1344+
"compilewheel-1.0-py2.py3-none-any.whl",
1345+
"simple-1.0.tar.gz",
1346+
],
1347+
"/compilewheel/compilewheel-1.0-py2.py3-none-any.whl",
1348+
),
1349+
],
1350+
)
1351+
def test_download_metadata_server(
1352+
download_server_html_index: Callable[..., Tuple[TestPipResult, Path]],
1353+
requirement_to_download: str,
1354+
expected_outputs: List[str],
1355+
doubled_path: str,
1356+
) -> None:
1357+
"""Verify that if a data-dist-info-metadata attribute is present, then it is used
1358+
instead of the actual dist's METADATA.
1359+
1360+
Additionally, verify that each dist is downloaded exactly once using a mock server.
1361+
1362+
This is a regression test for issue https://github.com/pypa/pip/issues/11847.
1363+
"""
1364+
_, download_dir = download_server_html_index(
1365+
[requirement_to_download, "--no-cache-dir"],
1366+
)
1367+
assert sorted(os.listdir(download_dir)) == expected_outputs
1368+
shutil.rmtree(download_dir)
1369+
result, _ = download_server_html_index(
1370+
[requirement_to_download, "--no-cache-dir"],
1371+
allow_error=True,
1372+
)
1373+
assert result.returncode != 0
1374+
expected_msg = f"File {doubled_path} not available more than once!"
1375+
assert expected_msg in result.stderr
1376+
1377+
13271378
@pytest.mark.parametrize(
13281379
"requirement_to_download, real_hash",
13291380
[

0 commit comments

Comments
 (0)