"""Integration tests for PyPI transparent proxy.""" import os import pytest import httpx def get_base_url(): """Get the base URL for the Orchard server from environment.""" return os.environ.get("ORCHARD_TEST_URL", "http://localhost:8080") class TestPyPIProxyEndpoints: """Tests for PyPI proxy endpoints. These endpoints are public (no auth required) since pip needs to use them. """ @pytest.mark.integration def test_pypi_simple_index(self): """Test that /pypi/simple/ returns HTML response.""" with httpx.Client(base_url=get_base_url(), timeout=30.0) as client: response = client.get("/pypi/simple/") # Returns 200 if sources configured, 503 if not assert response.status_code in (200, 503) if response.status_code == 200: assert "text/html" in response.headers.get("content-type", "") else: assert "No PyPI upstream sources configured" in response.json()["detail"] @pytest.mark.integration def test_pypi_package_endpoint(self): """Test that /pypi/simple/{package}/ returns appropriate response.""" with httpx.Client(base_url=get_base_url(), timeout=30.0) as client: response = client.get("/pypi/simple/requests/") # Returns 200 if sources configured and package found, # 404 if package not found, 503 if no sources assert response.status_code in (200, 404, 503) if response.status_code == 200: assert "text/html" in response.headers.get("content-type", "") @pytest.mark.integration def test_pypi_download_missing_upstream_param(self): """Test that /pypi/simple/{package}/{filename} requires upstream param.""" with httpx.Client(base_url=get_base_url(), timeout=30.0) as client: response = client.get("/pypi/simple/requests/requests-2.31.0.tar.gz") assert response.status_code == 400 assert "upstream" in response.json()["detail"].lower() class TestPyPILinkRewriting: """Tests for URL rewriting in PyPI proxy responses.""" def test_rewrite_package_links(self): """Test that download links are rewritten to go through proxy.""" from app.pypi_proxy import _rewrite_package_links html = '''
requests-2.31.0.tar.gz requests-2.31.0-py3-none-any.whl ''' # upstream_base_url is used to resolve relative URLs (not needed here since URLs are absolute) result = _rewrite_package_links( html, "http://localhost:8080", "requests", "https://pypi.org/simple/requests/" ) # Links should be rewritten to go through our proxy assert "/pypi/simple/requests/requests-2.31.0.tar.gz?upstream=" in result assert "/pypi/simple/requests/requests-2.31.0-py3-none-any.whl?upstream=" in result # Original URLs should be encoded in upstream param assert "files.pythonhosted.org" in result # Hash fragments should be preserved assert "#sha256=abc123" in result assert "#sha256=def456" in result def test_rewrite_relative_links(self): """Test that relative URLs are resolved to absolute URLs.""" from app.pypi_proxy import _rewrite_package_links # Artifactory-style relative URLs html = ''' requests-2.31.0.tar.gz ''' result = _rewrite_package_links( html, "https://orchard.example.com", "requests", "https://artifactory.example.com/api/pypi/pypi-remote/simple/requests/" ) # The relative URL should be resolved to absolute assert "upstream=https%3A%2F%2Fartifactory.example.com%2Fapi%2Fpypi%2Fpackages" in result # Hash fragment should be preserved assert "#sha256=abc123" in result class TestPyPIPackageNormalization: """Tests for PyPI package name normalization.""" @pytest.mark.integration def test_package_name_normalized(self): """Test that package names are normalized per PEP 503.""" # These should all be treated the same: # requests, Requests, requests_, requests- # The endpoint normalizes to lowercase with hyphens with httpx.Client(base_url=get_base_url(), timeout=30.0) as client: # Different capitalizations/separators should all be valid paths # Returns 200/404/503 depending on sources and package availability response = client.get("/pypi/simple/Requests/") assert response.status_code in (200, 404, 503) response = client.get("/pypi/simple/some_package/") assert response.status_code in (200, 404, 503) response = client.get("/pypi/simple/some-package/") assert response.status_code in (200, 404, 503)