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