gabriel / musehub public
test_musehub_ui_settings_ssr.py python
148 lines 5.3 KB
Raw
sha256:f0ce2f5b7a3316126a89512a9f2ab9e4ac3cf2dbd7dae9155812a102138d15b4 test: add AV_19-AV_26 SSR visibility gate tests; fix settin… Sonnet 4.6 patch 7 days 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 — 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_repo_models import MusehubRepo
25
26
27 # ---------------------------------------------------------------------------
28 # Fixtures / helpers
29 # ---------------------------------------------------------------------------
30
31
32 async def _make_repo(
33 db_session: AsyncSession,
34 owner: str = "ssrowner",
35 slug: str = "ssr-settings-repo",
36 visibility: str = "public",
37 name: str | None = None,
38 ) -> MusehubRepo:
39 """Seed a minimal repo for SSR settings tests and return the ORM row."""
40 repo_name = name or slug
41 owner_id = compute_identity_id(owner.encode())
42 created_at = datetime.now(tz=timezone.utc)
43 repo = MusehubRepo(
44 repo_id=compute_repo_id(owner_id, slug, "code", created_at.isoformat()),
45 name=repo_name,
46 owner=owner,
47 slug=slug,
48 visibility=visibility,
49 owner_user_id=owner_id,
50 created_at=created_at,
51 updated_at=created_at,
52 )
53 db_session.add(repo)
54 await db_session.commit()
55 await db_session.refresh(repo)
56 return repo
57
58
59 # ---------------------------------------------------------------------------
60 # SSR: repo name embedded in form value at render-time
61 # ---------------------------------------------------------------------------
62
63
64 async def test_settings_page_renders_repo_name_server_side(
65 client: AsyncClient,
66 db_session: AsyncSession,
67 ) -> None:
68 """GET settings page embeds the repo name directly into the HTML form value.
69
70 With SSR, the template renders ``value="{{ s.name }}"`` so the repo name
71 is present in the raw HTML response without any client-side fetch.
72 """
73 repo = await _make_repo(
74 db_session, owner="ssrname", slug="my-ssr-repo", name="my-ssr-repo"
75 )
76 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
77 assert resp.status_code == 200
78 assert "my-ssr-repo" in resp.text
79
80
81 # ---------------------------------------------------------------------------
82 # HTMX attributes
83 # ---------------------------------------------------------------------------
84
85
86 async def test_settings_page_general_form_has_hx_patch(
87 client: AsyncClient,
88 db_session: AsyncSession,
89 ) -> None:
90 """General settings form uses ``hx-patch`` for HTMX section-save."""
91 repo = await _make_repo(db_session, owner="htmxpatch", slug="htmx-patch-repo")
92 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
93 assert resp.status_code == 200
94 assert "hx-patch" in resp.text
95
96
97 async def test_settings_page_danger_zone_has_hx_delete(
98 client: AsyncClient,
99 db_session: AsyncSession,
100 ) -> None:
101 """Danger Zone delete form uses ``hx-delete`` for HTMX repo deletion."""
102 repo = await _make_repo(db_session, owner="htmxdel", slug="htmx-delete-repo")
103 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
104 assert resp.status_code == 200
105 assert "hx-delete" in resp.text
106
107
108 # ---------------------------------------------------------------------------
109 # Section navigation
110 # ---------------------------------------------------------------------------
111
112
113 async def test_settings_page_section_nav_present(
114 client: AsyncClient,
115 db_session: AsyncSession,
116 ) -> None:
117 """Settings page includes Alpine.js-powered section navigation links.
118
119 The nav uses ``x-on:click.prevent`` to switch sections client-side without
120 a server round-trip, and ``:class`` binding to highlight the active link.
121 """
122 repo = await _make_repo(db_session, owner="secnav", slug="sec-nav-repo")
123 resp = await client.get(f"/{repo.owner}/{repo.slug}/settings")
124 assert resp.status_code == 200
125 html = resp.text
126 assert "settings-nav-link" in html
127 # Alpine.js section switching
128 assert "x-data" in html
129 assert "x-show" in html
130 # All four sections present
131 assert "section-general" in html
132 assert "section-merge" in html
133 assert "section-collaboration" in html
134 assert "section-danger" in html
135
136
137 # ---------------------------------------------------------------------------
138 # 404 for unknown repo
139 # ---------------------------------------------------------------------------
140
141
142 async def test_settings_unknown_repo_404(
143 client: AsyncClient,
144 db_session: AsyncSession,
145 ) -> None:
146 """GET settings for an unknown repo/slug returns 404."""
147 resp = await client.get("/nobody/nonexistent-repo-ssr/settings")
148 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 7 days ago