gabriel / musehub public

test_musehub_ui_explore_ssr.py file-level

at sha256:3 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:0 fix: fall back to any indexed mpack in read_object_bytes when push mpac… · gabriel · Jun 17, 2026
1 """SSR tests for MuseHub explore page — issue #576.
2
3 Validates that repo data is rendered server-side into HTML (not deferred to
4 client JS) and that HTMX fragment requests return bare grid HTML without the
5 full page shell.
6
7 Covers GET /explore:
8 - test_explore_page_renders_repo_name_server_side — repo name in HTML
9 - test_explore_page_sort_filter_form_has_hx_get — filter form has hx-get
10 - test_explore_page_genre_filter_narrows_repos — ?topic=jazz → jazz-tagged
11 - test_explore_page_htmx_fragment_path — HX-Request → fragment only
12 - test_explore_page_empty_state_when_no_repos — no public repos → empty state
13 """
14 from __future__ import annotations
15
16 import pytest
17 from httpx import AsyncClient
18 from sqlalchemy.ext.asyncio import AsyncSession
19
20 from datetime import datetime, timezone
21 from musehub.core.genesis import compute_identity_id, compute_repo_id
22 from musehub.db.musehub_repo_models import MusehubRepo
23
24
25 # ---------------------------------------------------------------------------
26 # Seed helpers
27 # ---------------------------------------------------------------------------
28
29
30 async def _make_public_repo(
31 db: AsyncSession,
32 *,
33 owner: str = "artist",
34 slug: str = "cool-album",
35 name: str = "cool-album",
36 tags: list[str] | None = None,
37 ) -> MusehubRepo:
38 """Seed a public repo and return the ORM object."""
39 created_at = datetime.now(tz=timezone.utc)
40 owner_id = compute_identity_id(owner.encode())
41 repo = MusehubRepo(
42 repo_id=compute_repo_id(owner_id, slug, "code", created_at.isoformat()),
43 name=name,
44 owner=owner,
45 slug=slug,
46 visibility="public",
47 owner_user_id=owner_id,
48 tags=tags or [],
49 created_at=created_at,
50 updated_at=created_at,
51 )
52 db.add(repo)
53 await db.commit()
54 await db.refresh(repo)
55 return repo
56
57
58 # ---------------------------------------------------------------------------
59 # Explore page SSR tests
60 # ---------------------------------------------------------------------------
61
62
63 async def test_explore_page_renders_repo_name_server_side(
64 client: AsyncClient,
65 db_session: AsyncSession,
66 ) -> None:
67 """Repo name is in the HTML response without any client-side JS execution."""
68 await _make_public_repo(db_session, slug="jazz-sessions", name="jazz-sessions")
69 response = await client.get("/explore")
70 assert response.status_code == 200
71 assert "text/html" in response.headers["content-type"]
72 assert "jazz-sessions" in response.text
73
74
75 async def test_explore_page_sort_filter_form_has_hx_get(
76 client: AsyncClient,
77 db_session: AsyncSession,
78 ) -> None:
79 """Filter form has hx-get attribute enabling HTMX partial swap."""
80 response = await client.get("/explore")
81 assert response.status_code == 200
82 assert 'hx-get="/explore"' in response.text
83
84
85 async def test_explore_page_genre_filter_narrows_repos(
86 client: AsyncClient,
87 db_session: AsyncSession,
88 ) -> None:
89 """?topic=jazz returns only jazz-tagged repos (genre filter works SSR)."""
90 await _make_public_repo(
91 db_session, slug="jazz-album", name="jazz-album", tags=["jazz", "piano"]
92 )
93 await _make_public_repo(
94 db_session, slug="rock-album", name="rock-album", tags=["rock", "guitar"]
95 )
96 response = await client.get("/explore?topic=jazz")
97 assert response.status_code == 200
98 body = response.text
99 assert "jazz-album" in body
100 assert "rock-album" not in body
101
102
103 async def test_explore_page_htmx_fragment_path(
104 client: AsyncClient,
105 db_session: AsyncSession,
106 ) -> None:
107 """HX-Request: true returns a bare HTML fragment without the full page shell."""
108 await _make_public_repo(db_session, slug="htmx-repo", name="htmx-repo")
109 response = await client.get(
110 "/explore",
111 headers={"HX-Request": "true"},
112 )
113 assert response.status_code == 200
114 assert "<html" not in response.text
115 assert "<head" not in response.text
116 # Fragment should contain the repo or empty-state markup.
117 assert "htmx-repo" in response.text or "No repositories found" in response.text
118
119
120 async def test_explore_page_empty_state_when_no_repos(
121 client: AsyncClient,
122 db_session: AsyncSession,
123 ) -> None:
124 """When no public repos exist the empty-state message is rendered SSR."""
125 response = await client.get("/explore")
126 assert response.status_code == 200
127 assert "No repositories found" in response.text
128