gabriel / musehub public

test_musehub_ui_releases_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 releases UI pages — issue #572.
2
3 Validates that release data is rendered server-side into HTML (not deferred
4 to client JS) and that HTMX fragment requests return bare HTML without the
5 full page shell.
6
7 Covers GET /{owner}/{repo_slug}/releases:
8 - test_releases_list_renders_tag_server_side — release tag in HTML
9 - test_releases_list_shows_prerelease_badge — pre-release badge in HTML
10 - test_releases_list_htmx_fragment_path — HX-Request: true → bare fragment
11 - test_releases_list_empty_state_when_no_releases — no releases → empty state
12
13 Covers GET /{owner}/{repo_slug}/releases/{tag}:
14 - test_release_detail_renders_tag_server_side — tag in HTML
15 - test_release_detail_shows_audio_player_container — audioUrl → #release-audio-player div
16 - test_release_detail_unknown_tag_404 — unknown tag → 404
17 """
18 from __future__ import annotations
19
20 import pytest
21 from datetime import datetime, timezone
22 from httpx import AsyncClient
23 from sqlalchemy.ext.asyncio import AsyncSession
24
25 from muse.core.types import now_utc_iso
26 from musehub.core.genesis import compute_identity_id, compute_release_id, compute_repo_id
27 from musehub.db.musehub_release_models import MusehubRelease
28 from musehub.db.musehub_repo_models import MusehubRepo
29
30
31 # ---------------------------------------------------------------------------
32 # Seed helpers
33 # ---------------------------------------------------------------------------
34
35
36 async def _make_repo(db: AsyncSession, owner: str = "musician", slug: str = "ssr-album") -> str:
37 """Seed a public repo and return its repo_id string."""
38 created_at = datetime.now(tz=timezone.utc)
39 owner_id = compute_identity_id(owner.encode())
40 repo = MusehubRepo(
41 repo_id=compute_repo_id(owner_id, slug, "code", created_at.isoformat()),
42 name=slug,
43 owner=owner,
44 slug=slug,
45 visibility="public",
46 owner_user_id=owner_id,
47 created_at=created_at,
48 updated_at=created_at,
49 )
50 db.add(repo)
51 await db.commit()
52 await db.refresh(repo)
53 return str(repo.repo_id)
54
55
56 async def _make_release(
57 db: AsyncSession,
58 repo_id: str,
59 *,
60 tag: str = "v1.0",
61 title: str = "Version 1.0",
62 body: str = "Release notes here.",
63 author: str = "musician",
64 channel: str = "stable",
65 is_draft: bool = False,
66 mp3_url: str | None = None,
67 ) -> MusehubRelease:
68 """Seed a release and return the ORM object."""
69 download_urls = {}
70 if mp3_url:
71 download_urls["mp3"] = mp3_url
72 release = MusehubRelease(
73 release_id=compute_release_id(repo_id, tag, now_utc_iso()),
74 repo_id=repo_id,
75 tag=tag,
76 title=title,
77 body=body,
78 author=author,
79 channel=channel,
80 is_draft=is_draft,
81 download_urls=download_urls,
82 )
83 db.add(release)
84 await db.commit()
85 await db.refresh(release)
86 return release
87
88
89 # ---------------------------------------------------------------------------
90 # Releases list SSR tests
91 # ---------------------------------------------------------------------------
92
93
94 async def test_releases_list_renders_tag_server_side(
95 client: AsyncClient,
96 db_session: AsyncSession,
97 ) -> None:
98 """Release tag is in the HTML response server-side (no JS required)."""
99 repo_id = await _make_repo(db_session)
100 await _make_release(db_session, repo_id, tag="v2.5.0", title="Groove update 2.5")
101 response = await client.get("/musician/ssr-album/releases")
102 assert response.status_code == 200
103 assert "text/html" in response.headers["content-type"]
104 assert "v2.5.0" in response.text
105
106
107 async def test_releases_list_shows_prerelease_badge(
108 client: AsyncClient,
109 db_session: AsyncSession,
110 ) -> None:
111 """Pre-release badge renders server-side for releases flagged as pre-release."""
112 repo_id = await _make_repo(db_session)
113 await _make_release(db_session, repo_id, tag="v1.0-beta", channel="beta")
114 response = await client.get("/musician/ssr-album/releases")
115 assert response.status_code == 200
116 assert "Pre-release" in response.text
117 assert "v1.0-beta" in response.text
118
119
120 async def test_releases_list_htmx_fragment_path(
121 client: AsyncClient,
122 db_session: AsyncSession,
123 ) -> None:
124 """HX-Request: true returns a bare HTML fragment without the full page shell."""
125 repo_id = await _make_repo(db_session)
126 await _make_release(db_session, repo_id, tag="v2.0", title="Earlier release")
127 await _make_release(db_session, repo_id, tag="v3.0", title="Major release")
128 response = await client.get(
129 "/musician/ssr-album/releases",
130 headers={"HX-Request": "true"},
131 )
132 assert response.status_code == 200
133 assert "<html" not in response.text
134 assert "<head" not in response.text
135 assert "v2.0" in response.text
136
137
138 async def test_releases_list_empty_state_when_no_releases(
139 client: AsyncClient,
140 db_session: AsyncSession,
141 ) -> None:
142 """Empty release list renders the empty state message server-side."""
143 await _make_repo(db_session)
144 response = await client.get("/musician/ssr-album/releases")
145 assert response.status_code == 200
146 # empty_state macro renders "No releases yet" when the list is empty.
147 assert "No releases yet" in response.text
148
149
150 # ---------------------------------------------------------------------------
151 # Release detail SSR tests
152 # ---------------------------------------------------------------------------
153
154
155 async def test_release_detail_renders_tag_server_side(
156 client: AsyncClient,
157 db_session: AsyncSession,
158 ) -> None:
159 """Release tag appears in the detail page HTML server-side."""
160 repo_id = await _make_repo(db_session)
161 await _make_release(db_session, repo_id, tag="v1.2.3", title="Polished mix")
162 response = await client.get("/musician/ssr-album/releases/v1.2.3")
163 assert response.status_code == 200
164 assert "text/html" in response.headers["content-type"]
165 assert "v1.2.3" in response.text
166 # Title should appear too.
167 assert "Polished mix" in response.text
168
169
170
171 async def test_release_detail_unknown_tag_404(
172 client: AsyncClient,
173 db_session: AsyncSession,
174 ) -> None:
175 """Non-existent release tag returns 404."""
176 await _make_repo(db_session)
177 response = await client.get("/musician/ssr-album/releases/vNOPE")
178 assert response.status_code == 404