gabriel / musehub public
test_musehub_ui_explore_ssr.py python
121 lines 4.0 KB
Raw
sha256:9590cee1e0ccd6c76528f005b95d634d80f5019f0dcb7c371e149adc31d1fb65 refactor: enforce gRPC framing on all MWP wire traffic Sonnet 4.6 minor ⚠ breaking 156 days ago
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 musehub.db.musehub_models import MusehubRepo
21
22
23 # ---------------------------------------------------------------------------
24 # Seed helpers
25 # ---------------------------------------------------------------------------
26
27
28 async def _make_public_repo(
29 db: AsyncSession,
30 *,
31 owner: str = "artist",
32 slug: str = "cool-album",
33 name: str = "cool-album",
34 tags: list[str] | None = None,
35 ) -> MusehubRepo:
36 """Seed a public repo and return the ORM object."""
37 repo = MusehubRepo(
38 name=name,
39 owner=owner,
40 slug=slug,
41 visibility="public",
42 owner_user_id=f"uid-{slug}",
43 tags=tags or [],
44 )
45 db.add(repo)
46 await db.commit()
47 await db.refresh(repo)
48 return repo
49
50
51 # ---------------------------------------------------------------------------
52 # Explore page SSR tests
53 # ---------------------------------------------------------------------------
54
55
56 async def test_explore_page_renders_repo_name_server_side(
57 client: AsyncClient,
58 db_session: AsyncSession,
59 ) -> None:
60 """Repo name is in the HTML response without any client-side JS execution."""
61 await _make_public_repo(db_session, slug="jazz-sessions", name="jazz-sessions")
62 response = await client.get("/explore")
63 assert response.status_code == 200
64 assert "text/html" in response.headers["content-type"]
65 assert "jazz-sessions" in response.text
66
67
68 async def test_explore_page_sort_filter_form_has_hx_get(
69 client: AsyncClient,
70 db_session: AsyncSession,
71 ) -> None:
72 """Filter form has hx-get attribute enabling HTMX partial swap."""
73 response = await client.get("/explore")
74 assert response.status_code == 200
75 assert 'hx-get="/explore"' in response.text
76
77
78 async def test_explore_page_genre_filter_narrows_repos(
79 client: AsyncClient,
80 db_session: AsyncSession,
81 ) -> None:
82 """?topic=jazz returns only jazz-tagged repos (genre filter works SSR)."""
83 await _make_public_repo(
84 db_session, slug="jazz-album", name="jazz-album", tags=["jazz", "piano"]
85 )
86 await _make_public_repo(
87 db_session, slug="rock-album", name="rock-album", tags=["rock", "guitar"]
88 )
89 response = await client.get("/explore?topic=jazz")
90 assert response.status_code == 200
91 body = response.text
92 assert "jazz-album" in body
93 assert "rock-album" not in body
94
95
96 async def test_explore_page_htmx_fragment_path(
97 client: AsyncClient,
98 db_session: AsyncSession,
99 ) -> None:
100 """HX-Request: true returns a bare HTML fragment without the full page shell."""
101 await _make_public_repo(db_session, slug="htmx-repo", name="htmx-repo")
102 response = await client.get(
103 "/explore",
104 headers={"HX-Request": "true"},
105 )
106 assert response.status_code == 200
107 assert "<html" not in response.text
108 assert "<head" not in response.text
109 # Fragment should contain the repo or empty-state markup.
110 assert "htmx-repo" in response.text or "No repositories found" in response.text
111
112
113 async def test_explore_page_empty_state_when_no_repos(
114 client: AsyncClient,
115 db_session: AsyncSession,
116 ) -> None:
117 """When no public repos exist the empty-state message is rendered SSR."""
118 response = await client.get("/explore")
119 assert response.status_code == 200
120 assert "No repositories found" in response.text
121
File History 1 commit
sha256:9590cee1e0ccd6c76528f005b95d634d80f5019f0dcb7c371e149adc31d1fb65 refactor: enforce gRPC framing on all MWP wire traffic Sonnet 4.6 minor ⚠ 156 days ago