gabriel / musehub public
test_musehub_ui_similarity.py python
197 lines 6.8 KB
cd448303 Initial extraction of MuseHub from maestro monorepo. Gabriel Cardona <gabriel@tellurstori.com> 7d ago
1 """Tests for the Muse Hub musical similarity page.
2
3 Covers:
4 - test_similarity_page_renders — GET /{owner}/{repo}/similarity/{base}...{head} returns 200 HTML
5 - test_similarity_page_no_auth_required — accessible without JWT
6 - test_similarity_page_invalid_ref_404 — refs without '...' separator return 404
7 - test_similarity_page_unknown_owner_404 — unknown owner/slug returns 404
8 - test_similarity_page_includes_radar — page contains server-rendered SVG radar
9 - test_similarity_page_includes_dimensions — page contains 10-dimension breakdown table (SSR)
10 - test_similarity_page_includes_overall_badge — page contains overall % badge (SSR)
11 - test_similarity_page_includes_diff_button — page contains "Open Full Diff" link
12 - test_similarity_page_includes_create_pr — page contains "Create Pull Request" CTA
13 - test_similarity_json_response — ?format=json returns RefSimilarityResponse shape
14 """
15 from __future__ import annotations
16
17 import pytest
18 from httpx import AsyncClient
19 from sqlalchemy.ext.asyncio import AsyncSession
20
21 from musehub.db.musehub_models import MusehubRepo
22
23
24 # ---------------------------------------------------------------------------
25 # Helpers
26 # ---------------------------------------------------------------------------
27
28
29 async def _make_repo(db_session: AsyncSession) -> str:
30 """Seed a minimal repo and return its repo_id."""
31 repo = MusehubRepo(
32 name="test-beats",
33 owner="testuser",
34 slug="test-beats",
35 visibility="private",
36 owner_user_id="test-owner",
37 )
38 db_session.add(repo)
39 await db_session.commit()
40 await db_session.refresh(repo)
41 return str(repo.repo_id)
42
43
44 # ---------------------------------------------------------------------------
45 # Issue #427 — musical similarity page
46 # ---------------------------------------------------------------------------
47
48
49 @pytest.mark.anyio
50 async def test_similarity_page_renders(
51 client: AsyncClient,
52 db_session: AsyncSession,
53 ) -> None:
54 """GET /musehub/ui/{owner}/{slug}/similarity/{base}...{head} returns 200 HTML."""
55 await _make_repo(db_session)
56 response = await client.get("/musehub/ui/testuser/test-beats/similarity/main...feature")
57 assert response.status_code == 200
58 assert "text/html" in response.headers["content-type"]
59 body = response.text
60 assert "Muse Hub" in body
61 assert "main" in body
62 assert "feature" in body
63
64
65 @pytest.mark.anyio
66 async def test_similarity_page_no_auth_required(
67 client: AsyncClient,
68 db_session: AsyncSession,
69 ) -> None:
70 """Similarity page is accessible without a JWT token."""
71 await _make_repo(db_session)
72 response = await client.get("/musehub/ui/testuser/test-beats/similarity/main...feature")
73 assert response.status_code == 200
74
75
76 @pytest.mark.anyio
77 async def test_similarity_page_invalid_ref_404(
78 client: AsyncClient,
79 db_session: AsyncSession,
80 ) -> None:
81 """Similarity path without '...' separator returns 404."""
82 await _make_repo(db_session)
83 response = await client.get("/musehub/ui/testuser/test-beats/similarity/mainfeature")
84 assert response.status_code == 404
85
86
87 @pytest.mark.anyio
88 async def test_similarity_page_unknown_owner_404(
89 client: AsyncClient,
90 ) -> None:
91 """Unknown owner/slug combination returns 404 on similarity page."""
92 response = await client.get("/musehub/ui/nobody/norepo/similarity/main...feature")
93 assert response.status_code == 404
94
95
96 @pytest.mark.anyio
97 async def test_similarity_page_includes_radar(
98 client: AsyncClient,
99 db_session: AsyncSession,
100 ) -> None:
101 """Similarity page HTML contains a server-rendered SVG radar chart."""
102 await _make_repo(db_session)
103 response = await client.get("/musehub/ui/testuser/test-beats/similarity/main...feature")
104 assert response.status_code == 200
105 body = response.text
106 assert "<svg" in body
107 assert "viewBox" in body
108
109
110 @pytest.mark.anyio
111 async def test_similarity_page_includes_dimensions(
112 client: AsyncClient,
113 db_session: AsyncSession,
114 ) -> None:
115 """Similarity page HTML contains the 10-dimension breakdown table (SSR)."""
116 await _make_repo(db_session)
117 response = await client.get("/musehub/ui/testuser/test-beats/similarity/main...feature")
118 assert response.status_code == 200
119 body = response.text
120 assert "Dimension Breakdown" in body
121 assert "Pitch" in body
122
123
124 @pytest.mark.anyio
125 async def test_similarity_page_includes_overall_badge(
126 client: AsyncClient,
127 db_session: AsyncSession,
128 ) -> None:
129 """Similarity page HTML contains overall similarity badge rendered server-side."""
130 await _make_repo(db_session)
131 response = await client.get("/musehub/ui/testuser/test-beats/similarity/main...feature")
132 assert response.status_code == 200
133 body = response.text
134 assert "overall musical similarity" in body
135 assert "%" in body
136
137
138 @pytest.mark.anyio
139 async def test_similarity_page_includes_diff_button(
140 client: AsyncClient,
141 db_session: AsyncSession,
142 ) -> None:
143 """Similarity page HTML contains an 'Open Full Diff' button linking to the compare page."""
144 await _make_repo(db_session)
145 response = await client.get("/musehub/ui/testuser/test-beats/similarity/main...feature")
146 assert response.status_code == 200
147 body = response.text
148 assert "Open Full Diff" in body
149 assert "compare" in body
150
151
152 @pytest.mark.anyio
153 async def test_similarity_page_includes_create_pr(
154 client: AsyncClient,
155 db_session: AsyncSession,
156 ) -> None:
157 """Similarity page HTML contains a 'Create Pull Request' call-to-action."""
158 await _make_repo(db_session)
159 response = await client.get("/musehub/ui/testuser/test-beats/similarity/main...feature")
160 assert response.status_code == 200
161 body = response.text
162 assert "Create Pull Request" in body
163
164
165 @pytest.mark.anyio
166 async def test_similarity_json_response(
167 client: AsyncClient,
168 db_session: AsyncSession,
169 ) -> None:
170 """GET /musehub/ui/{owner}/{slug}/similarity/{refs}?format=json returns RefSimilarityResponse."""
171 await _make_repo(db_session)
172 response = await client.get(
173 "/musehub/ui/testuser/test-beats/similarity/main...feature?format=json"
174 )
175 assert response.status_code == 200
176 assert "application/json" in response.headers["content-type"]
177 body = response.json()
178 # RefSimilarityResponse camelCase fields
179 assert "overallSimilarity" in body
180 assert "dimensions" in body
181 assert "interpretation" in body
182 assert "baseRef" in body
183 assert "compareRef" in body
184 # Dimensions sub-object should have all 10 axes
185 dims = body["dimensions"]
186 assert "pitchDistribution" in dims
187 assert "rhythmPattern" in dims
188 assert "tempo" in dims
189 assert "dynamics" in dims
190 assert "harmonicContent" in dims
191 assert "form" in dims
192 assert "instrumentBlend" in dims
193 assert "groove" in dims
194 assert "contour" in dims
195 assert "emotion" in dims
196 # Scores are in [0, 1]
197 assert 0.0 <= body["overallSimilarity"] <= 1.0