test_workspace_unit.py python
251 lines 8.2 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
1 """Unit tests for multi-repo workspace lanes (§MR.10 unit)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 import pytest
8 import yaml
9
10 from tools.workspace.board_names import (
11 board_name_violation,
12 expected_handover_basename,
13 is_bare_legacy_basename,
14 repo_slug,
15 )
16 from tools.workspace.manifest import expand_root, validate_manifest_dict
17 from tools.workspace.next_extract import (
18 extract_next_blocks,
19 legacy_forbidden_archived_headings,
20 tip_hash_hex,
21 )
22 from tools.workspace.types import WorkspaceLoadError
23 from tests.fixtures.workspace import build_two_repo_constellation, primary_fence
24
25
26 def test_repo_slug_normalization() -> None:
27 assert repo_slug("overseer-kit") == "OVERSEER-KIT"
28 assert repo_slug("scooling") == "SCOOLING"
29 assert repo_slug("foo__bar!!") == "FOO-BAR"
30 assert expected_handover_basename("scooling") == "SCOOLING-OVERSEER-HANDOVER.md"
31
32
33 def test_bare_vs_prefixed_basename_classifier() -> None:
34 assert is_bare_legacy_basename("OVERSEER-HANDOVER.md", kind="handover")
35 assert is_bare_legacy_basename("roadmap.md", kind="roadmap")
36 assert board_name_violation(
37 repo_name="knowtation",
38 handover_basename="OVERSEER-HANDOVER.md",
39 roadmap_basename="ROADMAP.md",
40 strict=True,
41 )
42 assert not board_name_violation(
43 repo_name="knowtation",
44 handover_basename="KNOWTATION-OVERSEER-HANDOVER.md",
45 roadmap_basename="KNOWTATION-ROADMAP.md",
46 strict=True,
47 )
48
49
50 def test_root_env_and_home_expansion(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
51 monkeypatch.setenv("WS_ROOT", str(tmp_path / "app"))
52 assert expand_root("${WS_ROOT}", environ=dict(**{"WS_ROOT": str(tmp_path / "app")})) == str(
53 tmp_path / "app"
54 )
55 assert expand_root("${MISSING:-~/fallback}", home=tmp_path).endswith("fallback")
56 assert expand_root("~/proj", home=tmp_path) == str(tmp_path / "proj")
57
58
59 def test_manifest_rejects_bad_cardinality_and_secrets() -> None:
60 base = {
61 "overseer_workspace_version": 1,
62 "id": "x",
63 "product_order_member": "a",
64 "members": [
65 {
66 "id": "a",
67 "role": "product_order",
68 "root": "/tmp/a",
69 "regime": "git-only",
70 "required": True,
71 "relay": False,
72 }
73 ],
74 "lanes": [{"id": "product", "primary": True}],
75 }
76 validate_manifest_dict(base, source_path=Path("/tmp/ws.yaml"), manifest_source="local_workspace")
77
78 bad = dict(base)
79 bad["members"] = list(base["members"]) + [
80 {
81 "id": "b",
82 "role": "product_order",
83 "root": "/tmp/b",
84 "regime": "git-only",
85 "required": True,
86 "relay": False,
87 }
88 ]
89 with pytest.raises(WorkspaceLoadError):
90 validate_manifest_dict(bad, source_path=Path("/tmp/ws.yaml"), manifest_source="local_workspace")
91
92 secret = dict(base)
93 secret["token"] = "supersecret"
94 with pytest.raises(WorkspaceLoadError):
95 validate_manifest_dict(secret, source_path=Path("/tmp/ws.yaml"), manifest_source="local_workspace")
96
97 identity = dict(base)
98 identity["X-User-Id"] = "abc"
99 with pytest.raises(WorkspaceLoadError):
100 validate_manifest_dict(identity, source_path=Path("/tmp/ws.yaml"), manifest_source="local_workspace")
101
102
103 def test_regime_null_only_when_optional() -> None:
104 raw = {
105 "overseer_workspace_version": 1,
106 "id": "x",
107 "product_order_member": "a",
108 "members": [
109 {
110 "id": "a",
111 "role": "product_order",
112 "root": "/tmp/a",
113 "regime": "git-only",
114 "required": True,
115 "relay": False,
116 },
117 {
118 "id": "brain",
119 "role": "edge",
120 "root": "",
121 "regime": None,
122 "required": False,
123 "relay": False,
124 },
125 ],
126 "lanes": [{"id": "product", "primary": True}],
127 }
128 validate_manifest_dict(raw, source_path=Path("/tmp/ws.yaml"), manifest_source="local_workspace")
129 raw["members"][1]["required"] = True
130 with pytest.raises(WorkspaceLoadError):
131 validate_manifest_dict(raw, source_path=Path("/tmp/ws.yaml"), manifest_source="local_workspace")
132
133
134 def test_marker_parse_and_tip_hash_lf_normalize() -> None:
135 fence = primary_fence(step="K13b", model="Auto", repo="/tmp/r")
136 digest = tip_hash_hex(fence)
137 assert digest == tip_hash_hex(fence.replace("\n", "\r\n"))
138 text = f"""
139 <!-- overseer:next role=primary lane=product status=live -->
140 ## NEXT SESSION — K13b (PRIMARY)
141
142 ```
143 {fence}```
144
145 <!-- overseer:next role=archived status=archived -->
146 ## ARCHIVED SESSION — old
147
148 <!-- overseer:next role=lane_tip lane=security status=live -->
149 ## LANE TIP — sec (LANE: security)
150
151 ```
152 Model: Thinking
153 Repo: x
154 Branch: y
155 Step: SEC
156 Authority: lane_tip
157 ```
158 """
159 blocks = extract_next_blocks(text)
160 roles = [b.role.value for b in blocks]
161 assert roles == ["primary", "archived", "lane_tip"]
162 assert blocks[0].step_id == "K13b"
163 assert blocks[0].model == "Auto"
164
165
166 def test_forbidden_legacy_archived_heading() -> None:
167 text = "## NEXT SESSION — archived L-SEAM Thinking\n"
168 hits = legacy_forbidden_archived_headings(text)
169 assert hits and "archived" in hits[0][1].lower()
170
171
172 def test_freshness_true_false(tmp_path: Path) -> None:
173 from tools.workspace.check_next import check_next
174 from tools.workspace.manifest import load_manifest_file
175
176 good = build_two_repo_constellation(tmp_path / "good", stale_relay=False)
177 manifest = load_manifest_file(good["manifest"], manifest_source="local_workspace")
178 ok = check_next(manifest)
179 assert ok.ok and ok.exit_code == 0
180
181 bad = build_two_repo_constellation(tmp_path / "bad", stale_relay=True)
182 stale = check_next(load_manifest_file(bad["manifest"], manifest_source="local_workspace"))
183 assert not stale.ok and stale.exit_code == 35
184 assert stale.state == "stale_relay"
185
186
187 def test_strict_board_names_default_true() -> None:
188 raw = {
189 "overseer_workspace_version": 1,
190 "id": "x",
191 "product_order_member": "a",
192 "members": [
193 {
194 "id": "a",
195 "role": "product_order",
196 "root": "/tmp/a",
197 "regime": "git-only",
198 "required": True,
199 "relay": False,
200 }
201 ],
202 "lanes": [{"id": "product", "primary": True}],
203 }
204 m = validate_manifest_dict(raw, source_path=Path("/tmp/ws.yaml"), manifest_source="local_workspace")
205 assert m.strict_markers is True
206 assert m.strict_board_names is True
207
208
209 def test_workspace_config_parse(tmp_path: Path) -> None:
210 from adapters.config import load_config
211
212 cfg_path = tmp_path / "config.yaml"
213 data = {
214 "overseer_config_version": 1,
215 "repo": {"name": "demo", "root_relative_docs": "docs"},
216 "vcs": {
217 "regime": "git-only",
218 "canonical": "git",
219 "git": {
220 "remote": "origin",
221 "main_branch": "main",
222 "mirror_branch": None,
223 "feature_branch_pattern": "feat/{slug}",
224 },
225 "muse": {"staging_remote": None, "main_branch": None},
226 },
227 "docs": {
228 "handover": "DEMO-OVERSEER-HANDOVER.md",
229 "roadmap": "DEMO-ROADMAP.md",
230 "coordination": None,
231 "standing_decisions": "DEMO-ROADMAP.md",
232 "handover_title": "Demo Overseer Handover",
233 "roadmap_title": "Demo Roadmap",
234 },
235 "thresholds": {"realign_max_commits": 50, "drift_warn_only": True},
236 "freeze_contract": {
237 "enabled": True,
238 "reviewer": {
239 "mode": "agent",
240 "model": "thinking-high",
241 "provider": "local",
242 "fallback": "human",
243 },
244 "human_escalation": ["security"],
245 },
246 "workspace": {"constellation_id": "demo-stack", "manifest": None, "product_order_root": None},
247 }
248 cfg_path.write_text(yaml.safe_dump(data), encoding="utf-8")
249 cfg = load_config(cfg_path)
250 assert cfg.workspace is not None
251 assert cfg.workspace.constellation_id == "demo-stack"
File History 1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago