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