from pathlib import Path import shutil import sys from analyzer import analyze from agent import forge_legacy_repo from generator import capture, generate_suite, write_suite from inject import reset_sample, run_injected_suite from model_suggest import _parse_tuples from mutator import MUTATIONS, mutation_score from patch import make_pr_patch from runner import run_pytest from samples.legacy_repo.pricing import apply_discount, with_tax ROOT = Path(__file__).resolve().parents[1] LEGACY_ROOT = ROOT / "samples" / "legacy_repo" def test_analyzer_discovers_expected_public_functions(): analysis = analyze(LEGACY_ROOT, package="legacy_repo") discovered = {(fn.module, fn.qualname, fn.arity) for fn in analysis.functions} assert discovered == { ("legacy_repo.dates", "days_between", 2), ("legacy_repo.dates", "is_weekend", 1), ("legacy_repo.invoice", "invoice_total", 1), ("legacy_repo.invoice", "line_total", 2), ("legacy_repo.pricing", "apply_discount", 2), ("legacy_repo.pricing", "bulk_price", 2), ("legacy_repo.pricing", "with_tax", 2), ("legacy_repo.slugify", "slugify", 1), ("legacy_repo.slugify", "truncate", 2), } def test_analyzer_keeps_hints_docstrings_and_source(): analysis = analyze(LEGACY_ROOT, package="legacy_repo") fn = next(item for item in analysis.functions if item.qualname == "apply_discount") assert fn.type_hints == {"price": "float", "pct": "float"} assert fn.return_hint == "float" assert "percentage discount" in fn.docstring assert "def apply_discount" in fn.source def test_capture_records_return_and_exception(): sys.path.insert(0, str((ROOT / "samples").resolve())) analysis = analyze(LEGACY_ROOT, package="legacy_repo") discount = next(item for item in analysis.functions if item.qualname == "apply_discount") tax = next(item for item in analysis.functions if item.qualname == "with_tax") assert apply_discount(100, 10) == 90.0 returned = capture(discount, [(100.0, 10.0)]) raised = capture(tax, [(100.0, -1.0)]) assert returned[0].value_repr == "90.0" assert raised[0].exception_type == "ValueError" def test_deterministic_generated_suite_is_green(tmp_path): analysis = analyze(LEGACY_ROOT, package="legacy_repo") suite = generate_suite(analysis) workdir = tmp_path / "work" shutil.copytree(ROOT / "samples", workdir / "samples") write_suite(suite, workdir) result = run_pytest(workdir, package_parent=workdir / "samples") assert result.ok, result.stdout + result.stderr assert result.passed == suite.assertion_count assert result.coverage is None or result.coverage >= 0 def test_runner_parses_known_good_and_bad(tmp_path): good = tmp_path / "good" good.mkdir() (good / "tests").mkdir() (good / "tests" / "test_ok.py").write_text("def test_ok():\n assert 1 == 1\n", encoding="utf-8") good_result = run_pytest(good, with_coverage=False) bad = tmp_path / "bad" bad.mkdir() (bad / "tests").mkdir() (bad / "tests" / "test_bad.py").write_text("def test_bad():\n assert 1 == 2\n", encoding="utf-8") bad_result = run_pytest(bad, with_coverage=False) assert good_result.ok assert good_result.passed == 1 assert not bad_result.ok assert bad_result.failed == 1 def test_mutation_score_is_deterministic_and_kills_known_mutant(tmp_path): analysis = analyze(LEGACY_ROOT, package="legacy_repo") suite = generate_suite(analysis) score = mutation_score(ROOT / "samples", suite, tmp_path / "mutants") assert score.total == len(MUTATIONS) assert score.killed > 0 known = next( result for result in score.results if result.mutation.id == "bulk_multiply_to_divide" ) assert known.killed def test_patch_export_contains_generated_tests(): analysis = analyze(LEGACY_ROOT, package="legacy_repo") suite = generate_suite(analysis) patch = make_pr_patch(suite) assert "b/tests/test_legacy_repo_pricing_apply_discount.py" in patch assert "pytest tests -q" in patch def test_forge_pipeline_produces_green_suite_and_patch(): one_mutant = (next(item for item in MUTATIONS if item.id == "bulk_threshold_ge_to_gt"),) artifacts = forge_legacy_repo(max_cases_per_function=2, mutations=one_mutant) assert artifacts.green.ok, artifacts.green.stdout + artifacts.green.stderr assert artifacts.suite.assertion_count > 0 assert artifacts.mutation.killed > 0 assert artifacts.patch_path.exists() def test_model_suggest_parses_only_arity_matched_tuples(): text = "Here you go: [(0, 'x'), (-1, ''), (1, 2, 3)]" assert _parse_tuples(text, arity=2, limit=5) == [(0, "x"), (-1, "")] assert _parse_tuples("[(0, 1, 2)]", arity=2, limit=2) == [] assert _parse_tuples("no list here", arity=2, limit=2) == [] assert _parse_tuples("[5, -1, 0]", arity=1, limit=2) == [(5,), (-1,)] def test_inject_regression_causes_exactly_one_failure(tmp_path): analysis = analyze(LEGACY_ROOT, package="legacy_repo") suite = generate_suite(analysis) run_dir = tmp_path / "inject" run_dir.mkdir() reset_sample(ROOT / "samples", run_dir) write_suite(suite, run_dir) result = run_injected_suite(run_dir) assert not result.run.ok assert result.run.failed == 1