Add transparent PyPI proxy and improve upstream sources UI

- Implement PEP 503 Simple API endpoints for pip compatibility:
  - GET /pypi/simple/ - package index
  - GET /pypi/simple/{package}/ - version list with rewritten links
  - GET /pypi/simple/{package}/{filename} - download with auto-caching

- Improve upstream sources table UI:
  - Center text under column headers
  - Remove separate Source column, show ENV badge inline with name
  - Make Test/Edit buttons more prominent with secondary button style
This commit is contained in:
Mondo Diaz
2026-01-29 15:30:57 -06:00
parent e8cf2462b7
commit a9de32d922
6 changed files with 673 additions and 16 deletions

View File

@@ -0,0 +1,88 @@
"""Integration tests for PyPI transparent proxy."""
import pytest
import httpx
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):
"""Test that /pypi/simple/ returns 503 when no sources configured."""
# Use unauthenticated client since PyPI proxy is public
with httpx.Client(base_url="http://localhost:8080") as client:
response = 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):
"""Test that /pypi/simple/{package}/ returns 503 when no sources configured."""
with httpx.Client(base_url="http://localhost:8080") as client:
response = 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):
"""Test that /pypi/simple/{package}/{filename} requires upstream param."""
with httpx.Client(base_url="http://localhost:8080") 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 = '''
<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):
"""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="http://localhost:8080") as client:
# Without upstream sources, we get 503, but the normalization
# happens before the source lookup
response = client.get("/pypi/simple/Requests/")
assert response.status_code == 503 # No sources, but path was valid
response = client.get("/pypi/simple/some_package/")
assert response.status_code == 503
response = client.get("/pypi/simple/some-package/")
assert response.status_code == 503