gabriel / muse public

test_cmd_commit_tree.py file-level

at sha256:2 · 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 """Comprehensive tests for ``muse commit-tree``.
2
3 Coverage tiers
4 --------------
5 - Unit: _FORMAT_CHOICES
6 - Integration: basic creation, --parent chain, merge commit, text format,
7 --agent-id/--model-id/--toolchain-id provenance, --branch override
8 - Security: >2 parents silently rejected, errors to stderr, no traceback
9 - Stress: 200 sequential commit-tree calls
10 """
11 from __future__ import annotations
12
13 import datetime
14 import json
15 import pathlib
16
17 from muse.core.errors import ExitCode
18 from muse.core.ids import hash_commit, hash_snapshot
19 from muse.core.commits import (
20 CommitRecord,
21 read_commit,
22 write_commit,
23 )
24 from muse.core.snapshots import (
25 SnapshotRecord,
26 write_snapshot,
27 )
28 from muse.core.types import Manifest, fake_id
29 from muse.core.paths import muse_dir
30 from tests.cli_test_helper import CliRunner, InvokeResult
31
32 runner = CliRunner()
33
34
35 # ---------------------------------------------------------------------------
36 # Helpers
37 # ---------------------------------------------------------------------------
38
39 def _make_repo(tmp_path: pathlib.Path) -> pathlib.Path:
40 repo = tmp_path / "repo"
41 dot_muse = muse_dir(repo)
42 for sub in ("objects", "commits", "snapshots", "refs/heads"):
43 (dot_muse / sub).mkdir(parents=True)
44 (dot_muse / "HEAD").write_text("ref: refs/heads/main")
45 (dot_muse / "repo.json").write_text(json.dumps({"repo_id": "test-repo", "domain": "code"}))
46 return repo
47
48
49 def _snap(repo: pathlib.Path) -> str:
50 manifest: Manifest = {}
51 sid = hash_snapshot(manifest)
52 write_snapshot(repo, SnapshotRecord(
53 snapshot_id=sid,
54 manifest=manifest,
55 created_at=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc),
56 ))
57 return sid
58
59
60 def _commit(
61 repo: pathlib.Path,
62 snap_id: str,
63 parent: str | None = None,
64 message: str = "parent",
65 ) -> str:
66 committed_at = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
67 parent_ids: list[str] = [parent] if parent else []
68 commit_id = hash_commit(
69 parent_ids=parent_ids,
70 snapshot_id=snap_id,
71 message=message,
72 committed_at_iso=committed_at.isoformat(),
73 )
74 write_commit(repo, CommitRecord(
75 commit_id=commit_id,
76 branch="main",
77 snapshot_id=snap_id,
78 message=message,
79 committed_at=committed_at,
80 parent_commit_id=parent,
81 ))
82 return commit_id
83
84
85 def _ct(repo: pathlib.Path, *args: str) -> InvokeResult:
86 from muse.cli.app import main as cli
87 return runner.invoke(
88 cli,
89 ["commit-tree", "--json", *args],
90 env={"MUSE_REPO_ROOT": str(repo)},
91 )
92
93
94 # ---------------------------------------------------------------------------
95 # Unit
96 # ---------------------------------------------------------------------------
97
98
99 class TestUnit:
100 def test_json_flag_registered(self) -> None:
101 from muse.cli.commands.commit_tree import register
102 import argparse
103 p = argparse.ArgumentParser()
104 subs = p.add_subparsers()
105 register(subs)
106 ns = p.parse_args(["commit-tree", "--snapshot", fake_id("a"), "--json"])
107 assert ns.json_out is True
108
109
110 # ---------------------------------------------------------------------------
111 # Integration β€” basic creation
112 # ---------------------------------------------------------------------------
113
114
115 class TestBasicCreation:
116 def test_creates_commit(self, tmp_path: pathlib.Path) -> None:
117 repo = _make_repo(tmp_path)
118 sid = _snap(repo)
119 result = _ct(repo, "--snapshot", sid, "--message", "first commit")
120 assert result.exit_code == 0
121 data = json.loads(result.output)
122 assert "commit_id" in data
123 assert data["commit_id"].startswith("sha256:")
124 assert len(data["commit_id"]) == 71
125
126 def test_commit_persisted_in_store(self, tmp_path: pathlib.Path) -> None:
127 repo = _make_repo(tmp_path)
128 sid = _snap(repo)
129 data = json.loads(_ct(repo, "--snapshot", sid).output)
130 cid = data["commit_id"]
131 rec = read_commit(repo, cid)
132 assert rec is not None
133 assert rec.snapshot_id == sid
134
135 def test_json_flag_shorthand(self, tmp_path: pathlib.Path) -> None:
136 repo = _make_repo(tmp_path)
137 sid = _snap(repo)
138 result = _ct(repo, "--json", "--snapshot", sid)
139 assert result.exit_code == 0
140 assert "commit_id" in json.loads(result.output)
141
142 def test_text_format_bare_commit_id(self, tmp_path: pathlib.Path) -> None:
143 from muse.cli.app import main as cli
144 repo = _make_repo(tmp_path)
145 sid = _snap(repo)
146 result = runner.invoke(
147 cli,
148 ["commit-tree", "--snapshot", sid],
149 env={"MUSE_REPO_ROOT": str(repo)},
150 )
151 assert result.exit_code == 0
152 line = result.output.strip()
153 assert line.startswith("sha256:")
154 assert len(line) == 71
155
156 def test_message_stored(self, tmp_path: pathlib.Path) -> None:
157 repo = _make_repo(tmp_path)
158 sid = _snap(repo)
159 data = json.loads(_ct(repo, "--snapshot", sid, "--message", "my msg").output)
160 rec = read_commit(repo, data["commit_id"])
161 assert rec is not None
162 assert rec.message == "my msg"
163
164 def test_author_stored(self, tmp_path: pathlib.Path) -> None:
165 repo = _make_repo(tmp_path)
166 sid = _snap(repo)
167 data = json.loads(_ct(repo, "--snapshot", sid, "--author", "gabriel").output)
168 rec = read_commit(repo, data["commit_id"])
169 assert rec is not None
170 assert rec.author == "gabriel"
171
172
173 # ---------------------------------------------------------------------------
174 # Integration β€” parent chain
175 # ---------------------------------------------------------------------------
176
177
178 class TestParentChain:
179 def test_single_parent_stored(self, tmp_path: pathlib.Path) -> None:
180 repo = _make_repo(tmp_path)
181 sid = _snap(repo)
182 p1_id = _commit(repo, sid)
183 data = json.loads(_ct(repo, "--snapshot", sid, "--parent", p1_id).output)
184 rec = read_commit(repo, data["commit_id"])
185 assert rec is not None
186 assert rec.parent_commit_id == p1_id
187 assert rec.parent2_commit_id is None
188
189 def test_merge_commit_two_parents(self, tmp_path: pathlib.Path) -> None:
190 repo = _make_repo(tmp_path)
191 sid = _snap(repo)
192 p1 = _commit(repo, sid, message="parent1")
193 p2 = _commit(repo, sid, message="parent2")
194 data = json.loads(
195 _ct(repo, "--snapshot", sid, "--parent", p1, "--parent", p2).output
196 )
197 rec = read_commit(repo, data["commit_id"])
198 assert rec is not None
199 assert rec.parent_commit_id == p1
200 assert rec.parent2_commit_id == p2
201
202 def test_three_parents_rejected(self, tmp_path: pathlib.Path) -> None:
203 repo = _make_repo(tmp_path)
204 sid = _snap(repo)
205 p = _commit(repo, sid)
206 result = _ct(
207 repo, "--snapshot", sid,
208 "--parent", p, "--parent", p, "--parent", p,
209 )
210 assert result.exit_code == ExitCode.USER_ERROR
211
212 def test_missing_parent_errors(self, tmp_path: pathlib.Path) -> None:
213 repo = _make_repo(tmp_path)
214 sid = _snap(repo)
215 result = _ct(repo, "--snapshot", sid, "--parent", f"dead{'beef' * 15}")
216 assert result.exit_code == ExitCode.USER_ERROR
217
218
219 # ---------------------------------------------------------------------------
220 # Integration β€” agent provenance flags
221 # ---------------------------------------------------------------------------
222
223
224 class TestAgentProvenance:
225 def test_agent_id_stored(self, tmp_path: pathlib.Path) -> None:
226 repo = _make_repo(tmp_path)
227 sid = _snap(repo)
228 data = json.loads(
229 _ct(repo, "--snapshot", sid, "--agent-id", "my-bot").output
230 )
231 rec = read_commit(repo, data["commit_id"])
232 assert rec is not None
233 assert rec.agent_id == "my-bot"
234
235 def test_model_id_stored(self, tmp_path: pathlib.Path) -> None:
236 repo = _make_repo(tmp_path)
237 sid = _snap(repo)
238 data = json.loads(
239 _ct(repo, "--snapshot", sid, "--model-id", "claude-opus-4").output
240 )
241 rec = read_commit(repo, data["commit_id"])
242 assert rec is not None
243 assert rec.model_id == "claude-opus-4"
244
245 def test_toolchain_id_stored(self, tmp_path: pathlib.Path) -> None:
246 repo = _make_repo(tmp_path)
247 sid = _snap(repo)
248 data = json.loads(
249 _ct(repo, "--snapshot", sid, "--toolchain-id", "cursor-agent-v2").output
250 )
251 rec = read_commit(repo, data["commit_id"])
252 assert rec is not None
253 assert rec.toolchain_id == "cursor-agent-v2"
254
255 def test_full_provenance_round_trip(self, tmp_path: pathlib.Path) -> None:
256 repo = _make_repo(tmp_path)
257 sid = _snap(repo)
258 data = json.loads(_ct(
259 repo,
260 "--snapshot", sid,
261 "--agent-id", "audit-bot",
262 "--model-id", "claude-4",
263 "--toolchain-id", "muse-agent-v1",
264 ).output)
265 rec = read_commit(repo, data["commit_id"])
266 assert rec is not None
267 assert rec.agent_id == "audit-bot"
268 assert rec.model_id == "claude-4"
269 assert rec.toolchain_id == "muse-agent-v1"
270
271 def test_defaults_to_empty_strings(self, tmp_path: pathlib.Path) -> None:
272 repo = _make_repo(tmp_path)
273 sid = _snap(repo)
274 data = json.loads(_ct(repo, "--snapshot", sid).output)
275 rec = read_commit(repo, data["commit_id"])
276 assert rec is not None
277 assert rec.agent_id == ""
278 assert rec.model_id == ""
279 assert rec.toolchain_id == ""
280
281
282 # ---------------------------------------------------------------------------
283 # Integration β€” --branch override
284 # ---------------------------------------------------------------------------
285
286
287 class TestBranchOverride:
288 def test_branch_override_stored(self, tmp_path: pathlib.Path) -> None:
289 repo = _make_repo(tmp_path)
290 sid = _snap(repo)
291 data = json.loads(_ct(repo, "--snapshot", sid, "--branch", "feat/x").output)
292 rec = read_commit(repo, data["commit_id"])
293 assert rec is not None
294 assert rec.branch == "feat/x"
295
296
297 # ---------------------------------------------------------------------------
298 # Error cases
299 # ---------------------------------------------------------------------------
300
301
302 class TestErrors:
303 def test_missing_snapshot_errors(self, tmp_path: pathlib.Path) -> None:
304 repo = _make_repo(tmp_path)
305 result = _ct(repo, "--snapshot", f"dead{'beef' * 15}")
306 assert result.exit_code == ExitCode.USER_ERROR
307
308 def test_invalid_snapshot_id_errors(self, tmp_path: pathlib.Path) -> None:
309 repo = _make_repo(tmp_path)
310 result = _ct(repo, "--snapshot", "not-hex")
311 assert result.exit_code == ExitCode.USER_ERROR
312
313 def test_invalid_parent_id_errors(self, tmp_path: pathlib.Path) -> None:
314 repo = _make_repo(tmp_path)
315 sid = _snap(repo)
316 result = _ct(repo, "--snapshot", sid, "--parent", "bad-hex")
317 assert result.exit_code == ExitCode.USER_ERROR
318
319 def test_missing_snapshot_arg_errors(self, tmp_path: pathlib.Path) -> None:
320 from muse.cli.app import main as cli
321 repo = _make_repo(tmp_path)
322 result = runner.invoke(
323 cli,
324 ["commit-tree"],
325 env={"MUSE_REPO_ROOT": str(repo)},
326 )
327 assert result.exit_code != 0
328
329
330 # ---------------------------------------------------------------------------
331 # Security
332 # ---------------------------------------------------------------------------
333
334
335 class TestSecurity:
336 def test_no_traceback_on_bad_snapshot(self, tmp_path: pathlib.Path) -> None:
337 repo = _make_repo(tmp_path)
338 result = _ct(repo, "--snapshot", "bad")
339 assert "Traceback" not in result.output
340
341 def test_no_traceback_on_too_many_parents(self, tmp_path: pathlib.Path) -> None:
342 repo = _make_repo(tmp_path)
343 sid = _snap(repo)
344 p = _commit(repo, sid)
345 result = _ct(repo, "--snapshot", sid, "--parent", p, "--parent", p, "--parent", p)
346 assert "Traceback" not in result.output
347
348
349 # ---------------------------------------------------------------------------
350 # Stress
351 # ---------------------------------------------------------------------------
352
353
354 class TestStress:
355 def test_200_sequential_commits(self, tmp_path: pathlib.Path) -> None:
356 repo = _make_repo(tmp_path)
357 sid = _snap(repo)
358 for i in range(200):
359 result = _ct(repo, "--snapshot", sid, "--message", f"commit {i}")
360 assert result.exit_code == 0, f"failed at iteration {i}"
361 data = json.loads(result.output)
362 assert data["commit_id"].startswith("sha256:")
363 assert len(data["commit_id"]) == 71
364
365
366 # ---------------------------------------------------------------------------
367 # Supercharge β€” full JSON schema
368 # ---------------------------------------------------------------------------
369
370 _FULL_KEYS = frozenset({
371 "commit_id",
372 "snapshot_id",
373 "branch",
374 "message",
375 "committed_at",
376 "author",
377 "agent_id",
378 "model_id",
379 "toolchain_id",
380 "parent_commit_id",
381 "parent2_commit_id",
382 "duration_ms",
383 "exit_code",
384 })
385
386
387 class TestJsonSchemaComplete:
388 """JSON output must carry the full commit record so agents need no follow-up read."""
389
390 def test_all_keys_present_on_success(self, tmp_path: pathlib.Path) -> None:
391 repo = _make_repo(tmp_path)
392 sid = _snap(repo)
393 data = json.loads(_ct(repo, "--snapshot", sid, "--message", "m").output)
394 assert _FULL_KEYS <= set(data.keys()), (
395 f"Missing keys: {_FULL_KEYS - set(data.keys())}"
396 )
397
398 def test_snapshot_id_echoed(self, tmp_path: pathlib.Path) -> None:
399 repo = _make_repo(tmp_path)
400 sid = _snap(repo)
401 data = json.loads(_ct(repo, "--snapshot", sid).output)
402 assert data["snapshot_id"] == sid
403
404 def test_branch_echoed(self, tmp_path: pathlib.Path) -> None:
405 repo = _make_repo(tmp_path)
406 sid = _snap(repo)
407 data = json.loads(_ct(repo, "--snapshot", sid, "--branch", "feat/x").output)
408 assert data["branch"] == "feat/x"
409
410 def test_message_echoed(self, tmp_path: pathlib.Path) -> None:
411 repo = _make_repo(tmp_path)
412 sid = _snap(repo)
413 data = json.loads(_ct(repo, "--snapshot", sid, "--message", "hello world").output)
414 assert data["message"] == "hello world"
415
416 def test_author_echoed(self, tmp_path: pathlib.Path) -> None:
417 repo = _make_repo(tmp_path)
418 sid = _snap(repo)
419 data = json.loads(_ct(repo, "--snapshot", sid, "--author", "gabriel").output)
420 assert data["author"] == "gabriel"
421
422 def test_agent_id_echoed(self, tmp_path: pathlib.Path) -> None:
423 repo = _make_repo(tmp_path)
424 sid = _snap(repo)
425 data = json.loads(_ct(repo, "--snapshot", sid, "--agent-id", "bot-x").output)
426 assert data["agent_id"] == "bot-x"
427
428 def test_model_id_echoed(self, tmp_path: pathlib.Path) -> None:
429 repo = _make_repo(tmp_path)
430 sid = _snap(repo)
431 data = json.loads(_ct(repo, "--snapshot", sid, "--model-id", "claude-opus-4").output)
432 assert data["model_id"] == "claude-opus-4"
433
434 def test_toolchain_id_echoed(self, tmp_path: pathlib.Path) -> None:
435 repo = _make_repo(tmp_path)
436 sid = _snap(repo)
437 data = json.loads(_ct(repo, "--snapshot", sid, "--toolchain-id", "v2").output)
438 assert data["toolchain_id"] == "v2"
439
440 def test_parent_commit_id_null_when_no_parent(self, tmp_path: pathlib.Path) -> None:
441 repo = _make_repo(tmp_path)
442 sid = _snap(repo)
443 data = json.loads(_ct(repo, "--snapshot", sid).output)
444 assert data["parent_commit_id"] is None
445
446 def test_parent_commit_id_present(self, tmp_path: pathlib.Path) -> None:
447 repo = _make_repo(tmp_path)
448 sid = _snap(repo)
449 p1 = _commit(repo, sid)
450 data = json.loads(_ct(repo, "--snapshot", sid, "--parent", p1).output)
451 assert data["parent_commit_id"] == p1
452
453 def test_parent2_commit_id_null_when_no_second_parent(self, tmp_path: pathlib.Path) -> None:
454 repo = _make_repo(tmp_path)
455 sid = _snap(repo)
456 data = json.loads(_ct(repo, "--snapshot", sid).output)
457 assert data["parent2_commit_id"] is None
458
459 def test_parent2_commit_id_present_for_merge(self, tmp_path: pathlib.Path) -> None:
460 repo = _make_repo(tmp_path)
461 sid = _snap(repo)
462 p1 = _commit(repo, sid, message="p1")
463 p2 = _commit(repo, sid, message="p2")
464 data = json.loads(_ct(repo, "--snapshot", sid, "--parent", p1, "--parent", p2).output)
465 assert data["parent2_commit_id"] == p2
466
467 def test_committed_at_is_iso_string(self, tmp_path: pathlib.Path) -> None:
468 repo = _make_repo(tmp_path)
469 sid = _snap(repo)
470 data = json.loads(_ct(repo, "--snapshot", sid).output)
471 ts = data["committed_at"]
472 assert isinstance(ts, str)
473 # Must parse as ISO datetime
474 datetime.datetime.fromisoformat(ts)
475
476 def test_exit_code_zero(self, tmp_path: pathlib.Path) -> None:
477 repo = _make_repo(tmp_path)
478 sid = _snap(repo)
479 data = json.loads(_ct(repo, "--snapshot", sid).output)
480 assert data["exit_code"] == 0
481
482
483 # ---------------------------------------------------------------------------
484 # Supercharge β€” duration_ms
485 # ---------------------------------------------------------------------------
486
487
488 class TestElapsed:
489 def test_elapsed_present(self, tmp_path: pathlib.Path) -> None:
490 repo = _make_repo(tmp_path)
491 sid = _snap(repo)
492 data = json.loads(_ct(repo, "--snapshot", sid).output)
493 assert "duration_ms" in data
494
495 def test_elapsed_is_float(self, tmp_path: pathlib.Path) -> None:
496 repo = _make_repo(tmp_path)
497 sid = _snap(repo)
498 data = json.loads(_ct(repo, "--snapshot", sid).output)
499 assert isinstance(data["duration_ms"], float)
500
501 def test_elapsed_non_negative(self, tmp_path: pathlib.Path) -> None:
502 repo = _make_repo(tmp_path)
503 sid = _snap(repo)
504 data = json.loads(_ct(repo, "--snapshot", sid).output)
505 assert data["duration_ms"] >= 0.0
506
507
508 # ---------------------------------------------------------------------------
509 # Supercharge β€” exit_code
510 # ---------------------------------------------------------------------------
511
512
513 class TestExitCode:
514 def test_exit_code_zero_on_success(self, tmp_path: pathlib.Path) -> None:
515 repo = _make_repo(tmp_path)
516 sid = _snap(repo)
517 data = json.loads(_ct(repo, "--snapshot", sid).output)
518 assert data["exit_code"] == 0
519
520 def test_process_exit_zero_on_success(self, tmp_path: pathlib.Path) -> None:
521 repo = _make_repo(tmp_path)
522 sid = _snap(repo)
523 result = _ct(repo, "--snapshot", sid)
524 assert result.exit_code == 0
525
526
527 # ---------------------------------------------------------------------------
528 # Supercharge β€” data integrity
529 # ---------------------------------------------------------------------------
530
531
532 class TestDataIntegrity:
533 def test_commit_id_roundtrips_via_store(self, tmp_path: pathlib.Path) -> None:
534 """commit_id in JSON matches what was actually written to the store."""
535 repo = _make_repo(tmp_path)
536 sid = _snap(repo)
537 data = json.loads(_ct(repo, "--snapshot", sid, "--message", "integrity check").output)
538 rec = read_commit(repo, data["commit_id"])
539 assert rec is not None
540 assert rec.commit_id == data["commit_id"]
541 assert rec.snapshot_id == data["snapshot_id"]
542 assert rec.message == data["message"]
543
544 def test_snapshot_id_matches_store(self, tmp_path: pathlib.Path) -> None:
545 repo = _make_repo(tmp_path)
546 sid = _snap(repo)
547 data = json.loads(_ct(repo, "--snapshot", sid).output)
548 rec = read_commit(repo, data["commit_id"])
549 assert rec is not None
550 assert rec.snapshot_id == sid
551
552 def test_parent_id_matches_store(self, tmp_path: pathlib.Path) -> None:
553 repo = _make_repo(tmp_path)
554 sid = _snap(repo)
555 p1 = _commit(repo, sid)
556 data = json.loads(_ct(repo, "--snapshot", sid, "--parent", p1).output)
557 rec = read_commit(repo, data["commit_id"])
558 assert rec is not None
559 assert rec.parent_commit_id == data["parent_commit_id"] == p1
560
561 def test_provenance_matches_store(self, tmp_path: pathlib.Path) -> None:
562 repo = _make_repo(tmp_path)
563 sid = _snap(repo)
564 data = json.loads(_ct(
565 repo, "--snapshot", sid,
566 "--agent-id", "integrity-bot",
567 "--model-id", "claude-sonnet-4-6",
568 "--toolchain-id", "test-chain",
569 ).output)
570 rec = read_commit(repo, data["commit_id"])
571 assert rec is not None
572 assert rec.agent_id == data["agent_id"] == "integrity-bot"
573 assert rec.model_id == data["model_id"] == "claude-sonnet-4-6"
574 assert rec.toolchain_id == data["toolchain_id"] == "test-chain"
575
576
577 # ---------------------------------------------------------------------------
578 # Supercharge β€” security (ANSI injection)
579 # ---------------------------------------------------------------------------
580
581
582 class TestSecurityAnsi:
583 def test_ansi_in_message_does_not_corrupt_json(self, tmp_path: pathlib.Path) -> None:
584 repo = _make_repo(tmp_path)
585 sid = _snap(repo)
586 malicious_message = "feat: \x1b[31mred\x1b[0m alert"
587 result = _ct(repo, "--snapshot", sid, "--message", malicious_message)
588 assert result.exit_code == 0
589 data = json.loads(result.output)
590 assert data["message"] == malicious_message
591
592 def test_ansi_in_author_does_not_corrupt_json(self, tmp_path: pathlib.Path) -> None:
593 repo = _make_repo(tmp_path)
594 sid = _snap(repo)
595 malicious_author = "\x1b[32mgabriel\x1b[0m"
596 result = _ct(repo, "--snapshot", sid, "--author", malicious_author)
597 assert result.exit_code == 0
598 data = json.loads(result.output)
599 assert data["author"] == malicious_author
600
601 def test_long_message_handled(self, tmp_path: pathlib.Path) -> None:
602 repo = _make_repo(tmp_path)
603 sid = _snap(repo)
604 long_msg = "x" * 10_000
605 data = json.loads(_ct(repo, "--snapshot", sid, "--message", long_msg).output)
606 rec = read_commit(repo, data["commit_id"])
607 assert rec is not None
608 assert rec.message == long_msg
609
610
611 # ---------------------------------------------------------------------------
612 # Supercharge β€” performance
613 # ---------------------------------------------------------------------------
614
615
616 class TestPerformance:
617 def test_single_commit_under_500ms(self, tmp_path: pathlib.Path) -> None:
618 import time
619 repo = _make_repo(tmp_path)
620 sid = _snap(repo)
621 t0 = time.monotonic()
622 result = _ct(repo, "--snapshot", sid)
623 elapsed = time.monotonic() - t0
624 assert result.exit_code == 0
625 assert elapsed < 0.5, f"commit-tree took {elapsed:.3f}s β€” too slow"
626
627 def test_duration_ms_reasonable(self, tmp_path: pathlib.Path) -> None:
628 repo = _make_repo(tmp_path)
629 sid = _snap(repo)
630 data = json.loads(_ct(repo, "--snapshot", sid).output)
631 assert data["duration_ms"] < 50.0, (
632 f"reported elapsed {data['duration_ms']}ms β€” implausibly slow"
633 )
634
635
636 # ---------------------------------------------------------------------------
637 # Flag registration tests
638 # ---------------------------------------------------------------------------
639
640 import argparse as _argparse
641 from muse.cli.commands.commit_tree import register as _register_commit_tree
642
643
644 def _parse_ct(*args: str) -> _argparse.Namespace:
645 root_p = _argparse.ArgumentParser()
646 subs = root_p.add_subparsers(dest="cmd")
647 _register_commit_tree(subs)
648 return root_p.parse_args(["commit-tree", *args])
649
650
651 class TestRegisterFlags:
652 def test_default_json_out_is_false(self) -> None:
653 ns = _parse_ct("--snapshot", fake_id("a"))
654 assert ns.json_out is False
655
656 def test_json_flag_sets_json_out(self) -> None:
657 ns = _parse_ct("--snapshot", fake_id("a"), "--json")
658 assert ns.json_out is True
659
660 def test_j_shorthand_sets_json_out(self) -> None:
661 ns = _parse_ct("--snapshot", fake_id("a"), "-j")
662 assert ns.json_out is True
663
664 def test_format_flag_no_longer_exists(self) -> None:
665 import pytest
666 with pytest.raises(SystemExit):
667 _parse_ct("--snapshot", fake_id("a"), "--format", "json")