gabriel / muse public

test_code_add_supercharge.py file-level

at sha256:5 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:4 Merge branch 'dev' into main · gabriel · Jun 17, 2026
1 """Supercharge tests for ``muse code add`` / ``muse code reset`` — agent-usability gaps.
2
3 Coverage matrix
4 ---------------
5 - --json / -j: alias works identically to --format json for both add and reset
6 - exit_code: every JSON output path includes it (success and error branches)
7 - duration_ms: every JSON output path includes it; non-negative float
8 - TypedDicts: _CodeAddJson, _CodeResetJson annotations exist and carry new fields
9 - Docstrings: run_add, run_reset docstrings mention exit_code and duration_ms
10 - Error JSON: error branches include exit_code and duration_ms
11 - ANSI: file paths in JSON output never contain escape sequences
12 - Performance: duration_ms stays < 1000 ms for normal operations
13 """
14
15 from __future__ import annotations
16 from collections.abc import Mapping
17
18 import json
19 import pathlib
20
21 import pytest
22
23 from tests.cli_test_helper import CliRunner
24
25 runner = CliRunner()
26
27
28 # ---------------------------------------------------------------------------
29 # Helpers
30 # ---------------------------------------------------------------------------
31
32
33 def _env(root: pathlib.Path) -> Mapping[str, str]:
34 return {"MUSE_REPO_ROOT": str(root)}
35
36
37 def _run(root: pathlib.Path, *args: str) -> "InvokeResult":
38 return runner.invoke(None, list(args), env=_env(root))
39
40
41 @pytest.fixture()
42 def repo(tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> pathlib.Path:
43 """Fresh code-domain repo with one committed file (main.py = 'x = 1')."""
44 monkeypatch.chdir(tmp_path)
45 r = runner.invoke(None, ["init", "--domain", "code"], env=_env(tmp_path))
46 assert r.exit_code == 0, r.output
47 (tmp_path / "main.py").write_text("x = 1\n")
48 r2 = runner.invoke(None, ["commit", "--allow-empty", "-m", "init"], env=_env(tmp_path))
49 assert r2.exit_code == 0, r2.output
50 return tmp_path
51
52
53 # ---------------------------------------------------------------------------
54 # --json / -j alias
55 # ---------------------------------------------------------------------------
56
57
58 class TestJsonAlias:
59 """--json and -j are accepted on both add and reset, mirror --format json."""
60
61 def test_add_json_flag_accepted(self, repo: pathlib.Path) -> None:
62 (repo / "main.py").write_text("x = 2\n")
63 r = _run(repo, "code", "add", "--json", "main.py")
64 assert r.exit_code == 0
65 json.loads(r.output.strip()) # must be valid JSON
66
67 def test_add_j_flag_accepted(self, repo: pathlib.Path) -> None:
68 (repo / "main.py").write_text("x = 3\n")
69 r = _run(repo, "code", "add", "-j", "main.py")
70 assert r.exit_code == 0
71 json.loads(r.output.strip())
72
73 def test_reset_json_flag_accepted(self, repo: pathlib.Path) -> None:
74 (repo / "main.py").write_text("x = 4\n")
75 _run(repo, "code", "add", "main.py")
76 r = _run(repo, "code", "reset", "--json", "main.py")
77 assert r.exit_code == 0
78 json.loads(r.output.strip())
79
80 def test_reset_j_flag_accepted(self, repo: pathlib.Path) -> None:
81 (repo / "main.py").write_text("x = 5\n")
82 _run(repo, "code", "add", "main.py")
83 r = _run(repo, "code", "reset", "-j", "main.py")
84 assert r.exit_code == 0
85 json.loads(r.output.strip())
86
87 def test_add_json_and_j_produce_same_keys(self, repo: pathlib.Path) -> None:
88 (repo / "main.py").write_text("x = 10\n")
89 r1 = _run(repo, "code", "add", "--json", "main.py")
90 (repo / "main.py").write_text("x = 11\n")
91 r2 = _run(repo, "code", "add", "-j", "main.py")
92 d1 = json.loads(r1.output.strip())
93 d2 = json.loads(r2.output.strip())
94 d1.pop("duration_ms", None); d1.pop("timestamp", None)
95 d2.pop("duration_ms", None); d2.pop("timestamp", None)
96 assert set(d1.keys()) == set(d2.keys())
97
98 def test_add_j_alias_produces_same_keys(self, repo: pathlib.Path) -> None:
99 (repo / "main.py").write_text("x = 20\n")
100 r1 = _run(repo, "code", "add", "--json", "main.py")
101 (repo / "main.py").write_text("x = 21\n")
102 r2 = _run(repo, "code", "add", "-j", "main.py")
103 d1 = json.loads(r1.output.strip())
104 d2 = json.loads(r2.output.strip())
105 d1.pop("duration_ms", None); d1.pop("timestamp", None)
106 d2.pop("duration_ms", None); d2.pop("timestamp", None)
107 assert set(d1.keys()) == set(d2.keys())
108
109 def test_reset_json_and_j_produce_same_keys(self, repo: pathlib.Path) -> None:
110 (repo / "main.py").write_text("x = 30\n")
111 _run(repo, "code", "add", "main.py")
112 r1 = _run(repo, "code", "reset", "--json", "main.py")
113 (repo / "main.py").write_text("x = 31\n")
114 _run(repo, "code", "add", "main.py")
115 r2 = _run(repo, "code", "reset", "-j", "main.py")
116 d1 = json.loads(r1.output.strip())
117 d2 = json.loads(r2.output.strip())
118 d1.pop("duration_ms", None); d1.pop("timestamp", None)
119 d2.pop("duration_ms", None); d2.pop("timestamp", None)
120 assert set(d1.keys()) == set(d2.keys())
121
122 def test_add_dot_with_json_flag(self, repo: pathlib.Path) -> None:
123 (repo / "main.py").write_text("x = 99\n")
124 r = _run(repo, "code", "add", "--json", ".")
125 assert r.exit_code == 0
126 d = json.loads(r.output.strip())
127 assert "staged" in d
128
129 def test_add_update_flag_with_json(self, repo: pathlib.Path) -> None:
130 (repo / "main.py").write_text("x = 77\n")
131 r = _run(repo, "code", "add", "-u", "--json")
132 assert r.exit_code == 0
133 d = json.loads(r.output.strip())
134 assert "staged" in d
135
136 def test_add_all_flag_with_json(self, repo: pathlib.Path) -> None:
137 (repo / "new.py").write_text("y = 0\n")
138 r = _run(repo, "code", "add", "-A", "--json")
139 assert r.exit_code == 0
140 d = json.loads(r.output.strip())
141 assert "staged" in d
142
143
144 # ---------------------------------------------------------------------------
145 # duration_ms
146 # ---------------------------------------------------------------------------
147
148
149 class TestDurationMs:
150 """Every JSON output path from add and reset must include duration_ms."""
151
152 def test_add_success_has_duration_ms(self, repo: pathlib.Path) -> None:
153 (repo / "main.py").write_text("x = 2\n")
154 r = _run(repo, "code", "add", "--json", "main.py")
155 assert "duration_ms" in json.loads(r.output.strip())
156
157 def test_add_nothing_to_stage_has_duration_ms(self, repo: pathlib.Path) -> None:
158 r = _run(repo, "code", "add", "--json", ".")
159 assert "duration_ms" in json.loads(r.output.strip())
160
161 def test_add_dry_run_has_duration_ms(self, repo: pathlib.Path) -> None:
162 (repo / "main.py").write_text("x = 5\n")
163 r = _run(repo, "code", "add", "--dry-run", "--json", "main.py")
164 assert "duration_ms" in json.loads(r.output.strip())
165
166 def test_add_all_has_duration_ms(self, repo: pathlib.Path) -> None:
167 (repo / "new.py").write_text("y = 0\n")
168 r = _run(repo, "code", "add", "-A", "--json")
169 assert "duration_ms" in json.loads(r.output.strip())
170
171 def test_add_update_has_duration_ms(self, repo: pathlib.Path) -> None:
172 (repo / "main.py").write_text("x = 6\n")
173 r = _run(repo, "code", "add", "-u", "--json")
174 assert "duration_ms" in json.loads(r.output.strip())
175
176 def test_reset_success_has_duration_ms(self, repo: pathlib.Path) -> None:
177 (repo / "main.py").write_text("x = 7\n")
178 _run(repo, "code", "add", "main.py")
179 r = _run(repo, "code", "reset", "--json")
180 assert "duration_ms" in json.loads(r.output.strip())
181
182 def test_reset_nothing_staged_has_duration_ms(self, repo: pathlib.Path) -> None:
183 r = _run(repo, "code", "reset", "--json")
184 assert "duration_ms" in json.loads(r.output.strip())
185
186 def test_reset_dry_run_has_duration_ms(self, repo: pathlib.Path) -> None:
187 (repo / "main.py").write_text("x = 8\n")
188 _run(repo, "code", "add", "main.py")
189 r = _run(repo, "code", "reset", "--dry-run", "--json")
190 assert "duration_ms" in json.loads(r.output.strip())
191
192 def test_duration_ms_is_non_negative(self, repo: pathlib.Path) -> None:
193 (repo / "main.py").write_text("x = 9\n")
194 r = _run(repo, "code", "add", "--json", "main.py")
195 assert json.loads(r.output.strip())["duration_ms"] >= 0
196
197 def test_duration_ms_is_numeric(self, repo: pathlib.Path) -> None:
198 (repo / "main.py").write_text("x = 10\n")
199 r = _run(repo, "code", "add", "--json", "main.py")
200 val = json.loads(r.output.strip())["duration_ms"]
201 assert isinstance(val, (int, float))
202
203
204 # ---------------------------------------------------------------------------
205 # exit_code
206 # ---------------------------------------------------------------------------
207
208
209 class TestExitCode:
210 """Every JSON output path must include exit_code mirroring process exit."""
211
212 def test_add_success_has_exit_code(self, repo: pathlib.Path) -> None:
213 (repo / "main.py").write_text("x = 2\n")
214 r = _run(repo, "code", "add", "--json", "main.py")
215 assert "exit_code" in json.loads(r.output.strip())
216
217 def test_add_nothing_has_exit_code(self, repo: pathlib.Path) -> None:
218 r = _run(repo, "code", "add", "--json", ".")
219 assert "exit_code" in json.loads(r.output.strip())
220
221 def test_add_dry_run_has_exit_code(self, repo: pathlib.Path) -> None:
222 (repo / "main.py").write_text("x = 5\n")
223 r = _run(repo, "code", "add", "--dry-run", "--json", "main.py")
224 assert "exit_code" in json.loads(r.output.strip())
225
226 def test_reset_success_has_exit_code(self, repo: pathlib.Path) -> None:
227 (repo / "main.py").write_text("x = 3\n")
228 _run(repo, "code", "add", "main.py")
229 r = _run(repo, "code", "reset", "--json")
230 assert "exit_code" in json.loads(r.output.strip())
231
232 def test_reset_nothing_has_exit_code(self, repo: pathlib.Path) -> None:
233 r = _run(repo, "code", "reset", "--json")
234 assert "exit_code" in json.loads(r.output.strip())
235
236 def test_add_exit_code_zero_on_success(self, repo: pathlib.Path) -> None:
237 (repo / "main.py").write_text("x = 4\n")
238 r = _run(repo, "code", "add", "--json", "main.py")
239 assert json.loads(r.output.strip())["exit_code"] == 0
240
241 def test_reset_exit_code_zero_on_success(self, repo: pathlib.Path) -> None:
242 r = _run(repo, "code", "reset", "--json")
243 assert json.loads(r.output.strip())["exit_code"] == 0
244
245 def test_add_exit_code_mirrors_process_exit(self, repo: pathlib.Path) -> None:
246 (repo / "main.py").write_text("x = 5\n")
247 r = _run(repo, "code", "add", "--json", "main.py")
248 d = json.loads(r.output.strip())
249 assert d["exit_code"] == r.exit_code
250
251 def test_reset_exit_code_mirrors_process_exit(self, repo: pathlib.Path) -> None:
252 (repo / "main.py").write_text("x = 6\n")
253 _run(repo, "code", "add", "main.py")
254 r = _run(repo, "code", "reset", "--json", "main.py")
255 d = json.loads(r.output.strip())
256 assert d["exit_code"] == r.exit_code
257
258 def test_exit_code_is_int(self, repo: pathlib.Path) -> None:
259 r = _run(repo, "code", "reset", "--json")
260 assert isinstance(json.loads(r.output.strip())["exit_code"], int)
261
262 def test_add_all_exit_code_zero(self, repo: pathlib.Path) -> None:
263 (repo / "new.py").write_text("n = 0\n")
264 r = _run(repo, "code", "add", "-A", "--json")
265 assert json.loads(r.output.strip())["exit_code"] == 0
266
267
268 # ---------------------------------------------------------------------------
269 # TypedDicts
270 # ---------------------------------------------------------------------------
271
272
273 class TestTypedDicts:
274 """_CodeAddJson and _CodeResetJson TypedDicts must exist with required fields."""
275
276 def test_code_add_json_typeddict_exists(self) -> None:
277 from muse.cli.commands.code_stage import _CodeAddJson
278 assert "staged" in _CodeAddJson.__annotations__
279 assert "exit_code" in _CodeAddJson.__annotations__
280 assert "duration_ms" in _CodeAddJson.__annotations__
281
282 def test_code_add_json_has_files_annotation(self) -> None:
283 from muse.cli.commands.code_stage import _CodeAddJson
284 assert "files" in _CodeAddJson.__annotations__
285
286 def test_code_add_json_has_added_modified_deleted(self) -> None:
287 from muse.cli.commands.code_stage import _CodeAddJson
288 assert "added" in _CodeAddJson.__annotations__
289 assert "modified" in _CodeAddJson.__annotations__
290 assert "deleted" in _CodeAddJson.__annotations__
291
292 def test_code_add_json_has_dry_run(self) -> None:
293 from muse.cli.commands.code_stage import _CodeAddJson
294 assert "dry_run" in _CodeAddJson.__annotations__
295
296 def test_code_reset_json_typeddict_exists(self) -> None:
297 from muse.cli.commands.code_stage import _CodeResetJson
298 assert "unstaged" in _CodeResetJson.__annotations__
299 assert "exit_code" in _CodeResetJson.__annotations__
300 assert "duration_ms" in _CodeResetJson.__annotations__
301
302 def test_code_reset_json_has_files_annotation(self) -> None:
303 from muse.cli.commands.code_stage import _CodeResetJson
304 assert "files" in _CodeResetJson.__annotations__
305
306 def test_code_reset_json_has_not_staged(self) -> None:
307 from muse.cli.commands.code_stage import _CodeResetJson
308 assert "not_staged" in _CodeResetJson.__annotations__
309
310 def test_code_reset_json_has_dry_run(self) -> None:
311 from muse.cli.commands.code_stage import _CodeResetJson
312 assert "dry_run" in _CodeResetJson.__annotations__
313
314
315 # ---------------------------------------------------------------------------
316 # Docstrings
317 # ---------------------------------------------------------------------------
318
319
320 # ---------------------------------------------------------------------------
321 # Error JSON paths
322 # ---------------------------------------------------------------------------
323
324
325 class TestErrorJson:
326 """Error branches emit valid JSON with exit_code and duration_ms when --json."""
327
328 def test_add_no_matching_files_json_has_exit_code(self, repo: pathlib.Path) -> None:
329 r = _run(repo, "code", "add", "--json", "ghost.py")
330 assert r.exit_code != 0
331 d = json.loads(r.output.strip())
332 assert "exit_code" in d
333
334 def test_add_no_matching_files_json_has_duration_ms(self, repo: pathlib.Path) -> None:
335 r = _run(repo, "code", "add", "--json", "ghost.py")
336 assert r.exit_code != 0
337 d = json.loads(r.output.strip())
338 assert "duration_ms" in d
339
340 def test_add_error_exit_code_nonzero_in_json(self, repo: pathlib.Path) -> None:
341 r = _run(repo, "code", "add", "--json", "ghost.py")
342 assert r.exit_code != 0
343 d = json.loads(r.output.strip())
344 assert d["exit_code"] != 0
345
346 def test_add_error_exit_code_mirrors_process(self, repo: pathlib.Path) -> None:
347 r = _run(repo, "code", "add", "--json", "ghost.py")
348 d = json.loads(r.output.strip())
349 assert d["exit_code"] == r.exit_code
350
351 def test_add_error_json_has_staged_zero(self, repo: pathlib.Path) -> None:
352 r = _run(repo, "code", "add", "--json", "ghost.py")
353 d = json.loads(r.output.strip())
354 assert d.get("staged") == 0 or "error" in d
355
356
357 # ---------------------------------------------------------------------------
358 # ANSI sanitization in JSON output
359 # ---------------------------------------------------------------------------
360
361
362 class TestAnsiSanitizationJson:
363 """File paths emitted in JSON must never contain ANSI escape sequences."""
364
365 def test_add_json_file_path_has_no_ansi(self, repo: pathlib.Path) -> None:
366 (repo / "main.py").write_text("x = 2\n")
367 r = _run(repo, "code", "add", "--json", "main.py")
368 assert r.exit_code == 0
369 assert "\x1b" not in r.output
370
371 def test_add_json_multiple_files_no_ansi(self, repo: pathlib.Path) -> None:
372 for i in range(3):
373 (repo / f"f{i}.py").write_text(f"v = {i}\n")
374 r = _run(repo, "code", "add", "-A", "--json")
375 assert r.exit_code == 0
376 assert "\x1b" not in r.output
377
378 def test_reset_json_file_path_has_no_ansi(self, repo: pathlib.Path) -> None:
379 (repo / "main.py").write_text("x = 3\n")
380 _run(repo, "code", "add", "main.py")
381 r = _run(repo, "code", "reset", "--json", "main.py")
382 assert r.exit_code == 0
383 assert "\x1b" not in r.output
384
385 def test_add_json_dry_run_no_ansi(self, repo: pathlib.Path) -> None:
386 (repo / "main.py").write_text("x = 4\n")
387 r = _run(repo, "code", "add", "--dry-run", "--json", ".")
388 assert "\x1b" not in r.output
389
390
391 # ---------------------------------------------------------------------------
392 # Performance
393 # ---------------------------------------------------------------------------
394
395
396 class TestPerformance:
397 """duration_ms stays well within reason for normal operations."""
398
399 def test_add_single_file_duration_under_1000ms(self, repo: pathlib.Path) -> None:
400 (repo / "main.py").write_text("x = 2\n")
401 r = _run(repo, "code", "add", "--json", "main.py")
402 assert r.exit_code == 0
403 assert json.loads(r.output.strip())["duration_ms"] < 1000
404
405 def test_add_all_duration_under_1000ms(self, repo: pathlib.Path) -> None:
406 for i in range(20):
407 (repo / f"f{i}.py").write_text(f"v = {i}\n")
408 r = _run(repo, "code", "add", "-A", "--json")
409 assert r.exit_code == 0
410 assert json.loads(r.output.strip())["duration_ms"] < 1000
411
412 def test_reset_duration_under_1000ms(self, repo: pathlib.Path) -> None:
413 (repo / "main.py").write_text("x = 5\n")
414 _run(repo, "code", "add", "main.py")
415 r = _run(repo, "code", "reset", "--json")
416 assert r.exit_code == 0
417 assert json.loads(r.output.strip())["duration_ms"] < 1000