fix: correct test imports and health endpoint assertions

- Fix import in test_db_utils.py: use app.models instead of backend.app.models
- Update health endpoint test to expect 'ok' status and infrastructure keys
- Add CHANGELOG entries for PyPI proxy performance improvements
This commit is contained in:
Mondo Diaz
2026-02-04 10:37:12 -06:00
parent 9a1d578525
commit b27eb0a928
3 changed files with 38 additions and 14 deletions

View File

@@ -10,6 +10,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added S3 bucket provisioning terraform configuration (#59)
- Creates an S3 bucket to be used for anything Orchard
- Creates a log bucket for any logs tracking the S3 bucket
- Added HTTP connection pooling infrastructure for improved PyPI proxy performance
- `HttpClientManager` with configurable pool size, timeouts, and thread pool executor
- Eliminates per-request connection overhead (~100-500ms → ~5ms)
- Added Redis caching layer with category-aware TTL for hermetic builds
- `CacheService` with graceful fallback when Redis unavailable
- Immutable data (artifact metadata, dependencies) cached forever
- Mutable data (package index, versions) uses configurable TTL
- Added `ArtifactRepository` for batch database operations
- `batch_upsert_dependencies()` reduces N+1 queries to single INSERT
- `get_or_create_artifact()` uses atomic ON CONFLICT upsert
- Added infrastructure status to health endpoint (`/health`)
- Reports HTTP pool size and worker threads
- Reports Redis cache connection status
- Added new configuration settings for HTTP client, Redis, and cache TTL
- `ORCHARD_HTTP_MAX_CONNECTIONS`, `ORCHARD_HTTP_CONNECT_TIMEOUT`, etc.
- `ORCHARD_REDIS_HOST`, `ORCHARD_REDIS_PORT`, `ORCHARD_REDIS_ENABLED`
- `ORCHARD_CACHE_TTL_INDEX`, `ORCHARD_CACHE_TTL_VERSIONS`, etc.
- Added transparent PyPI proxy implementing PEP 503 Simple API (#108)
- `GET /pypi/simple/` - package index (proxied from upstream)
- `GET /pypi/simple/{package}/` - version list with rewritten download links
@@ -19,6 +36,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `POST /api/v1/cache/resolve` endpoint to cache packages by coordinates instead of URL (#108)
### Changed
- PyPI proxy now uses shared HTTP connection pool instead of per-request clients
- PyPI proxy now caches upstream source configuration in Redis
- Dependency storage now uses batch INSERT instead of individual queries
- Increased default database pool size from 5 to 20 connections
- Increased default database max overflow from 10 to 30 connections
- Enabled Redis in Helm chart values for dev, stage, and prod environments
- Upstream sources table text is now centered under column headers (#108)
- ENV badge now appears inline with source name instead of separate column (#108)
- Test and Edit buttons now have more prominent button styling (#108)

View File

@@ -147,6 +147,7 @@ class TestPyPIProxyInfrastructure:
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
# Infrastructure status may include these if implemented
# assert "infrastructure" in data
assert data["status"] == "ok"
# Infrastructure status should be present
assert "http_pool" in data
assert "cache" in data

View File

@@ -8,7 +8,7 @@ class TestArtifactRepository:
def test_batch_dependency_values_formatting(self):
"""batch_upsert_dependencies should format values correctly."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
deps = [
("_pypi", "numpy", ">=1.21.0"),
@@ -29,7 +29,7 @@ class TestArtifactRepository:
def test_empty_dependencies_returns_empty_list(self):
"""Empty dependency list should return empty values."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
values = ArtifactRepository._format_dependency_values("abc123", [])
@@ -37,7 +37,7 @@ class TestArtifactRepository:
def test_format_dependency_values_preserves_special_characters(self):
"""Version constraints with special characters should be preserved."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
deps = [
("_pypi", "package-name", ">=1.0.0,<2.0.0"),
@@ -51,7 +51,7 @@ class TestArtifactRepository:
def test_batch_upsert_dependencies_returns_zero_for_empty(self):
"""batch_upsert_dependencies should return 0 for empty list without DB call."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
mock_db = MagicMock()
repo = ArtifactRepository(mock_db)
@@ -64,8 +64,8 @@ class TestArtifactRepository:
def test_get_or_create_artifact_builds_correct_statement(self):
"""get_or_create_artifact should use ON CONFLICT DO UPDATE."""
from backend.app.db_utils import ArtifactRepository
from backend.app.models import Artifact
from app.db_utils import ArtifactRepository
from app.models import Artifact
mock_db = MagicMock()
mock_result = MagicMock()
@@ -88,7 +88,7 @@ class TestArtifactRepository:
def test_get_or_create_artifact_existing_not_created(self):
"""get_or_create_artifact should return created=False for existing artifact."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
mock_db = MagicMock()
mock_result = MagicMock()
@@ -108,7 +108,7 @@ class TestArtifactRepository:
def test_get_cached_url_with_artifact_returns_tuple(self):
"""get_cached_url_with_artifact should return (CachedUrl, Artifact) tuple."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
mock_db = MagicMock()
mock_cached_url = MagicMock()
@@ -125,7 +125,7 @@ class TestArtifactRepository:
def test_get_cached_url_with_artifact_returns_none_when_not_found(self):
"""get_cached_url_with_artifact should return None when URL not cached."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
mock_db = MagicMock()
mock_db.query.return_value.join.return_value.filter.return_value.first.return_value = None
@@ -137,7 +137,7 @@ class TestArtifactRepository:
def test_get_artifact_dependencies_returns_list(self):
"""get_artifact_dependencies should return list of dependencies."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
mock_db = MagicMock()
mock_dep1 = MagicMock()
@@ -156,7 +156,7 @@ class TestArtifactRepository:
def test_get_artifact_dependencies_returns_empty_list(self):
"""get_artifact_dependencies should return empty list when no dependencies."""
from backend.app.db_utils import ArtifactRepository
from app.db_utils import ArtifactRepository
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.all.return_value = []