gabriel / musehub public
test_musehub_ui_releases_ssr.py python
180 lines 6.2 KB
Raw
sha256:a34090cc4a394a78bd72cbbe34b08cc59525141e19135b6c0ab154f10611b9ef debug(push/stream): instrument O-frame decode path with INF… Sonnet 4.6 patch 121 days ago
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_models import (
28 MusehubRelease,
29 MusehubRepo,
30 )
31
32
33 # ---------------------------------------------------------------------------
34 # Seed helpers
35 # ---------------------------------------------------------------------------
36
37
38 async def _make_repo(db: AsyncSession, owner: str = "musician", slug: str = "ssr-album") -> str:
39 """Seed a public repo and return its repo_id string."""
40 created_at = datetime.now(tz=timezone.utc)
41 owner_id = compute_identity_id(owner.encode())
42 repo = MusehubRepo(
43 repo_id=compute_repo_id(owner_id, slug, "code", created_at.isoformat()),
44 name=slug,
45 owner=owner,
46 slug=slug,
47 visibility="public",
48 owner_user_id=owner_id,
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 str(repo.repo_id)
56
57
58 async def _make_release(
59 db: AsyncSession,
60 repo_id: str,
61 *,
62 tag: str = "v1.0",
63 title: str = "Version 1.0",
64 body: str = "Release notes here.",
65 author: str = "musician",
66 channel: str = "stable",
67 is_draft: bool = False,
68 mp3_url: str | None = None,
69 ) -> MusehubRelease:
70 """Seed a release and return the ORM object."""
71 download_urls = {}
72 if mp3_url:
73 download_urls["mp3"] = mp3_url
74 release = MusehubRelease(
75 release_id=compute_release_id(repo_id, tag, now_utc_iso()),
76 repo_id=repo_id,
77 tag=tag,
78 title=title,
79 body=body,
80 author=author,
81 channel=channel,
82 is_draft=is_draft,
83 download_urls=download_urls,
84 )
85 db.add(release)
86 await db.commit()
87 await db.refresh(release)
88 return release
89
90
91 # ---------------------------------------------------------------------------
92 # Releases list SSR tests
93 # ---------------------------------------------------------------------------
94
95
96 async def test_releases_list_renders_tag_server_side(
97 client: AsyncClient,
98 db_session: AsyncSession,
99 ) -> None:
100 """Release tag is in the HTML response server-side (no JS required)."""
101 repo_id = await _make_repo(db_session)
102 await _make_release(db_session, repo_id, tag="v2.5.0", title="Groove update 2.5")
103 response = await client.get("/musician/ssr-album/releases")
104 assert response.status_code == 200
105 assert "text/html" in response.headers["content-type"]
106 assert "v2.5.0" in response.text
107
108
109 async def test_releases_list_shows_prerelease_badge(
110 client: AsyncClient,
111 db_session: AsyncSession,
112 ) -> None:
113 """Pre-release badge renders server-side for releases flagged as pre-release."""
114 repo_id = await _make_repo(db_session)
115 await _make_release(db_session, repo_id, tag="v1.0-beta", channel="beta")
116 response = await client.get("/musician/ssr-album/releases")
117 assert response.status_code == 200
118 assert "Pre-release" in response.text
119 assert "v1.0-beta" in response.text
120
121
122 async def test_releases_list_htmx_fragment_path(
123 client: AsyncClient,
124 db_session: AsyncSession,
125 ) -> None:
126 """HX-Request: true returns a bare HTML fragment without the full page shell."""
127 repo_id = await _make_repo(db_session)
128 await _make_release(db_session, repo_id, tag="v2.0", title="Earlier release")
129 await _make_release(db_session, repo_id, tag="v3.0", title="Major release")
130 response = await client.get(
131 "/musician/ssr-album/releases",
132 headers={"HX-Request": "true"},
133 )
134 assert response.status_code == 200
135 assert "<html" not in response.text
136 assert "<head" not in response.text
137 assert "v2.0" in response.text
138
139
140 async def test_releases_list_empty_state_when_no_releases(
141 client: AsyncClient,
142 db_session: AsyncSession,
143 ) -> None:
144 """Empty release list renders the empty state message server-side."""
145 await _make_repo(db_session)
146 response = await client.get("/musician/ssr-album/releases")
147 assert response.status_code == 200
148 # empty_state macro renders "No releases yet" when the list is empty.
149 assert "No releases yet" in response.text
150
151
152 # ---------------------------------------------------------------------------
153 # Release detail SSR tests
154 # ---------------------------------------------------------------------------
155
156
157 async def test_release_detail_renders_tag_server_side(
158 client: AsyncClient,
159 db_session: AsyncSession,
160 ) -> None:
161 """Release tag appears in the detail page HTML server-side."""
162 repo_id = await _make_repo(db_session)
163 await _make_release(db_session, repo_id, tag="v1.2.3", title="Polished mix")
164 response = await client.get("/musician/ssr-album/releases/v1.2.3")
165 assert response.status_code == 200
166 assert "text/html" in response.headers["content-type"]
167 assert "v1.2.3" in response.text
168 # Title should appear too.
169 assert "Polished mix" in response.text
170
171
172
173 async def test_release_detail_unknown_tag_404(
174 client: AsyncClient,
175 db_session: AsyncSession,
176 ) -> None:
177 """Non-existent release tag returns 404."""
178 await _make_repo(db_session)
179 response = await client.get("/musician/ssr-album/releases/vNOPE")
180 assert response.status_code == 404
File History 1 commit
sha256:a34090cc4a394a78bd72cbbe34b08cc59525141e19135b6c0ab154f10611b9ef debug(push/stream): instrument O-frame decode path with INF… Sonnet 4.6 patch 121 days ago