test_clones_e2e.py
python
sha256:ef10830ce231e0a20efcb0e2586cb879471247e916616e6fdd0d51df459e2595
fix: typing audit — 0 violations, 0 untyped defs across all…
Sonnet 4.6
minor
⚠ breaking
21 days ago
| 1 | """Tier 3 — End-to-end navigation tests for the clone browser (issue #17). |
| 2 | |
| 3 | Full navigation flows over the ASGI stack with seeded DB state. |
| 4 | No mocking — all requests go through the real route → DB → template pipeline. |
| 5 | |
| 6 | Cases: |
| 7 | E01 Intel Hub → Clones list (dashboard link renders) |
| 8 | E02 Clones list → Cluster detail (cl-row link resolves) |
| 9 | E03 Detail → back to Clones (breadcrumb link works) |
| 10 | E04 Tier filter round-trip: exact → near → all |
| 11 | E05 Top filter round-trip: 50 pill → back to 20 |
| 12 | E06 Empty repo path — no 500 at /intel or /intel/clones |
| 13 | """ |
| 14 | from __future__ import annotations |
| 15 | |
| 16 | import json |
| 17 | |
| 18 | import pytest |
| 19 | import pytest_asyncio |
| 20 | from httpx import AsyncClient |
| 21 | from sqlalchemy.dialects.postgresql import insert as pg_insert |
| 22 | from sqlalchemy.ext.asyncio import AsyncSession |
| 23 | |
| 24 | from muse.core.types import long_id |
| 25 | from musehub.db.musehub_intel_models import MusehubIntelClones |
| 26 | from tests.factories import create_repo |
| 27 | |
| 28 | _REF = long_id("a" * 64) |
| 29 | _HASH_A = long_id("1" * 64) |
| 30 | _HASH_B = long_id("2" * 64) |
| 31 | |
| 32 | |
| 33 | async def _seed(session: AsyncSession, repo_id: str) -> None: |
| 34 | """Seed one exact and one near cluster for navigation tests.""" |
| 35 | for h, tier, mc in [(_HASH_A, "exact", 6), (_HASH_B, "near", 3)]: |
| 36 | members = json.dumps([ |
| 37 | {"address": f"src/a.py::fn{i}", "kind": "function", |
| 38 | "language": "Python", "body_hash": long_id("a" * 64), |
| 39 | "signature_id": long_id("b" * 64), "content_id": long_id("a" * 64)} |
| 40 | for i in range(mc) |
| 41 | ]) |
| 42 | await session.execute( |
| 43 | pg_insert(MusehubIntelClones) |
| 44 | .values(repo_id=repo_id, cluster_hash=h, tier=tier, |
| 45 | member_count=mc, members_json=members, ref=_REF) |
| 46 | .on_conflict_do_update( |
| 47 | index_elements=["repo_id", "cluster_hash"], |
| 48 | set_={"tier": tier, "member_count": mc, "members_json": members}, |
| 49 | ) |
| 50 | ) |
| 51 | await session.commit() |
| 52 | |
| 53 | |
| 54 | @pytest_asyncio.fixture |
| 55 | async def repo(db_session: AsyncSession) -> MusehubRepo: |
| 56 | r = await create_repo(db_session, owner="e2euser", slug="clone-e2e") |
| 57 | await _seed(db_session, str(r.repo_id)) |
| 58 | return r |
| 59 | |
| 60 | |
| 61 | @pytest_asyncio.fixture |
| 62 | async def empty_repo(db_session: AsyncSession) -> MusehubRepo: |
| 63 | return await create_repo(db_session, owner="e2euser", slug="empty-e2e") |
| 64 | |
| 65 | |
| 66 | class TestClonesE2E: |
| 67 | """Full navigation flows — no mocking.""" |
| 68 | |
| 69 | @pytest.mark.asyncio |
| 70 | async def test_E01_dashboard_links_to_clones( |
| 71 | self, client: AsyncClient, repo: MusehubRepo |
| 72 | ) -> None: |
| 73 | """Intel Hub page contains a link to /intel/clones.""" |
| 74 | r = await client.get("/e2euser/clone-e2e/intel") |
| 75 | assert r.status_code == 200 |
| 76 | assert b"/intel/clones" in r.content |
| 77 | |
| 78 | @pytest.mark.asyncio |
| 79 | async def test_E02_list_row_links_to_detail( |
| 80 | self, client: AsyncClient, repo: MusehubRepo |
| 81 | ) -> None: |
| 82 | """A cluster row on the list page carries a link to its detail page.""" |
| 83 | r = await client.get("/e2euser/clone-e2e/intel/clones") |
| 84 | assert r.status_code == 200 |
| 85 | assert b"intel/clones/detail?cluster=" in r.content |
| 86 | |
| 87 | @pytest.mark.asyncio |
| 88 | async def test_E03_detail_back_link_to_clones( |
| 89 | self, client: AsyncClient, repo: MusehubRepo |
| 90 | ) -> None: |
| 91 | """Detail page breadcrumb links back to the clones list.""" |
| 92 | r = await client.get( |
| 93 | f"/e2euser/clone-e2e/intel/clones/detail?cluster={_HASH_A}" |
| 94 | ) |
| 95 | assert r.status_code == 200 |
| 96 | assert "← Clones" in r.text |
| 97 | assert b"/intel/clones" in r.content |
| 98 | |
| 99 | @pytest.mark.asyncio |
| 100 | async def test_E04_tier_filter_round_trip( |
| 101 | self, client: AsyncClient, repo: MusehubRepo |
| 102 | ) -> None: |
| 103 | """Tier filter correctly switches between exact, near, and all.""" |
| 104 | r_exact = await client.get("/e2euser/clone-e2e/intel/clones?tier=exact") |
| 105 | assert r_exact.status_code == 200 |
| 106 | assert b"cl-badge--exact" in r_exact.content |
| 107 | assert b"cl-badge--near" not in r_exact.content |
| 108 | |
| 109 | r_near = await client.get("/e2euser/clone-e2e/intel/clones?tier=near") |
| 110 | assert r_near.status_code == 200 |
| 111 | assert b"cl-badge--near" in r_near.content |
| 112 | assert b"cl-badge--exact" not in r_near.content |
| 113 | |
| 114 | r_all = await client.get("/e2euser/clone-e2e/intel/clones") |
| 115 | assert r_all.status_code == 200 |
| 116 | assert b"cl-badge--exact" in r_all.content |
| 117 | assert b"cl-badge--near" in r_all.content |
| 118 | |
| 119 | @pytest.mark.asyncio |
| 120 | async def test_E05_top_filter_round_trip( |
| 121 | self, client: AsyncClient, repo: MusehubRepo |
| 122 | ) -> None: |
| 123 | """Top filter renders the active pill correctly.""" |
| 124 | r50 = await client.get("/e2euser/clone-e2e/intel/clones?top=50") |
| 125 | assert r50.status_code == 200 |
| 126 | assert b"top=50" in r50.content |
| 127 | |
| 128 | r20 = await client.get("/e2euser/clone-e2e/intel/clones?top=20") |
| 129 | assert r20.status_code == 200 |
| 130 | assert b"top=20" in r20.content |
| 131 | |
| 132 | @pytest.mark.asyncio |
| 133 | async def test_E06_empty_repo_no_500( |
| 134 | self, client: AsyncClient, empty_repo: MusehubRepo |
| 135 | ) -> None: |
| 136 | """Empty repo returns 200 at both /intel and /intel/clones — no 500.""" |
| 137 | r_dash = await client.get("/e2euser/empty-e2e/intel") |
| 138 | assert r_dash.status_code == 200 |
| 139 | |
| 140 | r_list = await client.get("/e2euser/empty-e2e/intel/clones") |
| 141 | assert r_list.status_code == 200 |
| 142 | assert b"intel.code.clones" in r_list.content |
File History
1 commit
sha256:ef10830ce231e0a20efcb0e2586cb879471247e916616e6fdd0d51df459e2595
fix: typing audit — 0 violations, 0 untyped defs across all…
Sonnet 4.6
minor
⚠
21 days ago