"""Tests for Phase 1.6 — .museattributes defaults pack for the knowtation domain. Test tiers covered ------------------ 1. Unit — defaults.museattributes file exists, is valid TOML, contains the three required rules (frontmatter/union, inbox/ours, archive/ours), and has correct meta.domain. 2. Integration — ``muse init --domain knowtation`` in a temp dir writes .museattributes with the three rules and .museignore with the knowtation block. 3. Unit — _museattributes_template() and _museignore_template() produce correct output for "knowtation" vs. other domains. """ from __future__ import annotations import json import pathlib import pytest try: import tomllib # Python 3.11+ except ImportError: import tomli as tomllib # type: ignore[no-reattr] from tests.cli_test_helper import CliRunner, InvokeResult runner = CliRunner() # Path to the shipped defaults file. _DEFAULTS_FILE = ( pathlib.Path(__file__).parent.parent / "muse" / "plugins" / "knowtation" / "defaults.museattributes" ) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _init(tmp_path: pathlib.Path, *args: str) -> InvokeResult: from muse.cli.app import main as cli return runner.invoke(cli, ["-C", str(tmp_path), "init", *args]) # ============================================================================ # TestDefaultsFile (unit — shipped defaults.museattributes) # ============================================================================ class TestDefaultsFile: def test_file_exists(self) -> None: assert _DEFAULTS_FILE.exists(), f"defaults.museattributes not found at {_DEFAULTS_FILE}" def test_file_is_valid_toml(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) assert isinstance(parsed, dict) def test_meta_domain_is_knowtation(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) assert parsed.get("meta", {}).get("domain") == "knowtation" def test_has_three_rules(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) rules = parsed.get("rules", []) assert len(rules) == 3, f"Expected 3 rules, got {len(rules)}: {rules}" def test_rule1_frontmatter_union(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) rules = parsed["rules"] rule1 = rules[0] assert rule1["dimension"] == "frontmatter" assert rule1["strategy"] == "union" def test_rule2_inbox_ours(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) rules = parsed["rules"] inbox_rules = [r for r in rules if "inbox" in r.get("path", "")] assert len(inbox_rules) >= 1 assert inbox_rules[0]["strategy"] == "ours" def test_rule3_archive_ours(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) rules = parsed["rules"] archive_rules = [r for r in rules if "archive" in r.get("path", "")] assert len(archive_rules) >= 1 assert archive_rules[0]["strategy"] == "ours" def test_all_rules_have_required_fields(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) for i, rule in enumerate(parsed["rules"]): assert "path" in rule, f"Rule {i} missing 'path'" assert "dimension" in rule, f"Rule {i} missing 'dimension'" assert "strategy" in rule, f"Rule {i} missing 'strategy'" def test_strategies_are_valid(self) -> None: valid_strategies = {"auto", "ours", "theirs", "union", "base", "manual"} content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) for rule in parsed["rules"]: assert rule["strategy"] in valid_strategies, ( f"Unknown strategy '{rule['strategy']}'" ) def test_priorities_are_integers_when_present(self) -> None: content = _DEFAULTS_FILE.read_text(encoding="utf-8") parsed = tomllib.loads(content) for rule in parsed["rules"]: if "priority" in rule: assert isinstance(rule["priority"], int) def test_file_is_utf8(self) -> None: # Must be decodable as UTF-8 without error. _DEFAULTS_FILE.read_text(encoding="utf-8") # ============================================================================ # TestMuseattributesTemplate (unit — init.py template function) # ============================================================================ class TestMuseattributesTemplate: def _template(self, domain: str) -> str: from muse.cli.commands.init import _museattributes_template return _museattributes_template(domain) def test_knowtation_contains_meta_domain(self) -> None: t = self._template("knowtation") assert 'domain = "knowtation"' in t def test_knowtation_contains_frontmatter_union_rule(self) -> None: t = self._template("knowtation") assert 'strategy = "union"' in t assert 'dimension = "frontmatter"' in t def test_knowtation_contains_inbox_ours_rule(self) -> None: t = self._template("knowtation") assert "inbox" in t assert 'strategy = "ours"' in t def test_knowtation_contains_archive_ours_rule(self) -> None: t = self._template("knowtation") assert "archive" in t def test_knowtation_template_is_valid_toml(self) -> None: t = self._template("knowtation") parsed = tomllib.loads(t) assert parsed["meta"]["domain"] == "knowtation" assert len(parsed.get("rules", [])) == 3 def test_code_domain_fallback_template(self) -> None: t = self._template("code") assert 'domain = "code"' in t # Should NOT have knowtation-specific rules. assert "inbox" not in t def test_unknown_domain_fallback(self) -> None: t = self._template("custom") assert 'domain = "custom"' in t def test_template_starts_with_comment(self) -> None: t = self._template("knowtation") assert t.startswith("#") # ============================================================================ # TestMuseignoreTemplate (unit — knowtation block) # ============================================================================ class TestMuseignoreTemplate: def _template(self, domain: str) -> str: from muse.cli.commands.init import _museignore_template return _museignore_template(domain) def test_knowtation_has_domain_block(self) -> None: t = self._template("knowtation") assert "[domain.knowtation]" in t def test_knowtation_excludes_data_dir(self) -> None: t = self._template("knowtation") assert "data/" in t def test_knowtation_excludes_local_config(self) -> None: t = self._template("knowtation") assert "config/local.yaml" in t def test_knowtation_excludes_node_modules(self) -> None: t = self._template("knowtation") assert "node_modules/" in t def test_global_block_always_present(self) -> None: t = self._template("knowtation") assert "[global]" in t # ============================================================================ # TestInitIntegration (integration — muse init --domain knowtation) # ============================================================================ class TestInitIntegration: def test_init_knowtation_exits_zero(self, tmp_path: pathlib.Path) -> None: result = _init(tmp_path, "--domain", "knowtation") assert result.exit_code == 0, result.output def test_init_knowtation_creates_museattributes(self, tmp_path: pathlib.Path) -> None: _init(tmp_path, "--domain", "knowtation") attrs = tmp_path / ".museattributes" assert attrs.exists(), ".museattributes not created" def test_init_knowtation_museattributes_valid_toml(self, tmp_path: pathlib.Path) -> None: _init(tmp_path, "--domain", "knowtation") content = (tmp_path / ".museattributes").read_text(encoding="utf-8") parsed = tomllib.loads(content) assert isinstance(parsed, dict) def test_init_knowtation_museattributes_has_three_rules( self, tmp_path: pathlib.Path ) -> None: _init(tmp_path, "--domain", "knowtation") content = (tmp_path / ".museattributes").read_text(encoding="utf-8") parsed = tomllib.loads(content) rules = parsed.get("rules", []) assert len(rules) == 3, f"Expected 3 rules in generated .museattributes, got {len(rules)}" def test_init_knowtation_museattributes_meta_domain(self, tmp_path: pathlib.Path) -> None: _init(tmp_path, "--domain", "knowtation") content = (tmp_path / ".museattributes").read_text(encoding="utf-8") parsed = tomllib.loads(content) assert parsed["meta"]["domain"] == "knowtation" def test_init_knowtation_creates_museignore(self, tmp_path: pathlib.Path) -> None: _init(tmp_path, "--domain", "knowtation") assert (tmp_path / ".museignore").exists() def test_init_knowtation_museignore_has_data_pattern( self, tmp_path: pathlib.Path ) -> None: _init(tmp_path, "--domain", "knowtation") content = (tmp_path / ".museignore").read_text(encoding="utf-8") assert "data/" in content def test_init_knowtation_repo_json_domain(self, tmp_path: pathlib.Path) -> None: _init(tmp_path, "--domain", "knowtation") repo_json = tmp_path / ".muse" / "repo.json" data = json.loads(repo_json.read_text()) assert data["domain"] == "knowtation" def test_init_code_domain_no_knowtation_rules(self, tmp_path: pathlib.Path) -> None: """code domain init must not include knowtation-specific rules.""" _init(tmp_path, "--domain", "code") content = (tmp_path / ".museattributes").read_text(encoding="utf-8") assert "inbox" not in content assert "archive" not in content def test_init_json_output_includes_domain(self, tmp_path: pathlib.Path) -> None: result = _init(tmp_path, "--domain", "knowtation", "--json") assert result.exit_code == 0 data = json.loads(result.stdout) assert data["domain"] == "knowtation"