test_landing_k12.py python
282 lines 11.2 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 2 days ago
1 """Unit tests for landing manifest and Landing + access clarity contract (§LAC.12)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 import pytest
8 import yaml
9
10 from tools.app.engine import handle_health
11 from tools.landing.schema import load_manifest
12 from tools.landing.validate import (
13 FROZEN_PRIMARY_DOWNLOAD_HREF,
14 LAC_SECTION_IDS,
15 validate_landing,
16 )
17
18 KIT_ROOT = Path(__file__).resolve().parents[2]
19 MANIFEST = KIT_ROOT / "docs" / "landing" / "manifest.yaml"
20 INDEX = KIT_ROOT / "docs" / "landing" / "index.html"
21
22
23 def test_manifest_loads_version_one() -> None:
24 manifest = load_manifest(MANIFEST)
25 assert manifest.version == 1
26 assert manifest.license == "MIT"
27
28
29 def test_manifest_section_ids_match_lac_contract() -> None:
30 """Public IA contract — visitor clarity pass (amends LAC §LAC.3.1)."""
31 manifest = load_manifest(MANIFEST)
32 assert manifest.section_ids == LAC_SECTION_IDS
33 assert len(manifest.section_ids) == 9
34 assert "how-it-works" in manifest.section_ids
35 assert "kit-basics" in manifest.section_ids
36 assert "musehub" in manifest.section_ids
37 assert "living-docs" not in manifest.section_ids
38 assert "funnel" not in manifest.section_ids
39
40
41 def test_primary_download_href_equals_frozen_dmg() -> None:
42 manifest = load_manifest(MANIFEST)
43 html = INDEX.read_text(encoding="utf-8")
44 assert manifest.primary_download_href == FROZEN_PRIMARY_DOWNLOAD_HREF
45 assert FROZEN_PRIMARY_DOWNLOAD_HREF in html
46 assert 'id="cta-download-mac"' in html
47 assert "Download Mac console (Apple Silicon)" in html
48 assert "via the Download link above" in html
49 assert "via the button above" not in html
50
51
52 def test_forbidden_strings_absent_on_landing() -> None:
53 html = INDEX.read_text(encoding="utf-8")
54 for phrase in ("Sign up", "Create account", "executes tasks"):
55 assert phrase not in html
56 assert "mint session_credential" not in html.lower()
57 assert "mint csrf" not in html.lower()
58 assert "mints session_credential" not in html.lower()
59
60
61 def test_no_done_todo_wip_status_table_on_main_landing() -> None:
62 html = INDEX.read_text(encoding="utf-8")
63 assert 'id="roadmap-public"' not in html
64 assert "K9b DONE" not in html
65 assert ">DONE<" not in html
66 assert ">TODO<" not in html
67 assert ">WIP<" not in html
68
69
70 def test_four_diagram_paths_referenced() -> None:
71 html = INDEX.read_text(encoding="utf-8")
72 for name in ("lanes.svg", "regimes.svg", "layers.svg", "kit-consumer.svg"):
73 assert f"assets/diagrams/{name}" in html
74 assert (KIT_ROOT / "docs" / "landing" / "assets" / "diagrams" / name).is_file()
75
76
77 def test_health_result_schema_documents_repo_root() -> None:
78 """Narrow Q0 additive: health success result includes absolute repo_root (§LAC.6.3)."""
79 envelope = handle_health(port=8765, bind="127.0.0.1", repo_root=KIT_ROOT)
80 assert envelope.ok is True
81 assert envelope.result is not None
82 assert set(envelope.result) >= {"status", "port", "bind", "repo_root"}
83 assert envelope.result["repo_root"] == str(KIT_ROOT.resolve())
84 assert envelope.result["status"] == "ok"
85 assert envelope.result["port"] == 8765
86 assert envelope.result["bind"] == "127.0.0.1"
87
88
89 def test_manifest_persona_ids_a_through_e() -> None:
90 manifest = load_manifest(MANIFEST)
91 assert manifest.persona_ids == ("A", "B", "C", "D", "E")
92
93
94 def test_manifest_status_badges_frozen_enum() -> None:
95 manifest = load_manifest(MANIFEST)
96 assert manifest.status_badges == frozenset({"dogfood", "reference", "aspirational"})
97
98
99 def test_manifest_rejects_bad_version(tmp_path: Path) -> None:
100 bad = tmp_path / "manifest.yaml"
101 bad.write_text(yaml.dump({"version": 99, "license": "MIT"}), encoding="utf-8")
102 with pytest.raises(ValueError, match="unsupported manifest version"):
103 load_manifest(bad)
104
105
106 def test_license_file_references_mit() -> None:
107 text = (KIT_ROOT / "LICENSE").read_text(encoding="utf-8")
108 assert "MIT License" in text
109 assert "Copyright 2026 Overseer Kit contributors" in text
110 assert "Apache License" not in text
111
112
113 def test_pyproject_license_matches_manifest() -> None:
114 pyproject = (KIT_ROOT / "pyproject.toml").read_text(encoding="utf-8")
115 manifest = load_manifest(MANIFEST)
116 assert 'license = { text = "MIT" }' in pyproject
117 assert manifest.license == "MIT"
118
119
120 def test_validate_missing_manifest_fails(tmp_path: Path) -> None:
121 result = validate_landing(tmp_path)
122 assert not result.ok
123 assert any(e.startswith("manifest_parse:") for e in result.errors)
124
125
126 def test_readme_and_runbook_carry_open_local_console_playbook() -> None:
127 readme = (KIT_ROOT / "README.md").read_text(encoding="utf-8")
128 runbook = (KIT_ROOT / "docs" / "TRACK-Q-DESKTOP-OPERATOR-RUNBOOK.md").read_text(
129 encoding="utf-8"
130 )
131 assert "Open the local console" in readme
132 assert "Open the local console" in runbook
133 assert "OVERSEER_REPO_ROOT" in readme
134 assert FROZEN_PRIMARY_DOWNLOAD_HREF in readme
135
136
137 def test_main_landing_has_no_personal_product_doors() -> None:
138 html = INDEX.read_text(encoding="utf-8")
139 for phrase in ("Knowtation", "Scooling", "VideoFactory", "musehub.ai"):
140 assert phrase not in html
141 assert 'id="theme-toggle"' in html
142 assert 'id="how-it-works"' in html
143 assert "logo-mark" in html
144 assert "brand-name" in html
145 assert "Overseer Kit" in html
146 assert "Honesty for your agents" in html
147 assert "logo-mark-hero" not in html
148 assert "hero-brand" not in html
149 assert "Lock the plan" in html
150 assert "Keep docs truthful" in html
151 assert "Spend models wisely" in html
152 assert "Close clean" in html
153 assert "paste-ready prompt" not in html.lower()
154 assert "model-routing.yaml" not in html
155 assert "governance-sync" not in html
156 assert "cta-honesty" not in html.split('id="hero"')[1].split('id="problem"')[0]
157 assert "hero-terminal" in html
158 assert "ok check-ok" in html
159 assert 'src="assets/diagrams/honesty-loop.svg"' in html
160 assert "btn-primary" not in html.split('id="hero"')[1].split('id="problem"')[0]
161 assert 'href="docs.html">Docs</a>' in html
162 assert (KIT_ROOT / "docs" / "landing" / "docs.html").is_file()
163 assert "https://github.com/aaronrene/overseer-kit#readme" not in html
164 assert 'href="assets/favicon.ico"' in html
165 assert (KIT_ROOT / "docs" / "landing" / "assets" / "favicon.ico").is_file()
166 assert (KIT_ROOT / "docs" / "landing" / "assets" / "ok-mark-1024.png").is_file()
167 assert 'href="http://127.0.0.1:8765/"' in html
168 assert "id=\"local-console-loopback\"" in html or 'id="local-console-loopback"' in html
169 assert "ok hosted-dashboard" not in html
170 assert "../../README.md" not in html
171 assert "../CONSUMER-ADAPTER-PATTERN.md" not in html
172
173
174 def test_musehub_band_after_console_access() -> None:
175 html = INDEX.read_text(encoding="utf-8")
176 assert 'id="musehub"' in html
177 assert "Optional deepen with MuseHub" in html
178 assert "assets/musehub-logo.svg" in html
179 assert "musehub.ai" not in html
180 assert "K7-DOGFOOD-OPERATOR-RUNBOOK" not in html
181 assert "ok init --regime muse+git-mirror" in html
182 logo = (KIT_ROOT / "docs" / "landing" / "assets" / "musehub-logo.svg").read_text(
183 encoding="utf-8"
184 )
185 assert "#6EA0F3" in logo
186 hero = html.index('id="hero"')
187 kit_basics = html.index('id="kit-basics"')
188 problem = html.index('id="problem"')
189 console = html.index('id="console-access"')
190 muse = html.index('id="musehub"')
191 next_steps = html.index('id="next-steps"')
192 assert hero < kit_basics < problem < console < muse < next_steps
193 kit_block = html.split('id="kit-basics"')[1].split('id="problem"')[0]
194 assert 'id="mechanical-check"' in kit_block
195 assert "Mechanical check" in kit_block
196 assert "your bar, on-disk evidence" in kit_block
197 assert "against that bar" in kit_block
198 assert "Customize the checks" in kit_block
199 assert "assets/diagrams/mechanical-check.svg" in kit_block
200 assert kit_block.index("mechanical-check") < kit_block.index("Roadmap · Handover")
201 assert "mech-check-details" in kit_block
202 assert "Technical detail" in kit_block
203 assert "ok verify-step --through current" in kit_block
204 assert "checkpoints.enabled: true" in kit_block
205 assert "ok review --freeze" in kit_block
206 assert "Check OK" in kit_block
207 assert "Any work" in kit_block
208 assert "Same honesty loop" in kit_block
209 assert "Anywhere you work" in kit_block
210 assert "Lanes" in kit_block
211
212
213 def test_theme_defaults_dark_not_os_preference() -> None:
214 js = (KIT_ROOT / "docs" / "landing" / "assets" / "theme.js").read_text(encoding="utf-8")
215 assert "matchMedia" not in js
216 assert 'return "dark"' in js
217
218
219 def test_landing_and_console_footers_say_mit() -> None:
220 landing = INDEX.read_text(encoding="utf-8")
221 scenarios = (
222 KIT_ROOT / "docs" / "landing" / "scenarios" / "index.html"
223 ).read_text(encoding="utf-8")
224 console = (KIT_ROOT / "tools" / "app" / "static" / "index.html").read_text(
225 encoding="utf-8"
226 )
227 assert ">MIT</a>" in landing or ">MIT open source</a>" in landing
228 assert ">MIT</a>" in scenarios or "MIT" in scenarios
229 assert "Open source — MIT" in console
230 footer = landing.split('class="site-footer"')[1]
231 assert "Hosting" not in footer
232 assert "footer-tagline" in landing
233 assert "🆗 Overseer Kit — portable governance for AI-assisted development." in footer
234 assert "🆗 Overseer Kit — portable governance for AI-assisted development." in scenarios
235 assert "Apache-2.0" not in landing
236 assert "Apache-2.0" not in scenarios
237 assert "Apache-2.0" not in console
238
239
240 def test_landing_funnel_steps_live_inside_next_steps() -> None:
241 html = INDEX.read_text(encoding="utf-8")
242 assert 'id="funnel"' in html
243 assert 'id="funnel-github"' in html
244 assert 'id="funnel-kit"' in html
245 assert 'id="funnel-musehub"' in html
246 next_start = html.index('id="next-steps"')
247 scenarios = html.index('id="scenarios"')
248 funnel = html.index('id="funnel"')
249 assert next_start < funnel < scenarios
250
251
252 def test_validate_rejects_apache_license_when_manifest_is_mit(tmp_path: Path) -> None:
253 """Fail-closed: SPDX flip must keep LICENSE and manifest aligned (§MIT.4)."""
254 landing = tmp_path / "docs" / "landing"
255 landing.mkdir(parents=True)
256 manifest_src = (KIT_ROOT / "docs" / "landing" / "manifest.yaml").read_text(
257 encoding="utf-8"
258 )
259 (landing / "manifest.yaml").write_text(manifest_src, encoding="utf-8")
260 (landing / "index.html").write_text(
261 (KIT_ROOT / "docs" / "landing" / "index.html").read_text(encoding="utf-8"),
262 encoding="utf-8",
263 )
264 scenarios = landing / "scenarios"
265 scenarios.mkdir()
266 (scenarios / "index.html").write_text(
267 (KIT_ROOT / "docs" / "landing" / "scenarios" / "index.html").read_text(
268 encoding="utf-8"
269 ),
270 encoding="utf-8",
271 )
272 (landing / "assets").mkdir()
273 (landing / "assets" / "style.css").write_text("body{}", encoding="utf-8")
274 (tmp_path / "LICENSE").write_text(
275 "Apache License Version 2.0\nApache-2.0\n", encoding="utf-8"
276 )
277 (tmp_path / "SECURITY.md").write_text(
278 "Reporting a vulnerability\n", encoding="utf-8"
279 )
280 result = validate_landing(tmp_path)
281 assert not result.ok
282 assert any(e.startswith("license:") for e in result.errors)
File History 2 commits
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 2 days ago
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 54 days ago