from __future__ import annotations import time import uuid import pytest from sqlalchemy import delete, event, update from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from api.models.managed_repo import ManagedRepo from api.models.repository_rename import RepositorySlug from tests.conftest import create_test_domain, create_test_repo @pytest.mark.asyncio @pytest.mark.parametrize("repository_count", [4, 131, 501]) @pytest.mark.parametrize("filter_domain", [False, True]) async def test_repository_collection_projects_identities_with_one_registry_query( client, test_engine, monkeypatch, repository_count, filter_domain ): domain = await create_test_domain(client) repositories = [ ManagedRepo( id=uuid.uuid4(), domain_id=uuid.UUID(domain["id"]), slug=f"bounded-repo-{index}", name=f"Bounded repo {index:04d}", ) for index in range(repository_count) ] factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False) async with factory() as session: session.add_all(repositories) await session.flush() session.add_all([ RepositorySlug(repo_id=repo.id, slug=repo.slug, kind="canonical") for repo in repositories[:-1] ]) session.add( RepositorySlug( repo_id=repositories[0].id, slug="bounded-repo-old-name", kind="alias", protected=True, ) ) await session.commit() async def reject_per_repository_resolution(*_args, **_kwargs): raise AssertionError("collection must not resolve repository identities one at a time") monkeypatch.setattr( "api.routers.repos.repository_resolution_for_id", reject_per_repository_resolution, ) statements: list[str] = [] def capture_statement(_conn, _cursor, statement, _parameters, _context, _executemany): statements.append(statement) event.listen(test_engine.sync_engine, "before_cursor_execute", capture_statement) try: started = time.perf_counter() response = await client.get("/repos/", params={"domain": domain["slug"]} if filter_domain else {}) elapsed = time.perf_counter() - started finally: event.remove(test_engine.sync_engine, "before_cursor_execute", capture_statement) assert response.status_code == 200, response.text assert len(response.json()) == repository_count assert all(repo["domain_slug"] == domain["slug"] for repo in response.json()) by_slug = {repository["slug"]: repository for repository in response.json()} assert by_slug["bounded-repo-0"]["aliases"] == ["bounded-repo-old-name"] assert by_slug["bounded-repo-0"]["slug_status"] == "canonical" assert by_slug[repositories[-1].slug]["aliases"] == [] assert by_slug[repositories[-1].slug]["canonical_slug"] == repositories[-1].slug registry_selects = [ statement for statement in statements if statement.lstrip().upper().startswith("SELECT") and "repository_slugs" in statement.lower() ] assert len(registry_selects) == 1 selects = [statement for statement in statements if statement.lstrip().upper().startswith("SELECT")] assert len(selects) == (3 if filter_domain else 2), selects print(f"\ncollection: repositories={repository_count}, filtered={filter_domain}, selects={len(selects)}, elapsed_seconds={elapsed:.4f}") @pytest.mark.asyncio async def test_empty_repository_collection_skips_registry_query(client, test_engine): statements = [] def capture(_conn, _cursor, statement, *_args): statements.append(statement) event.listen(test_engine.sync_engine, "before_cursor_execute", capture) try: response = await client.get("/repos/") finally: event.remove(test_engine.sync_engine, "before_cursor_execute", capture) assert response.status_code == 200 assert response.json() == [] assert not any("repository_slugs" in statement for statement in statements) @pytest.mark.asyncio @pytest.mark.parametrize("registry_state", ["missing", "alias_only", "canonical_mismatch"]) async def test_collection_matches_detail_identity_compatibility( client, test_engine, registry_state ): domain = await create_test_domain(client) repo = await create_test_repo(client, domain_slug=domain["slug"]) factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False) async with factory() as session: if registry_state == "missing": await session.execute(delete(RepositorySlug).where(RepositorySlug.repo_id == repo["id"])) else: await session.execute( update(RepositorySlug).where(RepositorySlug.repo_id == repo["id"]).values(kind="alias") ) if registry_state == "canonical_mismatch": session.add(RepositorySlug(repo_id=repo["id"], slug="different-canonical", kind="canonical")) await session.commit() detail = await client.get(f"/repos/{repo['slug']}") collection = await client.get("/repos/") if registry_state == "missing": assert detail.status_code == collection.status_code == 200 for field in ("canonical_slug", "requested_slug", "aliases", "slug_status"): assert collection.json()[0][field] == detail.json()[field] else: assert detail.status_code == collection.status_code == 409 assert collection.json() == detail.json() @pytest.mark.asyncio async def test_collection_filters_secondary_domain_and_classification(client, test_engine): primary = await create_test_domain(client) secondary = await create_test_domain(client, slug="secondary", name="Secondary") factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False) async with factory() as session: session.add_all([ ManagedRepo( domain_id=uuid.UUID(primary["id"]), slug="included", name="Included", secondary_domains=[secondary["slug"]], category="service", capability_tags=["projection"], business_stake=["internal"], ), ManagedRepo( domain_id=uuid.UUID(primary["id"]), slug="excluded", name="Excluded", category="service", capability_tags=["projection"], business_stake=["internal"], ), ]) await session.commit() params = {"domain": secondary["slug"], "category": "service", "capability_tag": "projection", "business_stake": "internal"} response = await client.get("/repos/", params=params) assert response.status_code == 200, response.text assert [repo["slug"] for repo in response.json()] == ["included"] assert response.json()[0]["domain_slug"] == primary["slug"] for key in ("category", "capability_tag", "business_stake"): response = await client.get("/repos/", params={**params, key: "absent"}) assert response.status_code == 200 assert response.json() == [] response = await client.get("/repos/", params={"domain": "missing"}) assert response.status_code == 404