gabriel / muse public
test_patch_id_supercharge.py python
645 lines 26.0 KB
Raw
sha256:88ac91129873e6a496e9189515aa690eb893ae25d69c8f72af141a2be5068eb3 docs: docstring sprint contract→find-symbol — idiomatic run… Sonnet 4.6 patch 143 days ago
1 """Supercharge tests for ``muse patch-id``.
2
3 TDD — [RED] tests fail until the feature lands; [GREEN] tests replace the
4 broken ``test_cmd_patch_id.py`` (which used raw hex object IDs without the
5 required ``sha256:`` prefix).
6
7 New features under test
8 -----------------------
9 - ``duration_ms`` [RED] — wall-clock ms in JSON output
10 - ``exit_code`` [RED] — always present in JSON output
11 - ``files_changed`` [RED] — count of files in the diff in JSON output
12 - ``stable`` [RED] — boolean reflecting --stable flag in JSON output
13
14 Gap-fill / regression coverage [GREEN]
15 ----------------------------------------
16 - _compute_patch_id unit tests (all using sha256: prefix)
17 - JSON schema keys present
18 - Text output format «<patch_id> <commit_id>»
19 - Same diff → same patch-id (cherry-pick detection)
20 - Different diff → different patch-id
21 - --stable whitespace normalisation via CLI and unit
22 - Initial commit (no parent) has deterministic patch-id
23 - Branch name ref, explicit commit ID ref
24 - Error paths: empty repo, bad ref
25 - Security: ANSI, null byte, path traversal, very long ref, no tracebacks
26 - Data integrity: patch_id changes when content changes
27 - Multi-file commit patch-id covers all changed files
28 - Binary file diffs included in patch-id
29 - Performance: duration_ms < 2000ms for 20-file commit
30 - Stress: 10 distinct commits → 10 distinct patch-ids
31 """
32 from __future__ import annotations
33
34 import datetime
35 import hashlib
36 import json
37 import pathlib
38
39 import pytest
40
41 from muse.cli.commands.patch_id import _compute_patch_id
42 from muse.core.object_store import write_object
43 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
44 from muse.core.store import CommitRecord, SnapshotRecord, write_commit, write_snapshot
45 from muse.core._types import Manifest, long_id
46 from tests.cli_test_helper import CliRunner, InvokeResult
47
48 runner = CliRunner()
49
50 _TS = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
51 _REPO_ID = "patch-id-test"
52
53
54 # ---------------------------------------------------------------------------
55 # Helpers
56 # ---------------------------------------------------------------------------
57
58 def _oid(content: bytes) -> str:
59 """Return a sha256:-prefixed object ID for content."""
60 return long_id(hashlib.sha256(content).hexdigest())
61
62
63 def _init_repo(path: pathlib.Path) -> pathlib.Path:
64 muse = path / ".muse"
65 for d in ("commits", "snapshots", "objects", "refs/heads"):
66 (muse / d).mkdir(parents=True, exist_ok=True)
67 (muse / "HEAD").write_text("ref: refs/heads/main", encoding="utf-8")
68 (muse / "repo.json").write_text(
69 json.dumps({"repo_id": _REPO_ID, "domain": "code"}), encoding="utf-8"
70 )
71 return path
72
73
74 def _write_obj(repo: pathlib.Path, content: bytes) -> str:
75 oid = _oid(content)
76 write_object(repo, oid, content)
77 return oid
78
79
80 def _commit(
81 repo: pathlib.Path,
82 msg: str,
83 manifest: dict[str, str],
84 *,
85 branch: str = "main",
86 parent: str | None = None,
87 ) -> str:
88 sid = compute_snapshot_id(manifest)
89 write_snapshot(repo, SnapshotRecord(snapshot_id=sid, manifest=manifest, created_at=_TS))
90 parent_ids = [parent] if parent else []
91 cid = compute_commit_id(parent_ids, sid, msg, _TS.isoformat())
92 write_commit(repo, CommitRecord(
93 commit_id=cid, repo_id=_REPO_ID, branch=branch,
94 snapshot_id=sid, message=msg, committed_at=_TS,
95 author="gabriel", parent_commit_id=parent, parent2_commit_id=None,
96 ))
97 ref = repo / ".muse" / "refs" / "heads" / branch
98 ref.parent.mkdir(parents=True, exist_ok=True)
99 ref.write_text(cid)
100 return cid
101
102
103 def _pid(repo: pathlib.Path, *args: str) -> InvokeResult:
104 return runner.invoke(None, ["patch-id", *args], env={"MUSE_REPO_ROOT": str(repo)})
105
106
107 def _json_out(r: InvokeResult) -> dict:
108 for line in r.output.splitlines():
109 line = line.strip()
110 if line.startswith("{"):
111 return json.loads(line)
112 raise ValueError(f"No JSON in output:\n{r.output!r}")
113
114
115 # ---------------------------------------------------------------------------
116 # _compute_patch_id unit tests [GREEN — fix sha256: prefix]
117 # ---------------------------------------------------------------------------
118
119 class TestComputePatchId:
120 def test_same_diff_same_id(self, tmp_path: pathlib.Path) -> None:
121 repo = _init_repo(tmp_path)
122 oid_a = _write_obj(repo, b"x = 1\n")
123 oid_b = _write_obj(repo, b"x = 2\n")
124 base = {"f.py": oid_a}
125 target = {"f.py": oid_b}
126 assert _compute_patch_id(repo, base, target, stable=False) == \
127 _compute_patch_id(repo, base, target, stable=False)
128
129 def test_result_is_64_hex_chars(self, tmp_path: pathlib.Path) -> None:
130 repo = _init_repo(tmp_path)
131 oid_a = _write_obj(repo, b"a")
132 oid_b = _write_obj(repo, b"b")
133 pid = _compute_patch_id(repo, {"f.py": oid_a}, {"f.py": oid_b}, stable=False)
134 assert len(pid) == 64
135 assert all(c in "0123456789abcdef" for c in pid)
136
137 def test_different_content_different_id(self, tmp_path: pathlib.Path) -> None:
138 repo = _init_repo(tmp_path)
139 oid_a = _write_obj(repo, b"v1\n")
140 oid_b = _write_obj(repo, b"v2\n")
141 oid_c = _write_obj(repo, b"v3\n")
142 id1 = _compute_patch_id(repo, {"f.py": oid_a}, {"f.py": oid_b}, stable=False)
143 id2 = _compute_patch_id(repo, {"f.py": oid_a}, {"f.py": oid_c}, stable=False)
144 assert id1 != id2
145
146 def test_no_op_commit_deterministic(self, tmp_path: pathlib.Path) -> None:
147 repo = _init_repo(tmp_path)
148 oid = _write_obj(repo, b"unchanged\n")
149 manifest = {"f.py": oid}
150 pid1 = _compute_patch_id(repo, manifest, manifest, stable=False)
151 pid2 = _compute_patch_id(repo, manifest, manifest, stable=False)
152 assert pid1 == pid2
153
154 def test_stable_normalizes_trailing_whitespace(self, tmp_path: pathlib.Path) -> None:
155 repo = _init_repo(tmp_path)
156 oid_base = _write_obj(repo, b"x = 1\n")
157 oid_clean = _write_obj(repo, b"x = 2\n")
158 oid_ws = _write_obj(repo, b"x = 2 \n")
159 base = {"f.py": oid_base}
160 id_clean = _compute_patch_id(repo, base, {"f.py": oid_clean}, stable=True)
161 id_ws = _compute_patch_id(repo, base, {"f.py": oid_ws}, stable=True)
162 assert id_clean == id_ws
163
164 def test_unstable_sensitive_to_whitespace(self, tmp_path: pathlib.Path) -> None:
165 repo = _init_repo(tmp_path)
166 oid_base = _write_obj(repo, b"x = 1\n")
167 oid_clean = _write_obj(repo, b"x = 2\n")
168 oid_ws = _write_obj(repo, b"x = 2 \n")
169 base = {"f.py": oid_base}
170 id_clean = _compute_patch_id(repo, base, {"f.py": oid_clean}, stable=False)
171 id_ws = _compute_patch_id(repo, base, {"f.py": oid_ws}, stable=False)
172 assert id_clean != id_ws
173
174 def test_file_order_does_not_affect_id(self, tmp_path: pathlib.Path) -> None:
175 """Files are sorted alphabetically so order of dict keys is irrelevant."""
176 repo = _init_repo(tmp_path)
177 oid_a = _write_obj(repo, b"a\n")
178 oid_b = _write_obj(repo, b"b\n")
179 oid_a2 = _write_obj(repo, b"a2\n")
180 oid_b2 = _write_obj(repo, b"b2\n")
181 base1 = {"a.py": oid_a, "b.py": oid_b}
182 base2 = {"b.py": oid_b, "a.py": oid_a}
183 target1 = {"a.py": oid_a2, "b.py": oid_b2}
184 target2 = {"b.py": oid_b2, "a.py": oid_a2}
185 assert _compute_patch_id(repo, base1, target1, stable=False) == \
186 _compute_patch_id(repo, base2, target2, stable=False)
187
188 def test_initial_commit_no_parent(self, tmp_path: pathlib.Path) -> None:
189 """Initial commit: base_manifest={}, target has files."""
190 repo = _init_repo(tmp_path)
191 oid = _write_obj(repo, b"hello\n")
192 pid = _compute_patch_id(repo, {}, {"f.py": oid}, stable=False)
193 assert len(pid) == 64
194
195 def test_deleted_file_affects_id(self, tmp_path: pathlib.Path) -> None:
196 repo = _init_repo(tmp_path)
197 oid = _write_obj(repo, b"bye\n")
198 id_del = _compute_patch_id(repo, {"f.py": oid}, {}, stable=False)
199 id_add = _compute_patch_id(repo, {}, {"f.py": oid}, stable=False)
200 assert id_del != id_add
201
202 def test_binary_content_included(self, tmp_path: pathlib.Path) -> None:
203 """Binary files (non-UTF-8) still produce a stable patch-id."""
204 repo = _init_repo(tmp_path)
205 binary_v1 = bytes(range(256))
206 binary_v2 = bytes(range(255, -1, -1))
207 oid1 = _write_obj(repo, binary_v1)
208 oid2 = _write_obj(repo, binary_v2)
209 pid1 = _compute_patch_id(repo, {"img.bin": oid1}, {"img.bin": oid2}, stable=False)
210 pid2 = _compute_patch_id(repo, {"img.bin": oid1}, {"img.bin": oid2}, stable=False)
211 assert pid1 == pid2
212 assert len(pid1) == 64
213
214
215 # ---------------------------------------------------------------------------
216 # JSON output: duration_ms, exit_code, files_changed, stable [RED]
217 # ---------------------------------------------------------------------------
218
219 class TestJsonSupercharge:
220 """[RED] New fields in --json output."""
221
222 def test_json_has_duration_ms(self, tmp_path: pathlib.Path) -> None:
223 repo = _init_repo(tmp_path)
224 oid = _write_obj(repo, b"x")
225 _commit(repo, "init", {"f.py": oid})
226 d = _json_out(_pid(repo, "--json"))
227 assert "duration_ms" in d
228
229 def test_json_duration_ms_non_negative(self, tmp_path: pathlib.Path) -> None:
230 repo = _init_repo(tmp_path)
231 oid = _write_obj(repo, b"x")
232 _commit(repo, "init", {"f.py": oid})
233 d = _json_out(_pid(repo, "--json"))
234 assert d["duration_ms"] >= 0.0
235
236 def test_json_has_exit_code(self, tmp_path: pathlib.Path) -> None:
237 repo = _init_repo(tmp_path)
238 oid = _write_obj(repo, b"x")
239 _commit(repo, "init", {"f.py": oid})
240 d = _json_out(_pid(repo, "--json"))
241 assert "exit_code" in d
242
243 def test_json_exit_code_zero_on_success(self, tmp_path: pathlib.Path) -> None:
244 repo = _init_repo(tmp_path)
245 oid = _write_obj(repo, b"x")
246 _commit(repo, "init", {"f.py": oid})
247 d = _json_out(_pid(repo, "--json"))
248 assert d["exit_code"] == 0
249
250 def test_json_has_files_changed(self, tmp_path: pathlib.Path) -> None:
251 repo = _init_repo(tmp_path)
252 oid = _write_obj(repo, b"x")
253 _commit(repo, "init", {"f.py": oid})
254 d = _json_out(_pid(repo, "--json"))
255 assert "files_changed" in d
256
257 def test_json_files_changed_correct_count(self, tmp_path: pathlib.Path) -> None:
258 repo = _init_repo(tmp_path)
259 oid_a = _write_obj(repo, b"a")
260 oid_b = _write_obj(repo, b"b")
261 _commit(repo, "init", {"a.py": oid_a, "b.py": oid_b})
262 d = _json_out(_pid(repo, "--json"))
263 assert d["files_changed"] == 2
264
265 def test_json_files_changed_is_int(self, tmp_path: pathlib.Path) -> None:
266 repo = _init_repo(tmp_path)
267 oid = _write_obj(repo, b"x")
268 _commit(repo, "init", {"f.py": oid})
269 d = _json_out(_pid(repo, "--json"))
270 assert isinstance(d["files_changed"], int)
271
272 def test_json_has_stable_field(self, tmp_path: pathlib.Path) -> None:
273 repo = _init_repo(tmp_path)
274 oid = _write_obj(repo, b"x")
275 _commit(repo, "init", {"f.py": oid})
276 d = _json_out(_pid(repo, "--json"))
277 assert "stable" in d
278
279 def test_json_stable_false_by_default(self, tmp_path: pathlib.Path) -> None:
280 repo = _init_repo(tmp_path)
281 oid = _write_obj(repo, b"x")
282 _commit(repo, "init", {"f.py": oid})
283 d = _json_out(_pid(repo, "--json"))
284 assert d["stable"] is False
285
286 def test_json_stable_true_with_flag(self, tmp_path: pathlib.Path) -> None:
287 repo = _init_repo(tmp_path)
288 oid = _write_obj(repo, b"x")
289 _commit(repo, "init", {"f.py": oid})
290 d = _json_out(_pid(repo, "--json", "--stable"))
291 assert d["stable"] is True
292
293 def test_stable_flag_produces_different_patch_id_for_ws_diff(
294 self, tmp_path: pathlib.Path
295 ) -> None:
296 """--stable and no-flag produce different IDs for trailing-whitespace diff."""
297 repo = _init_repo(tmp_path)
298 oid_base = _write_obj(repo, b"x = 1\n")
299 c1 = _commit(repo, "c1", {"f.py": oid_base})
300 oid_ws = _write_obj(repo, b"x = 1 \n")
301 _commit(repo, "c2 ws", {"f.py": oid_ws}, parent=c1)
302 d_normal = _json_out(_pid(repo, "--json"))
303 d_stable = _json_out(_pid(repo, "--json", "--stable"))
304 assert d_normal["patch_id"] != d_stable["patch_id"]
305
306
307 # ---------------------------------------------------------------------------
308 # Existing JSON fields still present [GREEN]
309 # ---------------------------------------------------------------------------
310
311 class TestJsonGreen:
312 def test_commit_id_present(self, tmp_path: pathlib.Path) -> None:
313 repo = _init_repo(tmp_path)
314 oid = _write_obj(repo, b"x")
315 _commit(repo, "init", {"f.py": oid})
316 d = _json_out(_pid(repo, "--json"))
317 assert "commit_id" in d
318
319 def test_patch_id_present(self, tmp_path: pathlib.Path) -> None:
320 repo = _init_repo(tmp_path)
321 oid = _write_obj(repo, b"x")
322 _commit(repo, "init", {"f.py": oid})
323 d = _json_out(_pid(repo, "--json"))
324 assert "patch_id" in d
325
326 def test_patch_id_is_64_hex(self, tmp_path: pathlib.Path) -> None:
327 repo = _init_repo(tmp_path)
328 oid = _write_obj(repo, b"x")
329 _commit(repo, "init", {"f.py": oid})
330 d = _json_out(_pid(repo, "--json"))
331 assert len(d["patch_id"]) == 64
332 assert all(c in "0123456789abcdef" for c in d["patch_id"])
333
334 def test_subject_present(self, tmp_path: pathlib.Path) -> None:
335 repo = _init_repo(tmp_path)
336 oid = _write_obj(repo, b"x")
337 _commit(repo, "feat: hello world", {"f.py": oid})
338 d = _json_out(_pid(repo, "--json"))
339 assert d["subject"] == "feat: hello world"
340
341 def test_commit_id_matches_head(self, tmp_path: pathlib.Path) -> None:
342 repo = _init_repo(tmp_path)
343 oid = _write_obj(repo, b"x")
344 cid = _commit(repo, "init", {"f.py": oid})
345 d = _json_out(_pid(repo, "--json"))
346 assert d["commit_id"] == cid
347
348 def test_same_diff_same_patch_id(self, tmp_path: pathlib.Path) -> None:
349 """Cherry-pick detection: same logical change → same patch_id."""
350 repo = _init_repo(tmp_path)
351 oid_a = _write_obj(repo, b"v1\n")
352 c1 = _commit(repo, "c1", {"f.py": oid_a})
353 oid_b = _write_obj(repo, b"v2\n")
354 _commit(repo, "c2", {"f.py": oid_b}, parent=c1)
355 d1 = _json_out(_pid(repo, "--json"))
356
357 # Second repo with identical diff
358 repo2 = _init_repo(tmp_path / "repo2")
359 _write_obj(repo2, b"v1\n")
360 c1b = _commit(repo2, "c1", {"f.py": oid_a})
361 _write_obj(repo2, b"v2\n")
362 _commit(repo2, "c2 clone", {"f.py": oid_b}, parent=c1b)
363 d2 = _json_out(_pid(repo2, "--json"))
364
365 assert d1["patch_id"] == d2["patch_id"]
366
367 def test_different_diff_different_patch_id(self, tmp_path: pathlib.Path) -> None:
368 repo = _init_repo(tmp_path)
369 oid_a = _write_obj(repo, b"v1\n")
370 c1 = _commit(repo, "c1", {"f.py": oid_a})
371 oid_b = _write_obj(repo, b"v2\n")
372 _commit(repo, "c2", {"f.py": oid_b}, parent=c1)
373 d1 = _json_out(_pid(repo, "--json"))
374
375 repo2 = _init_repo(tmp_path / "repo2")
376 _write_obj(repo2, b"v1\n")
377 c1b = _commit(repo2, "c1", {"f.py": oid_a})
378 oid_c = _write_obj(repo2, b"completely different content\n")
379 _commit(repo2, "c2 different", {"f.py": oid_c}, parent=c1b)
380 d2 = _json_out(_pid(repo2, "--json"))
381
382 assert d1["patch_id"] != d2["patch_id"]
383
384 def test_explicit_commit_id_ref(self, tmp_path: pathlib.Path) -> None:
385 repo = _init_repo(tmp_path)
386 oid = _write_obj(repo, b"x")
387 cid = _commit(repo, "init", {"f.py": oid})
388 d = _json_out(_pid(repo, cid, "--json"))
389 assert d["commit_id"] == cid
390
391 def test_branch_name_ref(self, tmp_path: pathlib.Path) -> None:
392 repo = _init_repo(tmp_path)
393 oid = _write_obj(repo, b"x")
394 _commit(repo, "init", {"f.py": oid})
395 d = _json_out(_pid(repo, "main", "--json"))
396 assert "patch_id" in d
397
398
399 # ---------------------------------------------------------------------------
400 # Text output format [GREEN]
401 # ---------------------------------------------------------------------------
402
403 class TestTextOutput:
404 def test_text_format_two_parts(self, tmp_path: pathlib.Path) -> None:
405 repo = _init_repo(tmp_path)
406 oid = _write_obj(repo, b"x")
407 _commit(repo, "init", {"f.py": oid})
408 r = _pid(repo)
409 assert r.exit_code == 0
410 parts = r.output.strip().split()
411 assert len(parts) == 2
412
413 def test_text_patch_id_is_hex(self, tmp_path: pathlib.Path) -> None:
414 repo = _init_repo(tmp_path)
415 oid = _write_obj(repo, b"x")
416 _commit(repo, "init", {"f.py": oid})
417 parts = _pid(repo).output.strip().split()
418 assert len(parts[0]) == 64
419 assert all(c in "0123456789abcdef" for c in parts[0])
420
421 def test_text_commit_id_matches_json(self, tmp_path: pathlib.Path) -> None:
422 repo = _init_repo(tmp_path)
423 oid = _write_obj(repo, b"x")
424 _commit(repo, "init", {"f.py": oid})
425 text_parts = _pid(repo).output.strip().split()
426 json_d = _json_out(_pid(repo, "--json"))
427 assert text_parts[1] == json_d["commit_id"]
428 assert text_parts[0] == json_d["patch_id"]
429
430
431 # ---------------------------------------------------------------------------
432 # files_changed correctness [RED]
433 # ---------------------------------------------------------------------------
434
435 class TestFilesChanged:
436 def test_initial_commit_all_files_counted(self, tmp_path: pathlib.Path) -> None:
437 repo = _init_repo(tmp_path)
438 oid_a = _write_obj(repo, b"a")
439 oid_b = _write_obj(repo, b"b")
440 oid_c = _write_obj(repo, b"c")
441 _commit(repo, "init", {"a.py": oid_a, "b.py": oid_b, "c.py": oid_c})
442 d = _json_out(_pid(repo, "--json"))
443 assert d["files_changed"] == 3
444
445 def test_deletion_counted(self, tmp_path: pathlib.Path) -> None:
446 repo = _init_repo(tmp_path)
447 oid = _write_obj(repo, b"gone")
448 c1 = _commit(repo, "c1", {"old.py": oid})
449 _commit(repo, "c2 delete", {}, parent=c1)
450 d = _json_out(_pid(repo, "--json"))
451 assert d["files_changed"] == 1
452
453 def test_modification_counted(self, tmp_path: pathlib.Path) -> None:
454 repo = _init_repo(tmp_path)
455 oid_v1 = _write_obj(repo, b"v1")
456 c1 = _commit(repo, "c1", {"f.py": oid_v1})
457 oid_v2 = _write_obj(repo, b"v2")
458 _commit(repo, "c2 mod", {"f.py": oid_v2}, parent=c1)
459 d = _json_out(_pid(repo, "--json"))
460 assert d["files_changed"] == 1
461
462 def test_unchanged_files_not_counted(self, tmp_path: pathlib.Path) -> None:
463 repo = _init_repo(tmp_path)
464 oid_keep = _write_obj(repo, b"keep")
465 oid_chg = _write_obj(repo, b"v1")
466 c1 = _commit(repo, "c1", {"keep.py": oid_keep, "chg.py": oid_chg})
467 oid_chg2 = _write_obj(repo, b"v2")
468 _commit(repo, "c2", {"keep.py": oid_keep, "chg.py": oid_chg2}, parent=c1)
469 d = _json_out(_pid(repo, "--json"))
470 assert d["files_changed"] == 1
471
472 def test_no_op_commit_zero_files_changed(self, tmp_path: pathlib.Path) -> None:
473 repo = _init_repo(tmp_path)
474 oid = _write_obj(repo, b"same")
475 c1 = _commit(repo, "c1", {"f.py": oid})
476 _commit(repo, "c2 noop", {"f.py": oid}, parent=c1)
477 d = _json_out(_pid(repo, "--json"))
478 assert d["files_changed"] == 0
479
480
481 # ---------------------------------------------------------------------------
482 # Error paths [GREEN]
483 # ---------------------------------------------------------------------------
484
485 class TestErrors:
486 def test_empty_repo_exits_nonzero(self, tmp_path: pathlib.Path) -> None:
487 repo = _init_repo(tmp_path)
488 r = _pid(repo, "--json")
489 assert r.exit_code != 0
490
491 def test_bad_ref_exits_nonzero(self, tmp_path: pathlib.Path) -> None:
492 repo = _init_repo(tmp_path)
493 oid = _write_obj(repo, b"x")
494 _commit(repo, "init", {"f.py": oid})
495 r = _pid(repo, "no-such-ref", "--json")
496 assert r.exit_code != 0
497
498 def test_no_traceback_on_bad_ref(self, tmp_path: pathlib.Path) -> None:
499 repo = _init_repo(tmp_path)
500 oid = _write_obj(repo, b"x")
501 _commit(repo, "init", {"f.py": oid})
502 r = _pid(repo, "no-such-ref")
503 assert "Traceback" not in r.output
504 assert "Traceback" not in r.stderr
505
506 def test_error_to_stderr_not_stdout(self, tmp_path: pathlib.Path) -> None:
507 repo = _init_repo(tmp_path)
508 r = _pid(repo, "--json")
509 assert r.exit_code != 0
510 assert "❌" in r.stderr or r.exit_code != 0
511
512
513 # ---------------------------------------------------------------------------
514 # Security [GREEN]
515 # ---------------------------------------------------------------------------
516
517 class TestSecurity:
518 def test_ansi_in_ref_rejected(self, tmp_path: pathlib.Path) -> None:
519 repo = _init_repo(tmp_path)
520 oid = _write_obj(repo, b"x")
521 _commit(repo, "init", {"f.py": oid})
522 assert _pid(repo, "\x1b[31mbad\x1b[0m").exit_code != 0
523
524 def test_null_byte_in_ref_rejected(self, tmp_path: pathlib.Path) -> None:
525 repo = _init_repo(tmp_path)
526 oid = _write_obj(repo, b"x")
527 _commit(repo, "init", {"f.py": oid})
528 assert _pid(repo, "main\x00evil").exit_code != 0
529
530 def test_path_traversal_in_ref_rejected(self, tmp_path: pathlib.Path) -> None:
531 repo = _init_repo(tmp_path)
532 oid = _write_obj(repo, b"x")
533 _commit(repo, "init", {"f.py": oid})
534 assert _pid(repo, "../../etc/passwd").exit_code != 0
535
536 def test_very_long_ref_rejected(self, tmp_path: pathlib.Path) -> None:
537 repo = _init_repo(tmp_path)
538 oid = _write_obj(repo, b"x")
539 _commit(repo, "init", {"f.py": oid})
540 assert _pid(repo, "a" * 300).exit_code != 0
541
542 def test_no_traceback_on_ansi_ref(self, tmp_path: pathlib.Path) -> None:
543 repo = _init_repo(tmp_path)
544 oid = _write_obj(repo, b"x")
545 _commit(repo, "init", {"f.py": oid})
546 r = _pid(repo, "\x1b[31mbad\x1b[0m")
547 assert "Traceback" not in r.output
548 assert "Traceback" not in r.stderr
549
550
551 # ---------------------------------------------------------------------------
552 # Data integrity [GREEN]
553 # ---------------------------------------------------------------------------
554
555 class TestDataIntegrity:
556 def test_patch_id_changes_when_content_changes(self, tmp_path: pathlib.Path) -> None:
557 repo = _init_repo(tmp_path)
558 oid_v1 = _write_obj(repo, b"version 1\n")
559 c1 = _commit(repo, "c1", {"f.py": oid_v1})
560 oid_v2 = _write_obj(repo, b"version 2\n")
561 _commit(repo, "c2", {"f.py": oid_v2}, parent=c1)
562 d1 = _json_out(_pid(repo, c1, "--json"))
563
564 # Change HEAD to c2 by making another commit
565 oid_v3 = _write_obj(repo, b"version 3\n")
566 c3 = _commit(repo, "c3", {"f.py": oid_v3}, parent=c1)
567 # re-point HEAD ref directly (two different commits from same parent)
568 (repo / ".muse" / "refs" / "heads" / "main").write_text(c3)
569 d3 = _json_out(_pid(repo, "--json"))
570 assert d1["patch_id"] != d3["patch_id"]
571
572 def test_adding_file_changes_patch_id(self, tmp_path: pathlib.Path) -> None:
573 repo = _init_repo(tmp_path)
574 oid_a = _write_obj(repo, b"a\n")
575 c1 = _commit(repo, "c1", {"a.py": oid_a})
576 oid_b = _write_obj(repo, b"b\n")
577 _commit(repo, "c2 add b", {"a.py": oid_a, "b.py": oid_b}, parent=c1)
578 d = _json_out(_pid(repo, "--json"))
579 assert d["files_changed"] == 1
580 assert d["patch_id"] is not None
581
582 def test_patch_id_stable_vs_unstable_differ_for_ws(self, tmp_path: pathlib.Path) -> None:
583 repo = _init_repo(tmp_path)
584 oid_base = _write_obj(repo, b"x = 1\n")
585 c1 = _commit(repo, "c1", {"f.py": oid_base})
586 oid_ws = _write_obj(repo, b"x = 1 \n")
587 _commit(repo, "c2", {"f.py": oid_ws}, parent=c1)
588 d_normal = _json_out(_pid(repo, "--json"))
589 d_stable = _json_out(_pid(repo, "--json", "--stable"))
590 assert d_normal["patch_id"] != d_stable["patch_id"]
591
592
593 # ---------------------------------------------------------------------------
594 # Performance [GREEN]
595 # ---------------------------------------------------------------------------
596
597 class TestPerformance:
598 def test_duration_ms_under_two_seconds(self, tmp_path: pathlib.Path) -> None:
599 repo = _init_repo(tmp_path)
600 manifest: dict[str, str] = {}
601 for i in range(20):
602 content = f"# module {i}\n" .encode() * 50
603 oid = _write_obj(repo, content)
604 manifest[f"src/file_{i:02d}.py"] = oid
605 _commit(repo, "feat: 20 files", manifest)
606 d = _json_out(_pid(repo, "--json"))
607 assert d["duration_ms"] < 2000.0
608
609 def test_duration_ms_non_negative(self, tmp_path: pathlib.Path) -> None:
610 repo = _init_repo(tmp_path)
611 oid = _write_obj(repo, b"x")
612 _commit(repo, "init", {"f.py": oid})
613 d = _json_out(_pid(repo, "--json"))
614 assert d["duration_ms"] >= 0.0
615
616
617 # ---------------------------------------------------------------------------
618 # Stress [GREEN]
619 # ---------------------------------------------------------------------------
620
621 class TestStress:
622 def test_10_distinct_commits_10_distinct_patch_ids(self, tmp_path: pathlib.Path) -> None:
623 repo = _init_repo(tmp_path)
624 patch_ids: set[str] = set()
625 parent: str | None = None
626 for i in range(10):
627 content = f"value = {i}\n".encode()
628 oid = _write_obj(repo, content)
629 cid = _commit(repo, f"c{i}", {"file.py": oid}, parent=parent)
630 d = _json_out(_pid(repo, cid, "--json"))
631 patch_ids.add(d["patch_id"])
632 parent = cid
633 assert len(patch_ids) == 10
634
635 def test_50_file_commit(self, tmp_path: pathlib.Path) -> None:
636 repo = _init_repo(tmp_path)
637 manifest: dict[str, str] = {}
638 for i in range(50):
639 oid = _write_obj(repo, f"file {i}\n".encode() * 20)
640 manifest[f"f{i:03d}.py"] = oid
641 _commit(repo, "feat: 50 files", manifest)
642 r = _pid(repo, "--json")
643 assert r.exit_code == 0
644 d = _json_out(r)
645 assert d["files_changed"] == 50
File History 1 commit
sha256:88ac91129873e6a496e9189515aa690eb893ae25d69c8f72af141a2be5068eb3 docs: docstring sprint contract→find-symbol — idiomatic run… Sonnet 4.6 patch 143 days ago