gabriel / muse public
test_core_hooks.py python
105 lines 4.1 KB
Raw
sha256:4802c0281407b5de70ce1b34bbe1d1cd055533cbd4b8be35861a6a3eb545c1dc feat(#192): Phase 1 — .musehooks.toml parser + `muse hooks list` Sonnet 5 patch 6 days ago
1 """Tests for muse/core/hooks.py — .musehooks.toml parser and validator.
2
3 musehub#192 Phase 1: HK_01, HK_02.
4 """
5
6 import pathlib
7
8 import pytest
9
10 from muse.core.hooks import (
11 VALID_HOOK_POINTS,
12 HooksFile,
13 load_hooks,
14 )
15
16
17 def _write_hooks(tmp_path: pathlib.Path, content: str) -> None:
18 (tmp_path / ".musehooks.toml").write_text(content, encoding="utf-8")
19
20
21 # ---------------------------------------------------------------------------
22 # HK_01 — parsing
23 # ---------------------------------------------------------------------------
24
25
26 class TestLoadHooksParsing:
27 def test_missing_file_returns_empty_hooks_file(self, tmp_path: pathlib.Path) -> None:
28 result = load_hooks(tmp_path)
29 assert result == HooksFile(hooks={})
30
31 def test_empty_file_returns_empty_hooks_file(self, tmp_path: pathlib.Path) -> None:
32 _write_hooks(tmp_path, "")
33 assert load_hooks(tmp_path) == HooksFile(hooks={})
34
35 def test_comment_only_returns_empty(self, tmp_path: pathlib.Path) -> None:
36 _write_hooks(tmp_path, "# nothing here yet\n")
37 assert load_hooks(tmp_path) == HooksFile(hooks={})
38
39 def test_parses_single_pre_commit_command(self, tmp_path: pathlib.Path) -> None:
40 _write_hooks(
41 tmp_path,
42 '[pre-commit]\ncommands = ["muse agent-config status --json"]\n',
43 )
44 result = load_hooks(tmp_path)
45 assert result.hooks["pre-commit"] == ["muse agent-config status --json"]
46
47 def test_parses_multiple_commands_preserving_order(self, tmp_path: pathlib.Path) -> None:
48 _write_hooks(
49 tmp_path,
50 '[pre-commit]\ncommands = ["first --check", "second --check", "third --check"]\n',
51 )
52 result = load_hooks(tmp_path)
53 assert result.hooks["pre-commit"] == [
54 "first --check",
55 "second --check",
56 "third --check",
57 ]
58
59 def test_empty_commands_list_is_valid(self, tmp_path: pathlib.Path) -> None:
60 _write_hooks(tmp_path, "[pre-commit]\ncommands = []\n")
61 result = load_hooks(tmp_path)
62 assert result.hooks["pre-commit"] == []
63
64 def test_section_with_no_commands_key_is_valid(self, tmp_path: pathlib.Path) -> None:
65 _write_hooks(tmp_path, "[pre-commit]\n")
66 result = load_hooks(tmp_path)
67 assert result.hooks["pre-commit"] == []
68
69
70 # ---------------------------------------------------------------------------
71 # HK_02 — malformed / invalid content handling
72 # ---------------------------------------------------------------------------
73
74
75 class TestLoadHooksValidation:
76 def test_malformed_toml_raises_value_error(self, tmp_path: pathlib.Path) -> None:
77 _write_hooks(tmp_path, "[pre-commit\ncommands = [")
78 with pytest.raises(ValueError, match="TOML parse error"):
79 load_hooks(tmp_path)
80
81 def test_unknown_hook_point_raises_value_error(self, tmp_path: pathlib.Path) -> None:
82 _write_hooks(tmp_path, '[post-merge-explosion]\ncommands = ["echo hi"]\n')
83 with pytest.raises(ValueError, match="unknown hook point"):
84 load_hooks(tmp_path)
85
86 def test_non_string_command_raises_value_error(self, tmp_path: pathlib.Path) -> None:
87 _write_hooks(tmp_path, "[pre-commit]\ncommands = [123]\n")
88 with pytest.raises(ValueError, match="must be a string"):
89 load_hooks(tmp_path)
90
91 def test_commands_not_a_list_raises_value_error(self, tmp_path: pathlib.Path) -> None:
92 _write_hooks(tmp_path, '[pre-commit]\ncommands = "not-a-list"\n')
93 with pytest.raises(ValueError, match="must be a list"):
94 load_hooks(tmp_path)
95
96 def test_oversized_file_raises_value_error(self, tmp_path: pathlib.Path) -> None:
97 # 1 MiB cap, matching .museattributes's own hardening precedent.
98 huge = "# " + ("x" * (1024 * 1024 + 1))
99 _write_hooks(tmp_path, huge)
100 with pytest.raises(ValueError, match="too large"):
101 load_hooks(tmp_path)
102
103 def test_valid_hook_points_constant_matches_mvp_scope(self) -> None:
104 # MVP is explicitly scoped to pre-commit only (musehub#192 "Out of Scope").
105 assert VALID_HOOK_POINTS == frozenset({"pre-commit"})
File History 1 commit
sha256:4802c0281407b5de70ce1b34bbe1d1cd055533cbd4b8be35861a6a3eb545c1dc feat(#192): Phase 1 — .musehooks.toml parser + `muse hooks list` Sonnet 5 patch 6 days ago