| 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 |
|
|