gabriel / muse public

test_knowtation_defaults.py file-level

at sha256:6 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:0 test: validate staging push and MP workflow for aaronrene · · Jul 7, 2026
1 """Tests for Phase 1.6 — .museattributes defaults pack for the knowtation domain.
2
3 Test tiers covered
4 ------------------
5 1. Unit — defaults.museattributes file exists, is valid TOML, contains the three
6 required rules (frontmatter/union, inbox/ours, archive/ours), and has correct
7 meta.domain.
8 2. Integration — ``muse init --domain knowtation`` in a temp dir writes
9 .museattributes with the three rules and .museignore with the knowtation block.
10 3. Unit — _museattributes_template() and _museignore_template() produce correct
11 output for "knowtation" vs. other domains.
12 """
13
14 from __future__ import annotations
15
16 import json
17 import pathlib
18
19 import pytest
20
21 try:
22 import tomllib # Python 3.11+
23 except ImportError:
24 import tomli as tomllib # type: ignore[no-reattr]
25
26 from tests.cli_test_helper import CliRunner, InvokeResult
27
28 runner = CliRunner()
29
30 # Path to the shipped defaults file.
31 _DEFAULTS_FILE = (
32 pathlib.Path(__file__).parent.parent
33 / "muse"
34 / "plugins"
35 / "knowtation"
36 / "defaults.museattributes"
37 )
38
39
40 # ---------------------------------------------------------------------------
41 # Helpers
42 # ---------------------------------------------------------------------------
43
44
45 def _init(tmp_path: pathlib.Path, *args: str) -> InvokeResult:
46 from muse.cli.app import main as cli
47 return runner.invoke(cli, ["-C", str(tmp_path), "init", *args])
48
49
50 # ============================================================================
51 # TestDefaultsFile (unit — shipped defaults.museattributes)
52 # ============================================================================
53
54
55 class TestDefaultsFile:
56 def test_file_exists(self) -> None:
57 assert _DEFAULTS_FILE.exists(), f"defaults.museattributes not found at {_DEFAULTS_FILE}"
58
59 def test_file_is_valid_toml(self) -> None:
60 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
61 parsed = tomllib.loads(content)
62 assert isinstance(parsed, dict)
63
64 def test_meta_domain_is_knowtation(self) -> None:
65 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
66 parsed = tomllib.loads(content)
67 assert parsed.get("meta", {}).get("domain") == "knowtation"
68
69 def test_has_three_rules(self) -> None:
70 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
71 parsed = tomllib.loads(content)
72 rules = parsed.get("rules", [])
73 assert len(rules) == 3, f"Expected 3 rules, got {len(rules)}: {rules}"
74
75 def test_rule1_frontmatter_union(self) -> None:
76 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
77 parsed = tomllib.loads(content)
78 rules = parsed["rules"]
79 rule1 = rules[0]
80 assert rule1["dimension"] == "frontmatter"
81 assert rule1["strategy"] == "union"
82
83 def test_rule2_inbox_ours(self) -> None:
84 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
85 parsed = tomllib.loads(content)
86 rules = parsed["rules"]
87 inbox_rules = [r for r in rules if "inbox" in r.get("path", "")]
88 assert len(inbox_rules) >= 1
89 assert inbox_rules[0]["strategy"] == "ours"
90
91 def test_rule3_archive_ours(self) -> None:
92 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
93 parsed = tomllib.loads(content)
94 rules = parsed["rules"]
95 archive_rules = [r for r in rules if "archive" in r.get("path", "")]
96 assert len(archive_rules) >= 1
97 assert archive_rules[0]["strategy"] == "ours"
98
99 def test_all_rules_have_required_fields(self) -> None:
100 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
101 parsed = tomllib.loads(content)
102 for i, rule in enumerate(parsed["rules"]):
103 assert "path" in rule, f"Rule {i} missing 'path'"
104 assert "dimension" in rule, f"Rule {i} missing 'dimension'"
105 assert "strategy" in rule, f"Rule {i} missing 'strategy'"
106
107 def test_strategies_are_valid(self) -> None:
108 valid_strategies = {"auto", "ours", "theirs", "union", "base", "manual"}
109 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
110 parsed = tomllib.loads(content)
111 for rule in parsed["rules"]:
112 assert rule["strategy"] in valid_strategies, (
113 f"Unknown strategy '{rule['strategy']}'"
114 )
115
116 def test_priorities_are_integers_when_present(self) -> None:
117 content = _DEFAULTS_FILE.read_text(encoding="utf-8")
118 parsed = tomllib.loads(content)
119 for rule in parsed["rules"]:
120 if "priority" in rule:
121 assert isinstance(rule["priority"], int)
122
123 def test_file_is_utf8(self) -> None:
124 # Must be decodable as UTF-8 without error.
125 _DEFAULTS_FILE.read_text(encoding="utf-8")
126
127
128 # ============================================================================
129 # TestMuseattributesTemplate (unit — init.py template function)
130 # ============================================================================
131
132
133 class TestMuseattributesTemplate:
134 def _template(self, domain: str) -> str:
135 from muse.cli.commands.init import _museattributes_template
136 return _museattributes_template(domain)
137
138 def test_knowtation_contains_meta_domain(self) -> None:
139 t = self._template("knowtation")
140 assert 'domain = "knowtation"' in t
141
142 def test_knowtation_contains_frontmatter_union_rule(self) -> None:
143 t = self._template("knowtation")
144 assert 'strategy = "union"' in t
145 assert 'dimension = "frontmatter"' in t
146
147 def test_knowtation_contains_inbox_ours_rule(self) -> None:
148 t = self._template("knowtation")
149 assert "inbox" in t
150 assert 'strategy = "ours"' in t
151
152 def test_knowtation_contains_archive_ours_rule(self) -> None:
153 t = self._template("knowtation")
154 assert "archive" in t
155
156 def test_knowtation_template_is_valid_toml(self) -> None:
157 t = self._template("knowtation")
158 parsed = tomllib.loads(t)
159 assert parsed["meta"]["domain"] == "knowtation"
160 assert len(parsed.get("rules", [])) == 3
161
162 def test_code_domain_fallback_template(self) -> None:
163 t = self._template("code")
164 assert 'domain = "code"' in t
165 # Should NOT have knowtation-specific rules.
166 assert "inbox" not in t
167
168 def test_unknown_domain_fallback(self) -> None:
169 t = self._template("custom")
170 assert 'domain = "custom"' in t
171
172 def test_template_starts_with_comment(self) -> None:
173 t = self._template("knowtation")
174 assert t.startswith("#")
175
176
177 # ============================================================================
178 # TestMuseignoreTemplate (unit — knowtation block)
179 # ============================================================================
180
181
182 class TestMuseignoreTemplate:
183 def _template(self, domain: str) -> str:
184 from muse.cli.commands.init import _museignore_template
185 return _museignore_template(domain)
186
187 def test_knowtation_has_domain_block(self) -> None:
188 t = self._template("knowtation")
189 assert "[domain.knowtation]" in t
190
191 def test_knowtation_excludes_data_dir(self) -> None:
192 t = self._template("knowtation")
193 assert "data/" in t
194
195 def test_knowtation_excludes_local_config(self) -> None:
196 t = self._template("knowtation")
197 assert "config/local.yaml" in t
198
199 def test_knowtation_excludes_node_modules(self) -> None:
200 t = self._template("knowtation")
201 assert "node_modules/" in t
202
203 def test_global_block_always_present(self) -> None:
204 t = self._template("knowtation")
205 assert "[global]" in t
206
207
208 # ============================================================================
209 # TestInitIntegration (integration — muse init --domain knowtation)
210 # ============================================================================
211
212
213 class TestInitIntegration:
214 def test_init_knowtation_exits_zero(self, tmp_path: pathlib.Path) -> None:
215 result = _init(tmp_path, "--domain", "knowtation")
216 assert result.exit_code == 0, result.output
217
218 def test_init_knowtation_creates_museattributes(self, tmp_path: pathlib.Path) -> None:
219 _init(tmp_path, "--domain", "knowtation")
220 attrs = tmp_path / ".museattributes"
221 assert attrs.exists(), ".museattributes not created"
222
223 def test_init_knowtation_museattributes_valid_toml(self, tmp_path: pathlib.Path) -> None:
224 _init(tmp_path, "--domain", "knowtation")
225 content = (tmp_path / ".museattributes").read_text(encoding="utf-8")
226 parsed = tomllib.loads(content)
227 assert isinstance(parsed, dict)
228
229 def test_init_knowtation_museattributes_has_three_rules(
230 self, tmp_path: pathlib.Path
231 ) -> None:
232 _init(tmp_path, "--domain", "knowtation")
233 content = (tmp_path / ".museattributes").read_text(encoding="utf-8")
234 parsed = tomllib.loads(content)
235 rules = parsed.get("rules", [])
236 assert len(rules) == 3, f"Expected 3 rules in generated .museattributes, got {len(rules)}"
237
238 def test_init_knowtation_museattributes_meta_domain(self, tmp_path: pathlib.Path) -> None:
239 _init(tmp_path, "--domain", "knowtation")
240 content = (tmp_path / ".museattributes").read_text(encoding="utf-8")
241 parsed = tomllib.loads(content)
242 assert parsed["meta"]["domain"] == "knowtation"
243
244 def test_init_knowtation_creates_museignore(self, tmp_path: pathlib.Path) -> None:
245 _init(tmp_path, "--domain", "knowtation")
246 assert (tmp_path / ".museignore").exists()
247
248 def test_init_knowtation_museignore_has_data_pattern(
249 self, tmp_path: pathlib.Path
250 ) -> None:
251 _init(tmp_path, "--domain", "knowtation")
252 content = (tmp_path / ".museignore").read_text(encoding="utf-8")
253 assert "data/" in content
254
255 def test_init_knowtation_repo_json_domain(self, tmp_path: pathlib.Path) -> None:
256 _init(tmp_path, "--domain", "knowtation")
257 repo_json = tmp_path / ".muse" / "repo.json"
258 data = json.loads(repo_json.read_text())
259 assert data["domain"] == "knowtation"
260
261 def test_init_code_domain_no_knowtation_rules(self, tmp_path: pathlib.Path) -> None:
262 """code domain init must not include knowtation-specific rules."""
263 _init(tmp_path, "--domain", "code")
264 content = (tmp_path / ".museattributes").read_text(encoding="utf-8")
265 assert "inbox" not in content
266 assert "archive" not in content
267
268 def test_init_json_output_includes_domain(self, tmp_path: pathlib.Path) -> None:
269 result = _init(tmp_path, "--domain", "knowtation", "--json")
270 assert result.exit_code == 0
271 data = json.loads(result.stdout)
272 assert data["domain"] == "knowtation"