test_mist_phase6_docs_page.py
python
sha256:3ff9c9863a9891bdcde71b4a43228f66d0493e38b7cc1d09fe9eb7de774046b2
feat: add repair-commit wire endpoint (API parity with repa…
Opus 4.8
minor
⚠ breaking
1 day ago
| 1 | """Phase 6 TDD: HTML docs page at /muse/mists. |
| 2 | |
| 3 | Tests are written RED first. Run before touching ui_docs.py and creating |
| 4 | the template to confirm failure, then implement. |
| 5 | |
| 6 | Work: |
| 7 | 1. Add 'mists' entry to _PHASES in ui_docs.py. |
| 8 | 2. Add GET /muse/mists route handler. |
| 9 | 3. Create musehub/templates/musehub/pages/docs_muse_mists.html. |
| 10 | 4. Link /muse/mists from the /muse index page. |
| 11 | |
| 12 | Tests operate against the live app client fixture (no DB state needed). |
| 13 | """ |
| 14 | from __future__ import annotations |
| 15 | |
| 16 | import pytest |
| 17 | from httpx import AsyncClient |
| 18 | |
| 19 | |
| 20 | class TestDocsMistsPage: |
| 21 | @pytest.mark.asyncio |
| 22 | async def test_docs_mists_page_returns_200(self, client: AsyncClient) -> None: |
| 23 | """GET /muse/mists must return 200 with text/html content-type.""" |
| 24 | r = await client.get("/muse/mists") |
| 25 | assert r.status_code == 200, ( |
| 26 | f"Expected 200 from GET /muse/mists; got {r.status_code}" |
| 27 | ) |
| 28 | assert "text/html" in r.headers.get("content-type", ""), ( |
| 29 | "Expected HTML response from /muse/mists" |
| 30 | ) |
| 31 | |
| 32 | @pytest.mark.asyncio |
| 33 | async def test_docs_mists_page_contains_mist_keyword( |
| 34 | self, client: AsyncClient |
| 35 | ) -> None: |
| 36 | """Page body must contain 'Mist' (the domain name).""" |
| 37 | r = await client.get("/muse/mists") |
| 38 | assert r.status_code == 200 |
| 39 | assert "Mist" in r.text, "Page must contain the keyword 'Mist'" |
| 40 | |
| 41 | @pytest.mark.asyncio |
| 42 | async def test_docs_mists_page_contains_artifact_keyword( |
| 43 | self, client: AsyncClient |
| 44 | ) -> None: |
| 45 | """Page body must contain 'artifact' (core Mist concept).""" |
| 46 | r = await client.get("/muse/mists") |
| 47 | assert r.status_code == 200 |
| 48 | assert "artifact" in r.text, "Page must contain the keyword 'artifact'" |
| 49 | |
| 50 | @pytest.mark.asyncio |
| 51 | async def test_docs_mists_page_contains_content_addressed( |
| 52 | self, client: AsyncClient |
| 53 | ) -> None: |
| 54 | """Page body must contain 'content-addressed'.""" |
| 55 | r = await client.get("/muse/mists") |
| 56 | assert r.status_code == 200 |
| 57 | assert "content-addressed" in r.text, ( |
| 58 | "Page must contain the keyword 'content-addressed'" |
| 59 | ) |
| 60 | |
| 61 | @pytest.mark.asyncio |
| 62 | async def test_docs_mists_page_contains_api_section( |
| 63 | self, client: AsyncClient |
| 64 | ) -> None: |
| 65 | """Page must document the REST API surface (POST /api/mists etc.).""" |
| 66 | r = await client.get("/muse/mists") |
| 67 | assert r.status_code == 200 |
| 68 | assert "/api/mists" in r.text, ( |
| 69 | "Page must reference the /api/mists REST endpoint" |
| 70 | ) |
| 71 | |
| 72 | @pytest.mark.asyncio |
| 73 | async def test_docs_mists_page_contains_security_section( |
| 74 | self, client: AsyncClient |
| 75 | ) -> None: |
| 76 | """Page must cover the security model.""" |
| 77 | r = await client.get("/muse/mists") |
| 78 | assert r.status_code == 200 |
| 79 | assert "security" in r.text.lower(), ( |
| 80 | "Page must contain a security section" |
| 81 | ) |
| 82 | |
| 83 | @pytest.mark.asyncio |
| 84 | async def test_docs_mists_page_has_sidebar_nav( |
| 85 | self, client: AsyncClient |
| 86 | ) -> None: |
| 87 | """Page must render the sidebar navigation present on all doc pages.""" |
| 88 | r = await client.get("/muse/mists") |
| 89 | assert r.status_code == 200 |
| 90 | assert "devdocs-nav" in r.text, ( |
| 91 | "Page must include the devdocs sidebar nav (devdocs-nav class)" |
| 92 | ) |
| 93 | |
| 94 | |
| 95 | class TestDocsIndexLinksMists: |
| 96 | @pytest.mark.asyncio |
| 97 | async def test_docs_index_links_to_mists(self, client: AsyncClient) -> None: |
| 98 | """GET /muse must contain a link to /muse/mists.""" |
| 99 | r = await client.get("/muse") |
| 100 | assert r.status_code == 200 |
| 101 | assert "/muse/mists" in r.text, ( |
| 102 | "The /muse index page must link to /muse/mists" |
| 103 | ) |
| 104 | |
| 105 | @pytest.mark.asyncio |
| 106 | async def test_docs_index_mists_entry_has_description( |
| 107 | self, client: AsyncClient |
| 108 | ) -> None: |
| 109 | """The /muse index card for mists must include a non-empty description.""" |
| 110 | r = await client.get("/muse") |
| 111 | assert r.status_code == 200 |
| 112 | # The index renders phase cards from _PHASES — each has a description. |
| 113 | # Confirm the mists page is referenced alongside a meaningful description. |
| 114 | assert "Mist" in r.text, ( |
| 115 | "The /muse index must mention 'Mist' in a nav card or description" |
| 116 | ) |
| 117 | |
| 118 | |
| 119 | class TestDocsMistsRouteRegistration: |
| 120 | def test_mists_slug_in_phases_list(self) -> None: |
| 121 | """'mists' slug must appear in the _PHASES list in ui_docs.py.""" |
| 122 | from musehub.api.routes.musehub.ui_docs import _PHASES |
| 123 | slugs = [slug for slug, *_ in _PHASES] |
| 124 | assert "mists" in slugs, ( |
| 125 | f"'mists' must be in _PHASES slugs; got {slugs}" |
| 126 | ) |
| 127 | |
| 128 | def test_docs_mists_route_exists(self) -> None: |
| 129 | """The docs_mists route function must be defined in ui_docs.""" |
| 130 | import musehub.api.routes.musehub.ui_docs as _mod |
| 131 | assert hasattr(_mod, "docs_mists"), ( |
| 132 | "ui_docs.py must define a 'docs_mists' route handler" |
| 133 | ) |
File History
1 commit
sha256:3ff9c9863a9891bdcde71b4a43228f66d0493e38b7cc1d09fe9eb7de774046b2
feat: add repair-commit wire endpoint (API parity with repa…
Opus 4.8
minor
⚠
1 day ago