band-aider-pub/tests/band_aider/context/test_edited_test_inventory.py
2026-10-17 11:57:37 -07:00

164 lines
6.8 KiB
Python

"""Explicit monitored membership and episode-lifetime contracts."""
from band_aider.judgement.test_runner_strategy import TestRunOutcome
from unittest import TestCase as _TestCase
from band_aider.context.edited_test_inventory import EditedTestInventory
from band_aider.strategies.compiler import TestRunCompileError
from band_aider.context.class_meta import ClassMeta, ContentType
from band_aider.context.validation_receipts import ValidationReceipt
from band_aider.judgement.test_runner_strategy import TestRunResult
def test_identified_failures_are_added_once() -> None:
"""Original failure evidence cannot resurrect a passing test."""
failure = TestRunCompileError("initial failure", test_name="test_value")
with EditedTestInventory(lambda: None) as inventory:
inventory.add_identified_failures((failure,))
inventory.set_candidate_results({"test_value": None})
inventory.commit_candidate_results()
inventory.add_identified_failures((failure,))
assert inventory.monitored == ()
assert inventory.failures == ()
def test_new_candidate_cannot_commit_a_previous_unaccepted_result() -> None:
"""Starting a new code generation drops provisional outcomes from the earlier candidate."""
failure = TestRunCompileError("original failure", test_name="test_value")
with EditedTestInventory(lambda: None) as inventory:
inventory.add_identified_failures((failure,))
inventory.begin_candidate()
inventory.set_candidate_results({"test_value": None})
inventory.begin_candidate()
inventory.commit_candidate_results()
assert inventory.failures == (failure,)
assert inventory.monitored == ("test_value",)
def test_admission_preserves_monitored() -> None:
"""Changed tests join monitored failures without admitting every current test."""
failure = TestRunCompileError("initial failure", test_name="test_alpha")
with EditedTestInventory(lambda: None) as inventory:
inventory.add_identified_failures((failure,))
inventory.add_monitored({"test_beta"})
inventory.remove_not_found({"test_alpha", "test_beta", "test_unrelated"})
assert inventory.monitored == ("test_alpha", "test_beta")
assert inventory.failures == (failure,)
def test_tests_to_run_does_not_change_inventory() -> None:
"""Candidate admission can determine test runs without changing inventory."""
identified = TestRunCompileError("incoming failure", test_name="test_identified")
with EditedTestInventory(lambda: None) as inventory:
inventory.add_identified_failures((identified,))
inventory.add_monitored({"test_changed"})
monitored_before = inventory.monitored
monitored_tests = inventory.tests_to_run(
{"test_changed", "test_identified"},
)
assert monitored_tests == ("test_changed", "test_identified")
assert inventory.monitored == monitored_before == (
"test_changed",
"test_identified",
)
assert inventory.failures == (identified,)
def test_pass_and_deletion_clear() -> None:
"""Passing and disappearing tests both clear monitored membership."""
failure = TestRunCompileError("old failure", test_name="test_deleted")
with EditedTestInventory(lambda: None) as inventory:
inventory.add_identified_failures((failure,))
inventory.add_monitored({"test_passing"})
inventory.set_candidate_results({
"test_deleted": None,
"test_passing": None,
})
inventory.commit_candidate_results()
assert inventory.monitored == ()
assert inventory.failures == ()
def test_discard_keeps_monitored_tests_and_failures() -> None:
"""Rejected results cannot clear admissions or replace accepted failures."""
failure = TestRunCompileError("accepted failure", test_name="test_value")
replacement = TestRunCompileError("candidate failure", test_name="test_value")
with EditedTestInventory(lambda: None) as inventory:
inventory.add_identified_failures((failure,))
inventory.add_monitored({"test_new"})
inventory.set_candidate_results({
"test_new": None,
"test_value": replacement,
})
inventory.discard_candidate_results()
assert inventory.monitored == ("test_new", "test_value")
assert inventory.failures == (failure,)
def test_scope_releases_on_success() -> None:
"""Ordinary exit clears evidence and invokes the owner's forget operation."""
forgotten: list[bool] = []
with EditedTestInventory(lambda: forgotten.append(True)) as inventory:
inventory.add_identified_failures((TestRunCompileError("failure", test_name="test_value"),))
assert inventory.monitored == ("test_value",)
assert inventory.monitored == ()
assert inventory.failures == ()
assert forgotten == [True]
def test_scope_releases_on_exception() -> None:
"""Leaving a failed scope clears state while preserving the original exception."""
forgotten: list[bool] = []
inventory = EditedTestInventory(lambda: forgotten.append(True))
with _TestCase().assertRaisesRegex(RuntimeError, "editor failed"), inventory:
inventory.add_identified_failures((TestRunCompileError("failure", test_name="test_value"),))
raise RuntimeError("editor failed")
assert inventory.monitored == ()
assert inventory.failures == ()
assert forgotten == [True]
def test_receipts_follow_candidate_commit_discard_and_generation() -> None:
"""A rejected pass cannot erase a retained failure or advance its code generation."""
test = ClassMeta("tests/test_value.py", "", [], {}, content_type=ContentType.Test)
with EditedTestInventory(lambda: None) as inventory:
inventory.begin_candidate()
failure = ValidationReceipt.from_result(test,
TestRunResult("pytest", TestRunOutcome.Failure, 1, "retained failure", test_name="test_value"),
test_definition="def test_value():\n assert False\n", generation=inventory.generation)
inventory.record_receipt(failure)
inventory.commit_candidate_results()
retained_generation = inventory.accepted_generation
inventory.begin_candidate()
passing = ValidationReceipt.from_result(test,
TestRunResult("pytest", TestRunOutcome.Success, 0, "rejected pass", test_name="test_value"),
test_definition="def test_value():\n assert True\n", generation=inventory.generation)
inventory.record_receipt(passing)
inventory.discard_candidate_results()
assert inventory.receipts == (failure,)
assert inventory.accepted_generation == retained_generation
inventory.begin_candidate()
inventory.commit_candidate_results()
assert inventory.accepted_generation > retained_generation
assert inventory.receipts == (failure,)
assert inventory.receipts == ()