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