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