TestForge / tests /test_testforge.py
perceptron01's picture
Upload 7 files
4c69128 verified
Raw
History Blame Contribute Delete
5.34 kB
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