gabriel / muse public
test_cmd_type.py python
664 lines 23.5 KB
Raw
sha256:81ae324db5ad375fbfe4834c6fcb378312cafad3cc92dec5d3e5c427306621a2 fix: remove commit_exists filter from have anchors — server… Sonnet 4.6 patch 21 days ago
1 """End-to-end CLI tests for ``muse code type``.
2
3 Coverage:
4 - Default health report: text and JSON on a minimal repo.
5 - --any-blast-radius: finds callers up to depth 2.
6 - --drift: across 5 commits shows coverage trend.
7 - --migration-targets: top-5 ranked correctly.
8 - --diff HEAD~1: detects widened signature.
9 - --file: filter restricts output.
10 - --json: output is valid JSON with all required keys.
11 - Missing repo exits non-zero.
12 - Depth cap respected (no infinite BFS).
13 - Stress: --drift over 100 commits completes in < 10 s.
14 """
15
16 from __future__ import annotations
17
18 import datetime
19 import json
20 import pathlib
21 import time
22
23 import pytest
24 from muse.core.types import blob_id
25 from muse.core.object_store import write_object as _write_object_store
26
27 from tests.cli_test_helper import CliRunner
28 from muse.core.paths import muse_dir
29
30 runner = CliRunner()
31 cli = None
32
33
34 # ---------------------------------------------------------------------------
35 # Helpers
36 # ---------------------------------------------------------------------------
37
38
39 def _env(root: pathlib.Path) -> Manifest:
40 return {"MUSE_REPO_ROOT": str(root)}
41
42
43 def _write_object(root: pathlib.Path, content: bytes) -> str:
44 oid = blob_id(content)
45 _write_object_store(root, oid, content)
46 return oid
47
48
49 def _make_repo(
50 tmp: pathlib.Path,
51 *,
52 src: bytes | None = None,
53 branch: str = "main",
54 repo_id: str = "test-type-repo",
55 ) -> pathlib.Path:
56 """Minimal one-commit repo with a single Python source file."""
57 from muse.core.ids import hash_commit, hash_snapshot
58 from muse.core.commits import (
59 CommitRecord,
60 write_commit,
61 )
62 from muse.core.snapshots import (
63 SnapshotRecord,
64 write_snapshot,
65 )
66
67 if src is None:
68 src = (
69 b"def add(x: int, y: int) -> int:\n"
70 b" return x + y\n"
71 b"\n"
72 b"def untyped(a, b):\n"
73 b" return a + b\n"
74 )
75
76 dot_muse = muse_dir(tmp)
77 dot_muse.mkdir(exist_ok=True)
78 (dot_muse / "repo.json").write_text(f'{{"repo_id": "{repo_id}", "name": "test"}}')
79
80 oid = _write_object(tmp, src)
81 (tmp / "sample.py").write_bytes(src)
82
83 manifest: Manifest = {"sample.py": oid}
84 snap_id = hash_snapshot(manifest)
85 write_snapshot(tmp, SnapshotRecord(snapshot_id=snap_id, manifest=manifest))
86
87 committed_at = datetime.datetime(2026, 3, 26, tzinfo=datetime.timezone.utc)
88 commit_id = hash_commit(
89 parent_ids=[],
90 snapshot_id=snap_id,
91 message="initial",
92 committed_at_iso=committed_at.isoformat(),
93 author="test",
94 )
95 commit = CommitRecord(
96 commit_id=commit_id,
97 branch=branch,
98 snapshot_id=snap_id,
99 message="initial",
100 committed_at=committed_at,
101 author="test",
102 )
103 write_commit(tmp, commit)
104
105 refs = dot_muse / "refs" / "heads"
106 refs.mkdir(parents=True, exist_ok=True)
107 (refs / branch).write_text(commit_id)
108 (dot_muse / "HEAD").write_text(f"ref: refs/heads/{branch}\n")
109
110 return tmp
111
112
113 def _make_multi_commit_repo(
114 tmp: pathlib.Path,
115 srcs: list[bytes],
116 branch: str = "main",
117 repo_id: str = "drift-repo",
118 ) -> pathlib.Path:
119 """Repo with multiple sequential commits, each replacing sample.py."""
120 from muse.core.ids import hash_commit, hash_snapshot
121 from muse.core.commits import (
122 CommitRecord,
123 write_commit,
124 )
125 from muse.core.snapshots import (
126 SnapshotRecord,
127 write_snapshot,
128 )
129
130 dot_muse = muse_dir(tmp)
131 dot_muse.mkdir(exist_ok=True)
132 (dot_muse / "repo.json").write_text(f'{{"repo_id": "{repo_id}", "name": "test"}}')
133 refs = dot_muse / "refs" / "heads"
134 refs.mkdir(parents=True, exist_ok=True)
135
136 parent_ids: list[str] = []
137 base_time = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
138
139 for i, src in enumerate(srcs):
140 oid = _write_object(tmp, src)
141 manifest: Manifest = {"sample.py": oid}
142 snap_id = hash_snapshot(manifest)
143 write_snapshot(tmp, SnapshotRecord(snapshot_id=snap_id, manifest=manifest))
144
145 committed_at = base_time + datetime.timedelta(days=i)
146 msg = f"commit {i}"
147 commit_id = hash_commit(
148 parent_ids=parent_ids,
149 snapshot_id=snap_id,
150 message=msg,
151 committed_at_iso=committed_at.isoformat(),
152 author="test",
153 )
154 commit = CommitRecord(
155 commit_id=commit_id,
156 branch=branch,
157 snapshot_id=snap_id,
158 message=msg,
159 committed_at=committed_at,
160 author="test",
161 parent_commit_id=parent_ids[0] if parent_ids else None,
162 )
163 write_commit(tmp, commit)
164 parent_ids = [commit_id]
165
166 (refs / branch).write_text(parent_ids[-1])
167 (dot_muse / "HEAD").write_text(f"ref: refs/heads/{branch}\n")
168 (tmp / "sample.py").write_bytes(srcs[-1])
169 return tmp
170
171
172 # ---------------------------------------------------------------------------
173 # Fixtures
174 # ---------------------------------------------------------------------------
175
176
177 @pytest.fixture()
178 def repo(tmp_path: pathlib.Path) -> pathlib.Path:
179 return _make_repo(tmp_path)
180
181
182 @pytest.fixture()
183 def empty_repo(tmp_path: pathlib.Path) -> pathlib.Path:
184 dot_muse = muse_dir(tmp_path)
185 dot_muse.mkdir()
186 (dot_muse / "repo.json").write_text('{"repo_id": "empty", "name": "empty"}')
187 refs = dot_muse / "refs" / "heads"
188 refs.mkdir(parents=True)
189 (dot_muse / "HEAD").write_text("ref: refs/heads/main\n")
190 return tmp_path
191
192
193 @pytest.fixture()
194 def no_repo(tmp_path: pathlib.Path) -> pathlib.Path:
195 return tmp_path
196
197
198 # ---------------------------------------------------------------------------
199 # Tests: default health report
200 # ---------------------------------------------------------------------------
201
202
203 class TestHealthReport:
204 def test_exits_zero(self, repo: pathlib.Path) -> None:
205 result = runner.invoke(cli, ["code", "type"], env=_env(repo))
206 assert result.exit_code == 0, result.output
207
208 def test_output_contains_health_header(self, repo: pathlib.Path) -> None:
209 result = runner.invoke(cli, ["code", "type"], env=_env(repo))
210 assert "Type Health" in result.output
211
212 def test_output_shows_coverage(self, repo: pathlib.Path) -> None:
213 result = runner.invoke(cli, ["code", "type"], env=_env(repo))
214 assert "%" in result.output
215
216 def test_json_output_valid(self, repo: pathlib.Path) -> None:
217 result = runner.invoke(cli, ["code", "type", "--json"], env=_env(repo))
218 assert result.exit_code == 0, result.output
219 data = json.loads(result.output)
220 required_keys = {
221 "total_symbols",
222 "fully_typed",
223 "partially_typed",
224 "untyped",
225 "any_count",
226 "coverage_fraction",
227 "symbols",
228 }
229 assert required_keys.issubset(data.keys())
230
231 def test_json_symbols_list(self, repo: pathlib.Path) -> None:
232 result = runner.invoke(cli, ["code", "type", "--json"], env=_env(repo))
233 data = json.loads(result.output)
234 assert isinstance(data["symbols"], list)
235 # Our fixture has 2 functions: add (typed) and untyped (not typed)
236 assert data["total_symbols"] == 2
237
238 def test_json_coverage_between_0_and_1(self, repo: pathlib.Path) -> None:
239 result = runner.invoke(cli, ["code", "type", "--json"], env=_env(repo))
240 data = json.loads(result.output)
241 assert 0.0 <= data["coverage_fraction"] <= 1.0
242
243 def test_file_filter_restricts_output(self, tmp_path: pathlib.Path) -> None:
244 src = b"def fn(x: int) -> int:\n return x\n"
245 _make_repo(tmp_path, src=src)
246 # Filter to "other/" which doesn't match "sample.py"
247 result = runner.invoke(
248 cli, ["code", "type", "--file", "other/", "--json"], env=_env(tmp_path)
249 )
250 assert result.exit_code == 0
251 data = json.loads(result.output)
252 assert data["total_symbols"] == 0
253
254 def test_no_repo_exits_nonzero(self, no_repo: pathlib.Path) -> None:
255 result = runner.invoke(cli, ["code", "type"], env=_env(no_repo))
256 assert result.exit_code != 0
257
258 def test_empty_repo_no_head_snapshot(self, empty_repo: pathlib.Path) -> None:
259 result = runner.invoke(cli, ["code", "type"], env=_env(empty_repo))
260 # Should exit non-zero with an informative message
261 assert result.exit_code != 0
262 assert "No snapshot" in result.stderr or "snapshot" in result.stderr.lower()
263
264
265 # ---------------------------------------------------------------------------
266 # Tests: --any-blast-radius
267 # ---------------------------------------------------------------------------
268
269
270 class TestAnyBlastRadius:
271 def test_any_return_exits_nonzero_when_callers_found(
272 self, tmp_path: pathlib.Path
273 ) -> None:
274 src = b"""\
275 import typing
276 from muse.core.paths import muse_dir
277 def load() -> typing.Any:
278 return {}
279
280 def process():
281 return load()
282 """
283 _make_repo(tmp_path, src=src)
284 result = runner.invoke(
285 cli,
286 ["code", "type", "--any-blast-radius", "sample.py::load"],
287 env=_env(tmp_path),
288 )
289 # load() has Any return AND has a caller (process), so exit non-zero
290 assert result.exit_code != 0
291
292 def test_no_any_exits_zero(self, repo: pathlib.Path) -> None:
293 # "add" is fully typed with no Any — blast radius is empty → exit 0
294 result = runner.invoke(
295 cli,
296 ["code", "type", "--any-blast-radius", "sample.py::add"],
297 env=_env(repo),
298 )
299 assert result.exit_code == 0
300
301 def test_json_output_has_nodes_key(self, tmp_path: pathlib.Path) -> None:
302 src = b"import typing\ndef f() -> typing.Any:\n return {}\n"
303 _make_repo(tmp_path, src=src)
304 result = runner.invoke(
305 cli,
306 ["code", "type", "--any-blast-radius", "sample.py::f", "--json"],
307 env=_env(tmp_path),
308 )
309 data = json.loads(result.output)
310 assert "nodes" in data
311 assert "address" in data
312
313 def test_depth_flag_accepted(self, repo: pathlib.Path) -> None:
314 result = runner.invoke(
315 cli,
316 ["code", "type", "--any-blast-radius", "sample.py::add", "--depth", "3"],
317 env=_env(repo),
318 )
319 # Should not raise — depth=3 is valid
320 assert result.exit_code == 0
321
322 def test_depth_over_max_capped(self, tmp_path: pathlib.Path) -> None:
323 src = b"from typing import Any\ndef f(x: Any) -> None:\n pass\n"
324 _make_repo(tmp_path, src=src)
325 result = runner.invoke(
326 cli,
327 ["code", "type", "--any-blast-radius", "sample.py::f", "--depth", "999"],
328 env=_env(tmp_path),
329 )
330 # Should not hang or error due to infinite BFS
331 assert result.exit_code in (0, 1) # 0 if no callers, 1 if callers found
332
333 def test_missing_address_exits_zero(self, repo: pathlib.Path) -> None:
334 result = runner.invoke(
335 cli,
336 ["code", "type", "--any-blast-radius", "sample.py::nonexistent"],
337 env=_env(repo),
338 )
339 assert result.exit_code == 0
340
341 def test_text_output_shows_address(self, tmp_path: pathlib.Path) -> None:
342 src = b"from typing import Any\ndef f(x: Any) -> None:\n pass\n"
343 _make_repo(tmp_path, src=src)
344 result = runner.invoke(
345 cli,
346 ["code", "type", "--any-blast-radius", "sample.py::f"],
347 env=_env(tmp_path),
348 )
349 assert "sample.py::f" in result.output
350
351
352 # ---------------------------------------------------------------------------
353 # Tests: --drift
354 # ---------------------------------------------------------------------------
355
356
357 class TestDrift:
358 def test_drift_on_single_commit(self, repo: pathlib.Path) -> None:
359 result = runner.invoke(cli, ["code", "type", "--drift"], env=_env(repo))
360 assert result.exit_code == 0
361
362 def test_drift_json_has_branch_and_drift(self, repo: pathlib.Path) -> None:
363 result = runner.invoke(
364 cli, ["code", "type", "--drift", "--json"], env=_env(repo)
365 )
366 assert result.exit_code == 0
367 data = json.loads(result.output)
368 assert "branch" in data
369 assert "drift" in data
370 assert isinstance(data["drift"], list)
371
372 def test_drift_json_point_keys(self, repo: pathlib.Path) -> None:
373 result = runner.invoke(
374 cli, ["code", "type", "--drift", "--json"], env=_env(repo)
375 )
376 data = json.loads(result.output)
377 assert len(data["drift"]) >= 1
378 point = data["drift"][0]
379 required = {
380 "commit_id",
381 "committed_at",
382 "message",
383 "coverage_fraction",
384 "any_count",
385 "delta_coverage",
386 }
387 assert required.issubset(point.keys())
388
389 def test_drift_five_commits_coverage_trend(self, tmp_path: pathlib.Path) -> None:
390 """Coverage improves across 5 commits (none typed → fully typed)."""
391 srcs: list[bytes] = [
392 b"def a(x, y): return x\n",
393 b"def a(x: int, y): return x\n",
394 b"def a(x: int, y: int): return x\n",
395 b"def a(x: int, y: int) -> int: return x\n",
396 b"def a(x: int, y: int) -> int:\n return x\n",
397 ]
398 _make_multi_commit_repo(tmp_path, srcs)
399 result = runner.invoke(
400 cli, ["code", "type", "--drift", "--json"], env=_env(tmp_path)
401 )
402 assert result.exit_code == 0
403 data = json.loads(result.output)
404 coverages = [p["coverage_fraction"] for p in data["drift"]]
405 assert len(coverages) == 5
406 # Coverage should be non-decreasing (or at worst plateau)
407 for i in range(1, len(coverages)):
408 assert coverages[i] >= coverages[0] - 0.01 # allow tiny float noise
409
410 def test_drift_since_flag_accepted(self, repo: pathlib.Path) -> None:
411 result = runner.invoke(
412 cli,
413 ["code", "type", "--drift", "--since", "2020-01-01"],
414 env=_env(repo),
415 )
416 assert result.exit_code == 0
417
418 def test_drift_since_invalid_date_exits_nonzero(self, repo: pathlib.Path) -> None:
419 result = runner.invoke(
420 cli,
421 ["code", "type", "--drift", "--since", "not-a-date"],
422 env=_env(repo),
423 )
424 assert result.exit_code != 0
425
426 def test_drift_max_commits_flag(self, repo: pathlib.Path) -> None:
427 result = runner.invoke(
428 cli,
429 ["code", "type", "--drift", "--max-commits", "10"],
430 env=_env(repo),
431 )
432 assert result.exit_code == 0
433
434 def test_drift_text_shows_trend(self, repo: pathlib.Path) -> None:
435 result = runner.invoke(cli, ["code", "type", "--drift"], env=_env(repo))
436 assert "Type Coverage Drift" in result.output or "Coverage" in result.output
437
438
439 # ---------------------------------------------------------------------------
440 # Tests: --migration-targets
441 # ---------------------------------------------------------------------------
442
443
444 class TestMigrationTargets:
445 def test_exits_zero(self, repo: pathlib.Path) -> None:
446 result = runner.invoke(
447 cli, ["code", "type", "--migration-targets"], env=_env(repo)
448 )
449 assert result.exit_code == 0
450
451 def test_json_has_targets_key(self, repo: pathlib.Path) -> None:
452 result = runner.invoke(
453 cli, ["code", "type", "--migration-targets", "--json"], env=_env(repo)
454 )
455 assert result.exit_code == 0
456 data = json.loads(result.output)
457 assert "targets" in data
458 assert isinstance(data["targets"], list)
459
460 def test_untyped_fn_appears_in_targets(self, repo: pathlib.Path) -> None:
461 result = runner.invoke(
462 cli, ["code", "type", "--migration-targets", "--json"], env=_env(repo)
463 )
464 data = json.loads(result.output)
465 addresses = [t["address"] for t in data["targets"]]
466 # "sample.py::untyped" should appear since it has no annotations
467 assert any("untyped" in addr for addr in addresses)
468
469 def test_fully_typed_repo_shows_no_targets(self, tmp_path: pathlib.Path) -> None:
470 src = b"def fn(x: int, y: str) -> float:\n return float(x)\n"
471 _make_repo(tmp_path, src=src)
472 result = runner.invoke(
473 cli, ["code", "type", "--migration-targets", "--json"], env=_env(tmp_path)
474 )
475 data = json.loads(result.output)
476 assert data["targets"] == []
477
478 def test_top_n_limits_output(self, tmp_path: pathlib.Path) -> None:
479 fns = b"\n".join([f"def fn{i}(x, y): pass".encode() for i in range(20)])
480 _make_repo(tmp_path, src=fns)
481 result = runner.invoke(
482 cli,
483 ["code", "type", "--migration-targets", "--top", "5", "--json"],
484 env=_env(tmp_path),
485 )
486 data = json.loads(result.output)
487 assert len(data["targets"]) <= 5
488
489 def test_targets_sorted_by_priority(self, repo: pathlib.Path) -> None:
490 result = runner.invoke(
491 cli, ["code", "type", "--migration-targets", "--json"], env=_env(repo)
492 )
493 data = json.loads(result.output)
494 scores = [t["priority_score"] for t in data["targets"]]
495 assert scores == sorted(scores, reverse=True)
496
497 def test_target_has_required_keys(self, repo: pathlib.Path) -> None:
498 result = runner.invoke(
499 cli, ["code", "type", "--migration-targets", "--json"], env=_env(repo)
500 )
501 data = json.loads(result.output)
502 if data["targets"]:
503 t = data["targets"][0]
504 required = {"address", "caller_count", "type_score", "priority_score"}
505 assert required.issubset(t.keys())
506
507
508 # ---------------------------------------------------------------------------
509 # Tests: --diff REF
510 # ---------------------------------------------------------------------------
511
512
513 class TestDiff:
514 def _make_two_commit_repo(
515 self,
516 tmp: pathlib.Path,
517 src_a: bytes,
518 src_b: bytes,
519 ) -> pathlib.Path:
520 return _make_multi_commit_repo(tmp, [src_a, src_b])
521
522 def test_identical_snapshots_exit_zero(self, tmp_path: pathlib.Path) -> None:
523 src = b"def fn(x: int) -> int:\n return x\n"
524 _make_multi_commit_repo(tmp_path, [src, src])
525 result = runner.invoke(
526 cli, ["code", "type", "--diff", "HEAD~1"], env=_env(tmp_path)
527 )
528 assert result.exit_code == 0
529
530 def test_widened_signature_exits_nonzero(self, tmp_path: pathlib.Path) -> None:
531 src_a = b"def fn(x: str) -> str:\n return x\n"
532 src_b = b"from typing import Any\ndef fn(x: Any) -> str:\n return str(x)\n"
533 self._make_two_commit_repo(tmp_path, src_a, src_b)
534 result = runner.invoke(
535 cli, ["code", "type", "--diff", "HEAD~1"], env=_env(tmp_path)
536 )
537 # Widened → exit non-zero
538 assert result.exit_code != 0
539
540 def test_narrowed_signature_exits_zero(self, tmp_path: pathlib.Path) -> None:
541 src_a = b"from typing import Any\ndef fn(x: Any) -> str:\n return str(x)\n"
542 src_b = b"def fn(x: str) -> str:\n return x\n"
543 self._make_two_commit_repo(tmp_path, src_a, src_b)
544 result = runner.invoke(
545 cli, ["code", "type", "--diff", "HEAD~1"], env=_env(tmp_path)
546 )
547 # Narrowed only → exit zero
548 assert result.exit_code == 0
549
550 def test_json_has_conflicts_key(self, tmp_path: pathlib.Path) -> None:
551 src = b"def fn(x: int) -> int:\n return x\n"
552 _make_multi_commit_repo(tmp_path, [src, src])
553 result = runner.invoke(
554 cli, ["code", "type", "--diff", "HEAD~1", "--json"], env=_env(tmp_path)
555 )
556 assert result.exit_code == 0
557 data = json.loads(result.output)
558 assert "conflicts" in data
559 assert "diff_ref" in data
560
561 def test_conflict_has_required_keys(self, tmp_path: pathlib.Path) -> None:
562 src_a = b"def fn(x: str) -> str:\n return x\n"
563 src_b = b"from typing import Any\ndef fn(x: Any) -> str:\n return str(x)\n"
564 self._make_two_commit_repo(tmp_path, src_a, src_b)
565 result = runner.invoke(
566 cli, ["code", "type", "--diff", "HEAD~1", "--json"], env=_env(tmp_path)
567 )
568 data = json.loads(result.output)
569 if data["conflicts"]:
570 c = data["conflicts"][0]
571 required = {"address", "signature_a", "signature_b", "change_kind"}
572 assert required.issubset(c.keys())
573
574 def test_invalid_ref_exits_nonzero(self, repo: pathlib.Path) -> None:
575 result = runner.invoke(
576 cli, ["code", "type", "--diff", "HEAD~999"], env=_env(repo)
577 )
578 assert result.exit_code != 0
579
580 def test_text_output_shows_type_diff_header(self, tmp_path: pathlib.Path) -> None:
581 src = b"def fn(x: int) -> int:\n return x\n"
582 _make_multi_commit_repo(tmp_path, [src, src])
583 result = runner.invoke(
584 cli, ["code", "type", "--diff", "HEAD~1"], env=_env(tmp_path)
585 )
586 assert "Type Diff" in result.output or "HEAD" in result.output
587
588
589 # ---------------------------------------------------------------------------
590 # Stress test
591 # ---------------------------------------------------------------------------
592
593
594 class TestStress:
595 def test_drift_100_commits_under_10_seconds(self, tmp_path: pathlib.Path) -> None:
596 """--drift over 100 commits must complete in under 10 seconds."""
597 from muse.core.ids import hash_commit, hash_snapshot
598 from muse.core.commits import (
599 CommitRecord,
600 write_commit,
601 )
602 from muse.core.snapshots import (
603 SnapshotRecord,
604 write_snapshot,
605 )
606
607 dot_muse = muse_dir(tmp_path)
608 dot_muse.mkdir()
609 repo_id = "stress-drift"
610 (dot_muse / "repo.json").write_text(
611 f'{{"repo_id": "{repo_id}", "name": "stress"}}'
612 )
613 refs = dot_muse / "refs" / "heads"
614 refs.mkdir(parents=True)
615
616 parent_ids: list[str] = []
617 base_time = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
618
619 # Write a single typed Python file once — reuse its object ID across commits.
620 src = b"def fn(x: int, y: int) -> int:\n return x + y\n"
621 oid = _write_object(tmp_path, src)
622
623 for i in range(100):
624 manifest: Manifest = {"sample.py": oid}
625 snap_id = hash_snapshot(manifest)
626 write_snapshot(tmp_path, SnapshotRecord(snapshot_id=snap_id, manifest=manifest))
627
628 committed_at = base_time + datetime.timedelta(days=i)
629 msg = f"commit {i}"
630 commit_id = hash_commit(
631 parent_ids=parent_ids,
632 snapshot_id=snap_id,
633 message=msg,
634 committed_at_iso=committed_at.isoformat(),
635 author="test",
636 )
637 commit = CommitRecord(
638 commit_id=commit_id,
639 branch="main",
640 snapshot_id=snap_id,
641 message=msg,
642 committed_at=committed_at,
643 author="test",
644 parent_commit_id=parent_ids[0] if parent_ids else None,
645 )
646 write_commit(tmp_path, commit)
647 parent_ids = [commit_id]
648
649 (refs / "main").write_text(parent_ids[-1])
650 (dot_muse / "HEAD").write_text("ref: refs/heads/main\n")
651 (tmp_path / "sample.py").write_bytes(src)
652
653 start = time.monotonic()
654 result = runner.invoke(
655 cli,
656 ["code", "type", "--drift", "--json"],
657 env=_env(tmp_path),
658 )
659 elapsed = time.monotonic() - start
660
661 assert result.exit_code == 0, result.output
662 data = json.loads(result.output)
663 assert len(data["drift"]) == 100
664 assert elapsed < 10.0, f"--drift 100 commits took {elapsed:.2f}s — too slow"
File History 4 commits
sha256:81ae324db5ad375fbfe4834c6fcb378312cafad3cc92dec5d3e5c427306621a2 fix: remove commit_exists filter from have anchors — server… Sonnet 4.6 patch 21 days ago
sha256:36c3cb3e76619d4c30a6d9bf81b5ec4ff148e30dcfed913e3114ca7b43b81c7e fix: rename objects→blobs in push client and all stale test… Sonnet 4.6 patch 23 days ago
sha256:c06a9b9b9fee26c68ea725b44d54b2c0a171301ce9de746d5b656617b4463a9a fix: repair four test failures from post-migration audit Sonnet 4.6 patch 29 days ago
sha256:1900655993c83c4107067375548a7be823e471d2515830842f1a12cba4bd3cdf fix: unified object store migration — idempotent writes, JS… Sonnet 4.6 minor 29 days ago