83 lines
3.5 KiB
Python
83 lines
3.5 KiB
Python
"""Integration tests for PyPI transparent proxy."""
|
|
|
|
import pytest
|
|
|
|
|
|
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_no_sources(self, unauthenticated_client):
|
|
"""Test that /pypi/simple/ returns 503 when no sources configured."""
|
|
response = unauthenticated_client.get("/pypi/simple/")
|
|
# Should return 503 when no PyPI upstream sources are configured
|
|
assert response.status_code == 503
|
|
assert "No PyPI upstream sources configured" in response.json()["detail"]
|
|
|
|
@pytest.mark.integration
|
|
def test_pypi_package_no_sources(self, unauthenticated_client):
|
|
"""Test that /pypi/simple/{package}/ returns 503 when no sources configured."""
|
|
response = unauthenticated_client.get("/pypi/simple/requests/")
|
|
assert response.status_code == 503
|
|
assert "No PyPI upstream sources configured" in response.json()["detail"]
|
|
|
|
@pytest.mark.integration
|
|
def test_pypi_download_missing_upstream_param(self, unauthenticated_client):
|
|
"""Test that /pypi/simple/{package}/{filename} requires upstream param."""
|
|
response = unauthenticated_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 = '''
|
|
<html>
|
|
<body>
|
|
<a href="https://files.pythonhosted.org/packages/ab/cd/requests-2.31.0.tar.gz#sha256=abc123">requests-2.31.0.tar.gz</a>
|
|
<a href="https://files.pythonhosted.org/packages/ef/gh/requests-2.31.0-py3-none-any.whl#sha256=def456">requests-2.31.0-py3-none-any.whl</a>
|
|
</body>
|
|
</html>
|
|
'''
|
|
|
|
result = _rewrite_package_links(html, "http://localhost:8080", "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
|
|
|
|
|
|
class TestPyPIPackageNormalization:
|
|
"""Tests for PyPI package name normalization."""
|
|
|
|
@pytest.mark.integration
|
|
def test_package_name_normalized(self, unauthenticated_client):
|
|
"""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
|
|
|
|
# Without upstream sources, we get 503, but the normalization
|
|
# happens before the source lookup
|
|
response = unauthenticated_client.get("/pypi/simple/Requests/")
|
|
assert response.status_code == 503 # No sources, but path was valid
|
|
|
|
response = unauthenticated_client.get("/pypi/simple/some_package/")
|
|
assert response.status_code == 503
|
|
|
|
response = unauthenticated_client.get("/pypi/simple/some-package/")
|
|
assert response.status_code == 503
|