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