164 lines
6.8 KiB
Python
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 == ()
|