gabriel / musehub public
test_musehub_ui_settings_ssr.py python
162 lines 5.7 KB
Raw
sha256:f0ce2f5b7a3316126a89512a9f2ab9e4ac3cf2dbd7dae9155812a102138d15b4 test: add AV_19-AV_26 SSR visibility gate tests; fix settin… Sonnet 4.6 patch 21 hours ago
1 """SSR-specific tests for the MuseHub repo settings page.
2
3 Verifies that the settings page uses server-side rendering (Jinja2 templates)
4 rather than a client-side JS shell. The handler passes ``RepoSettingsResponse``
5 into the template context so field values are embedded in the HTML at
6 render-time — no client-side fetch required to display the form.
7
8 Test matrix:
9 - test_settings_page_renders_repo_name_server_side — GET page, assert repo name in form value
10 - test_settings_page_general_form_has_hx_patch — general form has ``hx-patch`` attribute
11 - test_settings_page_danger_zone_has_hx_delete — delete form has ``hx-delete``
12 - test_settings_page_section_nav_present — section nav links present
13 - test_settings_unknown_repo_404 — authenticated owner + unknown slug → 404
14 """
15 from __future__ import annotations
16
17 import pytest
18 from httpx import AsyncClient
19 from sqlalchemy.ext.asyncio import AsyncSession
20
21 from datetime import datetime, timezone
22
23 from musehub.core.genesis import compute_identity_id, compute_repo_id
24 from musehub.db.musehub_identity_models import MusehubIdentity
25 from musehub.db.musehub_repo_models import MusehubRepo
26
27 # Must match the handle injected by the auth_headers fixture (conftest._TEST_HANDLE).
28 _OWNER = "testuser"
29
30
31 # ---------------------------------------------------------------------------
32 # Fixtures / helpers
33 # ---------------------------------------------------------------------------
34
35
36 async def _make_repo(
37 db_session: AsyncSession,
38 owner: str = _OWNER,
39 slug: str = "ssr-settings-repo",
40 visibility: str = "public",
41 name: str | None = None,
42 ) -> MusehubRepo:
43 """Seed a minimal repo for SSR settings tests and return the ORM row."""
44 repo_name = name or slug
45 owner_id = compute_identity_id(owner.encode())
46 created_at = datetime.now(tz=timezone.utc)
47 repo = MusehubRepo(
48 repo_id=compute_repo_id(owner_id, slug, "code", created_at.isoformat()),
49 name=repo_name,
50 owner=owner,
51 slug=slug,
52 visibility=visibility,
53 owner_user_id=owner_id,
54 created_at=created_at,
55 updated_at=created_at,
56 )
57 db_session.add(repo)
58 await db_session.commit()
59 await db_session.refresh(repo)
60 return repo
61
62
63 # ---------------------------------------------------------------------------
64 # SSR: repo name embedded in form value at render-time
65 # ---------------------------------------------------------------------------
66
67
68 async def test_settings_page_renders_repo_name_server_side(
69 client: AsyncClient,
70 db_session: AsyncSession,
71 test_user: MusehubIdentity,
72 auth_headers: dict[str, str],
73 ) -> None:
74 """GET settings page embeds the repo name directly into the HTML form value.
75
76 With SSR, the template renders ``value="{{ s.name }}"`` so the repo name
77 is present in the raw HTML response without any client-side fetch.
78 """
79 repo = await _make_repo(
80 db_session, slug="my-ssr-repo", name="my-ssr-repo"
81 )
82 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
83 assert resp.status_code == 200
84 assert "my-ssr-repo" in resp.text
85
86
87 # ---------------------------------------------------------------------------
88 # HTMX attributes
89 # ---------------------------------------------------------------------------
90
91
92 async def test_settings_page_general_form_has_hx_patch(
93 client: AsyncClient,
94 db_session: AsyncSession,
95 test_user: MusehubIdentity,
96 auth_headers: dict[str, str],
97 ) -> None:
98 """General settings form uses ``hx-patch`` for HTMX section-save."""
99 repo = await _make_repo(db_session, slug="htmx-patch-repo")
100 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
101 assert resp.status_code == 200
102 assert "hx-patch" in resp.text
103
104
105 async def test_settings_page_danger_zone_has_hx_delete(
106 client: AsyncClient,
107 db_session: AsyncSession,
108 test_user: MusehubIdentity,
109 auth_headers: dict[str, str],
110 ) -> None:
111 """Danger Zone delete form uses ``hx-delete`` for HTMX repo deletion."""
112 repo = await _make_repo(db_session, slug="htmx-delete-repo")
113 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
114 assert resp.status_code == 200
115 assert "hx-delete" in resp.text
116
117
118 # ---------------------------------------------------------------------------
119 # Section navigation
120 # ---------------------------------------------------------------------------
121
122
123 async def test_settings_page_section_nav_present(
124 client: AsyncClient,
125 db_session: AsyncSession,
126 test_user: MusehubIdentity,
127 auth_headers: dict[str, str],
128 ) -> None:
129 """Settings page includes Alpine.js-powered section navigation links.
130
131 The nav uses ``x-on:click.prevent`` to switch sections client-side without
132 a server round-trip, and ``:class`` binding to highlight the active link.
133 """
134 repo = await _make_repo(db_session, slug="sec-nav-repo")
135 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
136 assert resp.status_code == 200
137 html = resp.text
138 assert "settings-nav-link" in html
139 # Alpine.js section switching
140 assert "x-data" in html
141 assert "x-show" in html
142 # All four sections present
143 assert "section-general" in html
144 assert "section-merge" in html
145 assert "section-collaboration" in html
146 assert "section-danger" in html
147
148
149 # ---------------------------------------------------------------------------
150 # 404 for unknown repo
151 # ---------------------------------------------------------------------------
152
153
154 async def test_settings_unknown_repo_404(
155 client: AsyncClient,
156 db_session: AsyncSession,
157 test_user: MusehubIdentity,
158 auth_headers: dict[str, str],
159 ) -> None:
160 """Authenticated owner GET on an unknown slug returns 404."""
161 resp = await client.get(f"/{_OWNER}/nonexistent-repo-ssr-404/settings")
162 assert resp.status_code == 404
File History 1 commit
sha256:f0ce2f5b7a3316126a89512a9f2ab9e4ac3cf2dbd7dae9155812a102138d15b4 test: add AV_19-AV_26 SSR visibility gate tests; fix settin… Sonnet 4.6 patch 21 hours ago