gabriel / musehub public
test_musehub_ui_commits_enhanced.py python
377 lines 14.0 KB
Raw
sha256:9590cee1e0ccd6c76528f005b95d634d80f5019f0dcb7c371e149adc31d1fb65 refactor: enforce gRPC framing on all MWP wire traffic Sonnet 4.6 minor ⚠ breaking 156 days ago
1 """Regression tests for the enhanced commits list page.
2
3 Covers the four feature areas added to commits_list_page():
4
5 Filter bar
6 - test_commits_enhanced_filter_bar_present — filter-bar HTML element present
7 - test_commits_enhanced_author_dropdown_present — author <select> with 'All authors' default
8 - test_commits_enhanced_date_picker_inputs_present — dateFrom / dateTo date inputs present
9 - test_commits_enhanced_search_input_present — message search <input> present
10 - test_commits_enhanced_tag_filter_input_present — tag filter <input> present
11
12 Server-side filtering
13 - test_commits_enhanced_author_filter_narrows_results — ?author= returns only that author's commits
14 - test_commits_enhanced_author_filter_excludes_others — commits by other authors absent
15 - test_commits_enhanced_search_filter_matches_message — ?q= matches substring in commit message
16 - test_commits_enhanced_search_filter_excludes_others — non-matching commits absent
17 - test_commits_enhanced_date_from_filter — ?dateFrom= excludes older commits
18 - test_commits_enhanced_tag_filter_matches_tag — ?tag=emotion:funky matches message substring
19
20 Compare mode
21 - test_commits_enhanced_compare_toggle_btn_present — compare-toggle-btn button present
22 - test_commits_enhanced_compare_strip_present — compare-strip container present
23 - test_commits_enhanced_compare_check_inputs_present — compare-check checkboxes per row
24 - test_commits_enhanced_compare_js_function — toggleCompareMode() JS function present
25
26 Metadata badges (client-side JS)
27 - test_commits_enhanced_meta_badges_container_present — meta-badges span present per row
28 - test_commits_enhanced_badge_js_extract_function — extractBadges() JS function present
29 - test_commits_enhanced_chip_css_classes_present — chip-tempo / chip-key / chip-emotion CSS defined
30
31 Mini-lane
32 - test_commits_enhanced_dag_merge_arm_present — dag-merge-arm element on merge commits
33 - test_commits_enhanced_mini_lane_dag_col_present — dag-col column present
34
35 Pagination with active filters
36 - test_commits_enhanced_pagination_preserves_filters — page links carry active filter params
37 """
38 from __future__ import annotations
39
40 import uuid
41 from datetime import datetime, timezone
42
43 import pytest
44 from httpx import AsyncClient
45 from sqlalchemy.ext.asyncio import AsyncSession
46
47 from musehub.db.musehub_models import MusehubBranch, MusehubCommit, MusehubRepo
48
49 # ── Constants ─────────────────────────────────────────────────────────────────
50
51 _OWNER = "enhancedowner"
52 _SLUG = "enhanced-commits"
53
54 _SHA_ALICE_1 = "a1" + "0" * 38
55 _SHA_ALICE_2 = "a2" + "0" * 38
56 _SHA_BOB_1 = "b1" + "0" * 38
57 _SHA_MERGE = "cc" + "0" * 38
58
59 # ── Seed helpers ──────────────────────────────────────────────────────────────
60
61
62 async def _seed_repo(db: AsyncSession) -> str:
63 """Seed a public repo with 4 commits from 2 authors and return repo_id."""
64 repo = MusehubRepo(
65 repo_id=str(uuid.uuid4()),
66 name=_SLUG,
67 owner=_OWNER,
68 slug=_SLUG,
69 visibility="public",
70 owner_user_id=str(uuid.uuid4()),
71 )
72 db.add(repo)
73 await db.flush()
74 repo_id = str(repo.repo_id)
75
76 branch = MusehubBranch(repo_id=repo_id, name="main", head_commit_id=_SHA_MERGE)
77 db.add(branch)
78
79 # Alice: two commits with music metadata in messages
80 db.add(MusehubCommit(
81 commit_id=_SHA_ALICE_1,
82 repo_id=repo_id,
83 branch="main",
84 parent_ids=[],
85 message="Add walking bass line 120 BPM Cm emotion:funky",
86 author="alice",
87 timestamp=datetime(2026, 1, 10, tzinfo=timezone.utc),
88 ))
89 db.add(MusehubCommit(
90 commit_id=_SHA_ALICE_2,
91 repo_id=repo_id,
92 branch="main",
93 parent_ids=[_SHA_ALICE_1],
94 message="Refine rhodes chord voicings stage:chorus",
95 author="alice",
96 timestamp=datetime(2026, 2, 15, tzinfo=timezone.utc),
97 ))
98 # Bob: one commit
99 db.add(MusehubCommit(
100 commit_id=_SHA_BOB_1,
101 repo_id=repo_id,
102 branch="main",
103 parent_ids=[_SHA_ALICE_2],
104 message="Add jazz drums groove 90 BPM Gm",
105 author="bob",
106 timestamp=datetime(2026, 3, 1, tzinfo=timezone.utc),
107 ))
108 # Merge commit
109 db.add(MusehubCommit(
110 commit_id=_SHA_MERGE,
111 repo_id=repo_id,
112 branch="main",
113 parent_ids=[_SHA_ALICE_2, _SHA_BOB_1],
114 message="Merge feat/drums into main",
115 author="alice",
116 timestamp=datetime(2026, 3, 2, tzinfo=timezone.utc),
117 ))
118
119 await db.commit()
120 return repo_id
121
122
123 def _url(path: str = "") -> str:
124 return f"/{_OWNER}/{_SLUG}/commits{path}"
125
126
127 # ── Filter bar HTML ───────────────────────────────────────────────────────────
128
129
130 async def test_commits_enhanced_filter_bar_present(
131 client: AsyncClient, db_session: AsyncSession
132 ) -> None:
133 """filter-bar container is rendered on the commits list page."""
134 await _seed_repo(db_session)
135 resp = await client.get(_url())
136 assert resp.status_code == 200
137 assert "filter-bar" in resp.text
138
139
140 async def test_commits_enhanced_author_dropdown_present(
141 client: AsyncClient, db_session: AsyncSession
142 ) -> None:
143 """Author <select> dropdown is present and includes 'All authors' option."""
144 await _seed_repo(db_session)
145 resp = await client.get(_url())
146 assert resp.status_code == 200
147 assert "All authors" in resp.text
148 # Both authors appear as options
149 assert "alice" in resp.text
150 assert "bob" in resp.text
151
152
153 async def test_commits_enhanced_date_picker_inputs_present(
154 client: AsyncClient, db_session: AsyncSession
155 ) -> None:
156 """dateFrom and dateTo date inputs are present in the filter bar."""
157 await _seed_repo(db_session)
158 resp = await client.get(_url())
159 assert resp.status_code == 200
160 assert 'type="date"' in resp.text
161 assert "dateFrom" in resp.text
162 assert "dateTo" in resp.text
163
164
165 async def test_commits_enhanced_search_input_present(
166 client: AsyncClient, db_session: AsyncSession
167 ) -> None:
168 """Full-text message search input is present in the filter bar."""
169 await _seed_repo(db_session)
170 resp = await client.get(_url())
171 assert resp.status_code == 200
172 assert 'name="q"' in resp.text
173 assert "keyword in message" in resp.text
174
175
176 async def test_commits_enhanced_tag_filter_input_present(
177 client: AsyncClient, db_session: AsyncSession
178 ) -> None:
179 """Tag filter input is present in the filter bar."""
180 await _seed_repo(db_session)
181 resp = await client.get(_url())
182 assert resp.status_code == 200
183 assert 'name="tag"' in resp.text
184 assert "emotion:" in resp.text # placeholder hint text
185
186
187 # ── Server-side filtering ─────────────────────────────────────────────────────
188
189
190 async def test_commits_enhanced_author_filter_narrows_results(
191 client: AsyncClient, db_session: AsyncSession
192 ) -> None:
193 """?author=bob returns only bob's commits."""
194 await _seed_repo(db_session)
195 resp = await client.get(_url() + "?author=bob")
196 assert resp.status_code == 200
197 assert _SHA_BOB_1[:8] in resp.text
198
199
200 async def test_commits_enhanced_author_filter_excludes_others(
201 client: AsyncClient, db_session: AsyncSession
202 ) -> None:
203 """Commits by authors other than the filtered author do not appear."""
204 await _seed_repo(db_session)
205 resp = await client.get(_url() + "?author=bob")
206 assert resp.status_code == 200
207 # Alice's commit SHA should not appear
208 assert _SHA_ALICE_1[:8] not in resp.text
209
210
211 async def test_commits_enhanced_search_filter_matches_message(
212 client: AsyncClient, db_session: AsyncSession
213 ) -> None:
214 """?q=walking+bass returns the commit containing that substring."""
215 await _seed_repo(db_session)
216 resp = await client.get(_url() + "?q=walking+bass")
217 assert resp.status_code == 200
218 assert _SHA_ALICE_1[:8] in resp.text
219
220
221 async def test_commits_enhanced_search_filter_excludes_others(
222 client: AsyncClient, db_session: AsyncSession
223 ) -> None:
224 """?q= excludes commits that do not match the search term."""
225 await _seed_repo(db_session)
226 resp = await client.get(_url() + "?q=walking+bass")
227 assert resp.status_code == 200
228 # Bob's drums commit should not appear
229 assert _SHA_BOB_1[:8] not in resp.text
230
231
232 async def test_commits_enhanced_date_from_filter(
233 client: AsyncClient, db_session: AsyncSession
234 ) -> None:
235 """?dateFrom=2026-03-01 excludes commits before that date."""
236 await _seed_repo(db_session)
237 resp = await client.get(_url() + "?dateFrom=2026-03-01")
238 assert resp.status_code == 200
239 # Only Bob (2026-03-01) and merge (2026-03-02) should appear
240 assert _SHA_BOB_1[:8] in resp.text
241 assert _SHA_MERGE[:8] in resp.text
242 # Alice's January commit should not appear
243 assert _SHA_ALICE_1[:8] not in resp.text
244
245
246 async def test_commits_enhanced_tag_filter_matches_tag(
247 client: AsyncClient, db_session: AsyncSession
248 ) -> None:
249 """?tag=emotion:funky matches the commit containing that tag string."""
250 await _seed_repo(db_session)
251 resp = await client.get(_url() + "?tag=emotion%3Afunky")
252 assert resp.status_code == 200
253 assert _SHA_ALICE_1[:8] in resp.text
254 # The commit without the tag should not appear
255 assert _SHA_BOB_1[:8] not in resp.text
256
257
258 # ── Compare mode ──────────────────────────────────────────────────────────────
259
260
261 async def test_commits_enhanced_compare_toggle_btn_present(
262 client: AsyncClient, db_session: AsyncSession
263 ) -> None:
264 """Compare toggle button is present in the toolbar."""
265 await _seed_repo(db_session)
266 resp = await client.get(_url())
267 assert resp.status_code == 200
268 assert "compare-toggle-btn" in resp.text
269
270
271 async def test_commits_enhanced_compare_strip_present(
272 client: AsyncClient, db_session: AsyncSession
273 ) -> None:
274 """compare-strip container is present (initially hidden via CSS/JS)."""
275 await _seed_repo(db_session)
276 resp = await client.get(_url())
277 assert resp.status_code == 200
278 assert "compare-strip" in resp.text
279 assert "compare-link" in resp.text
280
281
282 async def test_commits_enhanced_compare_check_inputs_present(
283 client: AsyncClient, db_session: AsyncSession
284 ) -> None:
285 """Per-row compare checkboxes are rendered for each commit."""
286 await _seed_repo(db_session)
287 resp = await client.get(_url())
288 assert resp.status_code == 200
289 assert "compare-check" in resp.text
290 assert "compare-col" in resp.text
291
292
293 async def test_commits_enhanced_compare_js_function(
294 client: AsyncClient, db_session: AsyncSession
295 ) -> None:
296 """Compare mode uses SSR compare-toggle-btn and compare-strip (JS moved to commits.ts)."""
297 await _seed_repo(db_session)
298 resp = await client.get(_url())
299 assert resp.status_code == 200
300 assert "compare-toggle-btn" in resp.text
301 assert "compare-strip" in resp.text
302
303
304 # ── Metadata badge JS ─────────────────────────────────────────────────────────
305
306
307 async def test_commits_enhanced_meta_badges_container_present(
308 client: AsyncClient, db_session: AsyncSession
309 ) -> None:
310 """meta-badges span is rendered inside each commit row."""
311 await _seed_repo(db_session)
312 resp = await client.get(_url())
313 assert resp.status_code == 200
314 assert "meta-badges" in resp.text
315
316
317 async def test_commits_enhanced_badge_js_extract_function(
318 client: AsyncClient, db_session: AsyncSession
319 ) -> None:
320 """Badge logic (extractBadges/renderBadges) moved to commits.ts; page dispatches commits module."""
321 await _seed_repo(db_session)
322 resp = await client.get(_url())
323 assert resp.status_code == 200
324 assert '"page": "commits"' in resp.text
325
326
327 async def test_commits_enhanced_chip_css_classes_present(
328 client: AsyncClient, db_session: AsyncSession
329 ) -> None:
330 """CSS classes for metadata chips are defined in the page <style>."""
331 await _seed_repo(db_session)
332 resp = await client.get(_url())
333 assert resp.status_code == 200
334 assert "chip-tempo" in resp.text
335 assert "chip-key" in resp.text
336 assert "chip-emotion" in resp.text
337 assert "chip-stage" in resp.text
338 assert "chip-instr" in resp.text
339
340
341 # ── Mini-lane DAG ─────────────────────────────────────────────────────────────
342
343
344 async def test_commits_enhanced_dag_merge_arm_present(
345 client: AsyncClient, db_session: AsyncSession
346 ) -> None:
347 """dag-merge-arm element is rendered for merge commits."""
348 await _seed_repo(db_session)
349 resp = await client.get(_url())
350 assert resp.status_code == 200
351 assert "dag-merge-arm" in resp.text
352
353
354 async def test_commits_enhanced_mini_lane_dag_col_present(
355 client: AsyncClient, db_session: AsyncSession
356 ) -> None:
357 """dag-col column is rendered for every commit row."""
358 await _seed_repo(db_session)
359 resp = await client.get(_url())
360 assert resp.status_code == 200
361 assert "dag-col" in resp.text
362 assert "dag-node" in resp.text
363
364
365 # ── Pagination preserves filters ──────────────────────────────────────────────
366
367
368 async def test_commits_enhanced_pagination_preserves_filters(
369 client: AsyncClient, db_session: AsyncSession
370 ) -> None:
371 """Pagination links forward active filter params so state persists across pages."""
372 await _seed_repo(db_session)
373 resp = await client.get(_url() + "?author=alice&limit=1")
374 assert resp.status_code == 200
375 body = resp.text
376 # Older link should carry author=alice forward via active_filters
377 assert "author=alice" in body
File History 1 commit
sha256:9590cee1e0ccd6c76528f005b95d634d80f5019f0dcb7c371e149adc31d1fb65 refactor: enforce gRPC framing on all MWP wire traffic Sonnet 4.6 minor ⚠ 156 days ago