gabriel / muse public

test_commit_dir_changes.py file-level

at sha256:8 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:b adding issues docs to bust staging mpack prebuild cache. · gabriel · Jun 20, 2026
1 """Directory changes in ``muse commit`` and ``muse read`` output.
2
3 When a commit adds or removes empty directories, those changes must be
4 visible in both the commit summary line and in ``muse read --json``,
5 alongside the existing ``files_added`` / ``files_removed`` fields.
6
7 Coverage matrix
8 ---------------
9 DC Directory changes in commit / read output
10 DC1 commit adding an empty dir → dirs_added: ["foobar/"] in muse read
11 DC2 commit removing a committed dir → dirs_removed: ["emptydir/"] in muse read
12 DC3 total_changes in muse read includes dir changes
13 DC4 commit summary line mentions directory additions ("1 dir added")
14 DC5 commit summary line mentions directory removals ("1 dir removed")
15 DC6 commit with only file changes → dirs_added: [] and dirs_removed: []
16 DC7 dirs_added and dirs_removed always present in muse read --json
17 """
18
19 from __future__ import annotations
20
21 import json
22 import pathlib
23 from collections.abc import Mapping
24
25 import pytest
26
27 from tests.cli_test_helper import CliRunner
28
29 cli = None
30 runner = CliRunner()
31
32
33 # ---------------------------------------------------------------------------
34 # Helpers
35 # ---------------------------------------------------------------------------
36
37
38 def _env(root: pathlib.Path) -> Mapping[str, str]:
39 return {"MUSE_REPO_ROOT": str(root)}
40
41
42 def _run(root: pathlib.Path, *args: str) -> str:
43 result = runner.invoke(cli, list(args), env=_env(root))
44 assert result.exit_code == 0, f"{args} failed:\n{result.output}\n{result.stderr}"
45 return result.output.strip()
46
47
48 def _read(root: pathlib.Path) -> Mapping[str, object]:
49 return json.loads(_run(root, "read", "--json"))
50
51
52 # ---------------------------------------------------------------------------
53 # Fixtures
54 # ---------------------------------------------------------------------------
55
56
57 @pytest.fixture()
58 def base_repo(tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> pathlib.Path:
59 """Code repo with one committed file, clean working tree."""
60 monkeypatch.chdir(tmp_path)
61 _run(tmp_path, "init", "--domain", "code")
62 (tmp_path / "main.py").write_text("x = 1\n")
63 _run(tmp_path, "code", "add", ".")
64 _run(tmp_path, "commit", "-m", "initial")
65 return tmp_path
66
67
68 @pytest.fixture()
69 def repo_with_committed_dir(
70 tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
71 ) -> pathlib.Path:
72 """Code repo with a committed empty directory ``emptydir``."""
73 monkeypatch.chdir(tmp_path)
74 _run(tmp_path, "init", "--domain", "code")
75 (tmp_path / "main.py").write_text("x = 1\n")
76 (tmp_path / "emptydir").mkdir()
77 _run(tmp_path, "code", "add", ".")
78 _run(tmp_path, "commit", "-m", "initial with emptydir")
79 return tmp_path
80
81
82 # ---------------------------------------------------------------------------
83 # DC Directory changes in commit / read
84 # ---------------------------------------------------------------------------
85
86
87 class TestCommitDirChanges:
88 def test_DC1_dirs_added_in_read_json(self, base_repo: pathlib.Path) -> None:
89 """DC1: commit adding an empty dir → dirs_added: ['foobar/'] in muse read."""
90 root = base_repo
91 (root / "foobar").mkdir()
92 _run(root, "code", "add", "foobar")
93 _run(root, "commit", "-m", "add foobar dir")
94
95 data = _read(root)
96
97 assert "dirs_added" in data, "muse read --json must have a 'dirs_added' key"
98 assert "foobar/" in data["dirs_added"], (
99 "newly committed empty dir must appear in dirs_added with trailing slash"
100 )
101
102 def test_DC2_dirs_removed_in_read_json(
103 self, repo_with_committed_dir: pathlib.Path
104 ) -> None:
105 """DC2: commit removing a committed dir → dirs_removed: ['emptydir/']."""
106 root = repo_with_committed_dir
107 (root / "emptydir").rmdir()
108 _run(root, "code", "add", ".")
109 _run(root, "commit", "-m", "remove emptydir")
110
111 data = _read(root)
112
113 assert "dirs_removed" in data, "muse read --json must have a 'dirs_removed' key"
114 assert "emptydir/" in data["dirs_removed"], (
115 "removed committed dir must appear in dirs_removed with trailing slash"
116 )
117
118 def test_DC3_total_changes_includes_dirs(self, base_repo: pathlib.Path) -> None:
119 """DC3: total_changes in muse read includes directory additions."""
120 root = base_repo
121 (root / "foobar").mkdir()
122 _run(root, "code", "add", "foobar")
123 _run(root, "commit", "-m", "add foobar dir")
124
125 data = _read(root)
126
127 assert data["total_changes"] == 1, (
128 "adding one empty dir must count as 1 total change"
129 )
130
131 def test_DC4_commit_summary_mentions_dir_added(
132 self, base_repo: pathlib.Path
133 ) -> None:
134 """DC4: commit summary line mentions directory additions."""
135 root = base_repo
136 (root / "foobar").mkdir()
137 _run(root, "code", "add", "foobar")
138
139 result = runner.invoke(
140 cli, ["commit", "-m", "add foobar dir"], env=_env(root)
141 )
142 assert result.exit_code == 0
143 assert "dir" in result.output.lower(), (
144 f"commit output must mention directory change; got:\n{result.output}"
145 )
146
147 def test_DC5_commit_summary_mentions_dir_removed(
148 self, repo_with_committed_dir: pathlib.Path
149 ) -> None:
150 """DC5: commit summary line mentions directory removals."""
151 root = repo_with_committed_dir
152 (root / "emptydir").rmdir()
153 _run(root, "code", "add", ".")
154
155 result = runner.invoke(
156 cli, ["commit", "-m", "remove emptydir"], env=_env(root)
157 )
158 assert result.exit_code == 0
159 assert "dir" in result.output.lower(), (
160 f"commit output must mention directory removal; got:\n{result.output}"
161 )
162
163 def test_DC6_no_dir_changes_gives_empty_lists(
164 self, base_repo: pathlib.Path
165 ) -> None:
166 """DC6: commit with only file changes → dirs_added: [] and dirs_removed: []."""
167 root = base_repo
168 (root / "other.py").write_text("y = 2\n")
169 _run(root, "code", "add", ".")
170 _run(root, "commit", "-m", "add other.py")
171
172 data = _read(root)
173
174 assert data["dirs_added"] == []
175 assert data["dirs_removed"] == []
176
177 def test_DC7_dirs_keys_always_present(self, base_repo: pathlib.Path) -> None:
178 """DC7: dirs_added and dirs_removed always present in muse read --json."""
179 data = _read(base_repo)
180
181 assert "dirs_added" in data
182 assert "dirs_removed" in data
183 assert isinstance(data["dirs_added"], list)
184 assert isinstance(data["dirs_removed"], list)