gabriel / musehub public
test_clones_e2e.py python
142 lines 5.3 KB
Raw
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