gabriel / muse public
test_stress_code_analytics.py python
655 lines 26.0 KB
Raw
sha256:74b5023693ac2ab80e3b89fddc66e0d60d7d931a1266d3f9294f645c3102fe76 tests/test_lineage_supercharge.py, tests/test_narrative_sup… Human 15 days ago
1 """Stress tests for all history-walking ``muse code`` analytics commands.
2
3 The mega-suite (test_code_commands.py) has comprehensive CLI integration
4 coverage for each command. This file adds the missing stress layer:
5 a large repo with many commits and symbols to verify that each command
6 completes in a reasonable time, emits valid JSON, and handles filters
7 correctly under load.
8
9 Stress budget per command: < 15 s on a standard developer machine.
10
11 Commands covered
12 ----------------
13 muse code hotspots — symbol churn leaderboard
14 muse code stable — symbol stability leaderboard
15 muse code coupling — file-level co-change
16 muse code compare — symbol diff between refs
17 muse code blast-risk — impact risk score
18 muse code age — symbol age / staleness
19 muse code velocity — commit velocity by module
20 muse code entangle — cross-module coupling
21 muse code narrative — prose timeline
22 muse code gravity — call-graph gravity
23 muse code contract — type/call contract inference
24 muse code predict — change prediction
25 muse code semantic-test-coverage — test coverage mapping
26 """
27
28 from __future__ import annotations
29
30 import json
31 import pathlib
32 import textwrap
33 import time
34 from collections.abc import Generator
35
36 import pytest
37
38 from tests.cli_test_helper import CliRunner
39
40 cli = None
41 runner = CliRunner()
42
43 # ── Time budget (generous to avoid flakiness on CI) ─────────────────────────
44
45 _TIME_BUDGET_S: float = 15.0
46
47
48 # ---------------------------------------------------------------------------
49 # Shared large repo fixture — built once per module
50 # ---------------------------------------------------------------------------
51
52
53 @pytest.fixture(scope="module")
54 def large_repo(tmp_path_factory: pytest.TempPathFactory) -> Generator[pathlib.Path, None, None]:
55 """Repo with 30 commits across 6 Python modules (~180 total symbol events).
56
57 Module relationships:
58 billing.py imports from utils.py
59 auth.py imports from utils.py
60 report.py imports from billing.py and auth.py
61 api.py imports from report.py
62 tests/test_billing.py imports from billing.py
63 """
64 tmp_path = tmp_path_factory.mktemp("large_analytics_repo")
65
66 import os
67 _prev_root = os.environ.get("MUSE_REPO_ROOT")
68 _prev_cwd = os.getcwd()
69 os.environ["MUSE_REPO_ROOT"] = str(tmp_path)
70 os.chdir(tmp_path)
71
72 r = runner.invoke(cli, ["init", "--domain", "code"])
73 assert r.exit_code == 0, r.output
74
75 (tmp_path / "tests").mkdir()
76
77 # Commit 0 — scaffold all modules.
78 (tmp_path / "utils.py").write_text(textwrap.dedent("""\
79 def validate(amount: float) -> bool:
80 return amount > 0
81
82 def format_currency(amount: float) -> str:
83 return f"${amount:.2f}"
84 """))
85 (tmp_path / "billing.py").write_text(textwrap.dedent("""\
86 from utils import validate, format_currency
87
88 class Invoice:
89 def compute_total(self, items: list[float]) -> float:
90 return sum(items)
91
92 def apply_discount(self, total: float, rate: float) -> float:
93 return total * (1 - rate)
94
95 def process(invoice: Invoice, items: list[float]) -> str:
96 total = invoice.compute_total(items)
97 return format_currency(total)
98 """))
99 (tmp_path / "auth.py").write_text(textwrap.dedent("""\
100 from utils import validate
101
102 def authenticate(token: str) -> bool:
103 return validate(len(token))
104
105 def authorise(user: str, action: str) -> bool:
106 return bool(user) and bool(action)
107 """))
108 (tmp_path / "report.py").write_text(textwrap.dedent("""\
109 from billing import Invoice, process
110 from auth import authenticate
111
112 def generate_report(user: str, items: list[float]) -> dict[str, str | float]:
113 if not authenticate(user):
114 return {}
115 inv = Invoice()
116 return {"total": inv.compute_total(items), "result": process(inv, items)}
117 """))
118 (tmp_path / "api.py").write_text(textwrap.dedent("""\
119 from report import generate_report
120
121 def handle_request(user: str, payload: list[float]) -> dict[str, str | float]:
122 return generate_report(user, payload)
123
124 def health_check() -> bool:
125 return True
126 """))
127 (tmp_path / "tests" / "test_billing.py").write_text(textwrap.dedent("""\
128 from billing import Invoice, process
129
130 def test_compute_total() -> None:
131 inv = Invoice()
132 assert inv.compute_total([1.0, 2.0]) == 3.0
133
134 def test_apply_discount() -> None:
135 inv = Invoice()
136 assert inv.apply_discount(100.0, 0.1) == 90.0
137
138 def test_process() -> None:
139 inv = Invoice()
140 assert "$" in process(inv, [10.0])
141 """))
142 r = runner.invoke(cli, ["commit", "-m", "scaffold all modules"])
143 assert r.exit_code == 0, r.output
144
145 # Commits 1–9 — iterative modifications to billing.py
146 discounts = [0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45]
147 for i, rate in enumerate(discounts):
148 (tmp_path / "billing.py").write_text(textwrap.dedent(f"""\
149 from utils import validate, format_currency
150
151 class Invoice:
152 def compute_total(self, items: list[float]) -> float:
153 return sum(items)
154
155 def apply_discount(self, total: float, rate: float = {rate}) -> float:
156 return total * (1 - rate)
157
158 def process(invoice: Invoice, items: list[float]) -> str:
159 total = invoice.compute_total(items)
160 if not validate(total):
161 raise ValueError("invalid total")
162 return format_currency(total)
163 """))
164 r = runner.invoke(cli, ["commit", "-m", f"billing: update discount default to {rate}"])
165 assert r.exit_code == 0, r.output
166
167 # Commits 10–14 — iterative modifications to auth.py
168 for i in range(5):
169 (tmp_path / "auth.py").write_text(textwrap.dedent(f"""\
170 from utils import validate
171
172 MIN_TOKEN_LEN: int = {8 + i}
173
174 def authenticate(token: str) -> bool:
175 return validate(len(token) - MIN_TOKEN_LEN)
176
177 def authorise(user: str, action: str) -> bool:
178 return bool(user) and bool(action) and len(user) > {i}
179 """))
180 r = runner.invoke(cli, ["commit", "-m", f"auth: tighten token min length to {8 + i}"])
181 assert r.exit_code == 0, r.output
182
183 # Commits 15–19 — add helpers to utils.py
184 helpers = ["clamp", "round_up", "is_positive", "is_negative", "safe_divide"]
185 utils_lines = [
186 "def validate(amount: float) -> bool:\n return amount > 0\n",
187 "def format_currency(amount: float) -> str:\n return f'${amount:.2f}'\n",
188 ]
189 for i, helper in enumerate(helpers):
190 utils_lines.append(f"def {helper}(x: float) -> float:\n return float(x)\n")
191 (tmp_path / "utils.py").write_text("\n".join(utils_lines))
192 r = runner.invoke(cli, ["commit", "-m", f"utils: add {helper}"])
193 assert r.exit_code == 0, r.output
194
195 # Commits 20–24 — add API endpoints to api.py
196 for i in range(5):
197 (tmp_path / "api.py").write_text(textwrap.dedent(f"""\
198 from report import generate_report
199
200 def handle_request(user: str, payload: list[float]) -> dict[str, str | float]:
201 return generate_report(user, payload)
202
203 def health_check() -> bool:
204 return True
205
206 def endpoint_{i:02d}(x: float) -> float:
207 return float(x)
208 """))
209 r = runner.invoke(cli, ["commit", "-m", f"api: add endpoint_{i:02d}"])
210 assert r.exit_code == 0, r.output
211
212 # Commits 25–29 — modify report.py
213 for i in range(5):
214 (tmp_path / "report.py").write_text(textwrap.dedent(f"""\
215 from billing import Invoice, process
216 from auth import authenticate
217
218 REPORT_VERSION: int = {i + 1}
219
220 def generate_report(user: str, items: list[float]) -> dict[str, str | float | int]:
221 if not authenticate(user):
222 return {{}}
223 inv = Invoice()
224 return {{
225 "version": REPORT_VERSION,
226 "total": inv.compute_total(items),
227 "result": process(inv, items),
228 }}
229 """))
230 r = runner.invoke(cli, ["commit", "-m", f"report: version {i + 1}"])
231 assert r.exit_code == 0, r.output
232
233 yield tmp_path
234
235 # Restore environment so subsequent test modules see a clean state.
236 os.chdir(_prev_cwd)
237 if _prev_root is None:
238 os.environ.pop("MUSE_REPO_ROOT", None)
239 else:
240 os.environ["MUSE_REPO_ROOT"] = _prev_root
241
242
243 # ---------------------------------------------------------------------------
244 # Helper
245 # ---------------------------------------------------------------------------
246
247
248 def _assert_under_budget(elapsed: float, cmd: str) -> None:
249 assert elapsed < _TIME_BUDGET_S, (
250 f"'{cmd}' took {elapsed:.1f}s — exceeds {_TIME_BUDGET_S}s budget"
251 )
252
253
254 # ---------------------------------------------------------------------------
255 # muse code hotspots — stress
256 # ---------------------------------------------------------------------------
257
258
259 class TestHotspotsStress:
260 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
261 result = runner.invoke(cli, ["code", "hotspots"])
262 assert result.exit_code == 0, result.output
263
264 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
265 result = runner.invoke(cli, ["code", "hotspots", "--json"])
266 assert result.exit_code == 0, result.output
267 data = json.loads(result.output)
268 assert "hotspots" in data
269 assert "commits_analysed" in data
270
271 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
272 start = time.monotonic()
273 runner.invoke(cli, ["code", "hotspots", "--json"])
274 _assert_under_budget(time.monotonic() - start, "hotspots --json")
275
276 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
277 result = runner.invoke(cli, ["code", "hotspots", "--top", "5", "--json"])
278 assert result.exit_code == 0
279 data = json.loads(result.output)
280 assert len(data["hotspots"]) <= 5
281
282 def test_kind_filter(self, large_repo: pathlib.Path) -> None:
283 result = runner.invoke(cli, ["code", "hotspots", "--kind", "function", "--json"])
284 assert result.exit_code == 0
285 data = json.loads(result.output)
286 for h in data["hotspots"]:
287 assert h.get("kind") in (None, "function", "method")
288
289 def test_min_changes_filter(self, large_repo: pathlib.Path) -> None:
290 result = runner.invoke(cli, ["code", "hotspots", "--min", "5", "--json"])
291 assert result.exit_code == 0
292
293 def test_from_to_range(self, large_repo: pathlib.Path) -> None:
294 result = runner.invoke(cli, ["code", "hotspots", "--from", "HEAD~10", "--to", "HEAD", "--json"])
295 assert result.exit_code == 0
296
297
298 # ---------------------------------------------------------------------------
299 # muse code stable — stress
300 # ---------------------------------------------------------------------------
301
302
303 class TestStableStress:
304 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
305 result = runner.invoke(cli, ["code", "stable"])
306 assert result.exit_code == 0, result.output
307
308 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
309 result = runner.invoke(cli, ["code", "stable", "--json"])
310 assert result.exit_code == 0, result.output
311 data = json.loads(result.output)
312 assert "stable" in data
313 assert "commits_analysed" in data
314
315 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
316 start = time.monotonic()
317 runner.invoke(cli, ["code", "stable", "--json"])
318 _assert_under_budget(time.monotonic() - start, "stable --json")
319
320 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
321 result = runner.invoke(cli, ["code", "stable", "--top", "3", "--json"])
322 assert result.exit_code == 0
323 data = json.loads(result.output)
324 assert len(data["stable"]) <= 3
325
326 def test_kind_filter_function(self, large_repo: pathlib.Path) -> None:
327 result = runner.invoke(cli, ["code", "stable", "--kind", "function", "--json"])
328 assert result.exit_code == 0
329
330
331 # ---------------------------------------------------------------------------
332 # muse code coupling — stress
333 # ---------------------------------------------------------------------------
334
335
336 class TestCouplingStress:
337 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
338 result = runner.invoke(cli, ["code", "coupling"])
339 assert result.exit_code == 0, result.output
340
341 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
342 result = runner.invoke(cli, ["code", "coupling", "--json"])
343 assert result.exit_code == 0
344 data = json.loads(result.output)
345 assert "pairs" in data
346
347 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
348 start = time.monotonic()
349 runner.invoke(cli, ["code", "coupling", "--json"])
350 _assert_under_budget(time.monotonic() - start, "coupling --json")
351
352 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
353 result = runner.invoke(cli, ["code", "coupling", "--top", "5", "--json"])
354 assert result.exit_code == 0
355 data = json.loads(result.output)
356 assert len(data["pairs"]) <= 5
357
358 def test_min_filter(self, large_repo: pathlib.Path) -> None:
359 result = runner.invoke(cli, ["code", "coupling", "--min", "2", "--json"])
360 assert result.exit_code == 0
361 data = json.loads(result.output)
362 for c in data["pairs"]:
363 assert c["count"] >= 2
364
365
366 # ---------------------------------------------------------------------------
367 # muse code compare — stress
368 # ---------------------------------------------------------------------------
369
370
371 class TestCompareStress:
372 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
373 result = runner.invoke(cli, ["code", "compare", "HEAD~5", "HEAD"])
374 assert result.exit_code == 0, result.output
375
376 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
377 result = runner.invoke(cli, ["code", "compare", "HEAD~5", "HEAD", "--json"])
378 assert result.exit_code == 0
379 data = json.loads(result.output)
380 # compare returns from/to refs and an ops list (or stat breakdown).
381 assert "from" in data or "ops" in data or "stat" in data
382
383 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
384 start = time.monotonic()
385 runner.invoke(cli, ["code", "compare", "HEAD~10", "HEAD", "--json"])
386 _assert_under_budget(time.monotonic() - start, "compare HEAD~10 HEAD")
387
388 def test_kind_filter_function(self, large_repo: pathlib.Path) -> None:
389 result = runner.invoke(cli, [
390 "code", "compare", "HEAD~5", "HEAD", "--kind", "function", "--json",
391 ])
392 assert result.exit_code == 0
393
394 def test_same_refs_empty_diff(self, large_repo: pathlib.Path) -> None:
395 result = runner.invoke(cli, ["code", "compare", "HEAD", "HEAD", "--json"])
396 assert result.exit_code == 0
397 data = json.loads(result.output)
398 # With identical refs, ops list should be empty and stat should show 0 changes.
399 ops = data.get("ops", [])
400 assert len(ops) == 0
401
402
403 # ---------------------------------------------------------------------------
404 # muse code blast-risk — stress
405 # ---------------------------------------------------------------------------
406
407
408 class TestBlastRiskStress:
409 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
410 result = runner.invoke(cli, ["code", "blast-risk"])
411 assert result.exit_code == 0, result.output
412
413 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
414 result = runner.invoke(cli, ["code", "blast-risk", "--json"])
415 assert result.exit_code == 0
416 data = json.loads(result.output)
417 assert "symbols" in data
418
419 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
420 start = time.monotonic()
421 runner.invoke(cli, ["code", "blast-risk", "--json"])
422 _assert_under_budget(time.monotonic() - start, "blast-risk --json")
423
424 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
425 result = runner.invoke(cli, ["code", "blast-risk", "--top", "5", "--json"])
426 assert result.exit_code == 0
427 data = json.loads(result.output)
428 assert len(data["symbols"]) <= 5
429
430
431 # ---------------------------------------------------------------------------
432 # muse code age — stress
433 # ---------------------------------------------------------------------------
434
435
436 class TestAgeStress:
437 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
438 result = runner.invoke(cli, ["code", "age"])
439 assert result.exit_code == 0, result.output
440
441 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
442 result = runner.invoke(cli, ["code", "age", "--json"])
443 assert result.exit_code == 0
444 data = json.loads(result.output)
445 assert "symbols" in data
446
447 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
448 start = time.monotonic()
449 runner.invoke(cli, ["code", "age", "--json"])
450 _assert_under_budget(time.monotonic() - start, "age --json")
451
452 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
453 result = runner.invoke(cli, ["code", "age", "--top", "5", "--json"])
454 assert result.exit_code == 0
455 data = json.loads(result.output)
456 assert len(data["symbols"]) <= 5
457
458 def test_kind_filter(self, large_repo: pathlib.Path) -> None:
459 result = runner.invoke(cli, ["code", "age", "--kind", "function", "--json"])
460 assert result.exit_code == 0
461
462
463 # ---------------------------------------------------------------------------
464 # muse code velocity — stress
465 # ---------------------------------------------------------------------------
466
467
468 class TestVelocityStress:
469 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
470 result = runner.invoke(cli, ["code", "velocity"])
471 assert result.exit_code == 0, result.output
472
473 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
474 result = runner.invoke(cli, ["code", "velocity", "--json"])
475 assert result.exit_code == 0
476 data = json.loads(result.output)
477 assert "modules" in data
478
479 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
480 start = time.monotonic()
481 runner.invoke(cli, ["code", "velocity", "--json"])
482 _assert_under_budget(time.monotonic() - start, "velocity --json")
483
484 def test_window_flag(self, large_repo: pathlib.Path) -> None:
485 result = runner.invoke(cli, ["code", "velocity", "--window", "10", "--json"])
486 assert result.exit_code == 0
487
488
489 # ---------------------------------------------------------------------------
490 # muse code entangle — stress
491 # ---------------------------------------------------------------------------
492
493
494 class TestEntangleStress:
495 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
496 result = runner.invoke(cli, ["code", "entangle"])
497 assert result.exit_code == 0, result.output
498
499 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
500 result = runner.invoke(cli, ["code", "entangle", "--json"])
501 assert result.exit_code == 0
502 data = json.loads(result.output)
503 assert "pairs" in data
504
505 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
506 start = time.monotonic()
507 runner.invoke(cli, ["code", "entangle", "--json"])
508 _assert_under_budget(time.monotonic() - start, "entangle --json")
509
510 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
511 result = runner.invoke(cli, ["code", "entangle", "--top", "5", "--json"])
512 assert result.exit_code == 0
513 data = json.loads(result.output)
514 assert len(data["pairs"]) <= 5
515
516
517 # ---------------------------------------------------------------------------
518 # muse code narrative — stress
519 # ---------------------------------------------------------------------------
520
521
522 class TestNarrativeStress:
523 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
524 result = runner.invoke(cli, ["code", "narrative", "billing.py::Invoice.apply_discount"])
525 assert result.exit_code == 0, result.output
526
527 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
528 result = runner.invoke(cli, [
529 "code", "narrative", "billing.py::Invoice.apply_discount", "--json",
530 ])
531 assert result.exit_code == 0
532 data = json.loads(result.output)
533 assert "address" in data
534 assert "events" in data
535
536 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
537 start = time.monotonic()
538 runner.invoke(cli, [
539 "code", "narrative", "billing.py::Invoice.apply_discount", "--json",
540 ])
541 _assert_under_budget(time.monotonic() - start, "narrative billing.py::... --json")
542
543 def test_format_prose_mode(self, large_repo: pathlib.Path) -> None:
544 result = runner.invoke(cli, [
545 "code", "narrative", "billing.py::Invoice.apply_discount", "--format", "prose",
546 ])
547 assert result.exit_code == 0
548
549
550 # ---------------------------------------------------------------------------
551 # muse code gravity — stress
552 # ---------------------------------------------------------------------------
553
554
555 class TestGravityStress:
556 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
557 result = runner.invoke(cli, ["code", "gravity"])
558 assert result.exit_code == 0, result.output
559
560 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
561 result = runner.invoke(cli, ["code", "gravity", "--json"])
562 assert result.exit_code == 0
563 data = json.loads(result.output)
564 assert "symbols" in data
565
566 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
567 start = time.monotonic()
568 runner.invoke(cli, ["code", "gravity", "--json"])
569 _assert_under_budget(time.monotonic() - start, "gravity --json")
570
571 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
572 result = runner.invoke(cli, ["code", "gravity", "--top", "5", "--json"])
573 assert result.exit_code == 0
574 data = json.loads(result.output)
575 assert len(data["symbols"]) <= 5
576
577
578 # ---------------------------------------------------------------------------
579 # muse code contract — stress
580 # ---------------------------------------------------------------------------
581
582
583 class TestContractStress:
584 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
585 result = runner.invoke(cli, ["code", "contract", "billing.py::Invoice.compute_total"])
586 assert result.exit_code == 0, result.output
587
588 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
589 result = runner.invoke(cli, [
590 "code", "contract", "billing.py::Invoice.compute_total", "--json",
591 ])
592 assert result.exit_code == 0
593 data = json.loads(result.output)
594 assert "address" in data
595
596 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
597 start = time.monotonic()
598 runner.invoke(cli, [
599 "code", "contract", "billing.py::Invoice.compute_total", "--json",
600 ])
601 _assert_under_budget(time.monotonic() - start, "contract billing.py::... --json")
602
603
604 # ---------------------------------------------------------------------------
605 # muse code predict — stress
606 # ---------------------------------------------------------------------------
607
608
609 class TestPredictStress:
610 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
611 result = runner.invoke(cli, ["code", "predict"])
612 assert result.exit_code == 0, result.output
613
614 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
615 result = runner.invoke(cli, ["code", "predict", "--json"])
616 assert result.exit_code == 0
617 data = json.loads(result.output)
618 assert "predictions" in data
619
620 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
621 start = time.monotonic()
622 runner.invoke(cli, ["code", "predict", "--json"])
623 _assert_under_budget(time.monotonic() - start, "predict --json")
624
625 def test_top_n_respected(self, large_repo: pathlib.Path) -> None:
626 result = runner.invoke(cli, ["code", "predict", "--top", "5", "--json"])
627 assert result.exit_code == 0
628 data = json.loads(result.output)
629 assert len(data.get("predictions", [])) <= 5
630
631
632 # ---------------------------------------------------------------------------
633 # muse code semantic-test-coverage — stress
634 # ---------------------------------------------------------------------------
635
636
637 class TestSemanticTestCoverageStress:
638 def test_exits_zero_on_large_repo(self, large_repo: pathlib.Path) -> None:
639 result = runner.invoke(cli, ["code", "semantic-test-coverage"])
640 assert result.exit_code == 0, result.output
641
642 def test_json_schema_correct(self, large_repo: pathlib.Path) -> None:
643 result = runner.invoke(cli, ["code", "semantic-test-coverage", "--json"])
644 assert result.exit_code == 0
645 data = json.loads(result.output)
646 assert "files" in data or "coverage" in data or "summary" in data
647
648 def test_completes_under_budget(self, large_repo: pathlib.Path) -> None:
649 start = time.monotonic()
650 runner.invoke(cli, ["code", "semantic-test-coverage", "--json"])
651 _assert_under_budget(time.monotonic() - start, "semantic-test-coverage --json")
652
653 def test_file_filter(self, large_repo: pathlib.Path) -> None:
654 result = runner.invoke(cli, ["code", "semantic-test-coverage", "--file", "billing.py"])
655 assert result.exit_code == 0
File History 1 commit
sha256:74b5023693ac2ab80e3b89fddc66e0d60d7d931a1266d3f9294f645c3102fe76 tests/test_lineage_supercharge.py, tests/test_narrative_sup… Human 15 days ago