Extend action recovery reconciliation
This commit is contained in:
@@ -181,6 +181,14 @@ class AutomationContractTests(unittest.TestCase):
|
||||
|
||||
self.assertTrue(preview.allowed)
|
||||
self.assertEqual("compensatable", preview.reversibility)
|
||||
self.assertEqual("forward_recovery", provider.action.recovery_mode)
|
||||
self.assertEqual(
|
||||
(
|
||||
"verify the provider result and every announced effect "
|
||||
"before continuation",
|
||||
),
|
||||
provider.action.recovery_verification,
|
||||
)
|
||||
self.assertEqual("completed", result.state)
|
||||
self.assertEqual(
|
||||
"postbox-message:1",
|
||||
@@ -234,6 +242,22 @@ class AutomationContractTests(unittest.TestCase):
|
||||
description="Test effect",
|
||||
contract_version="2",
|
||||
)
|
||||
with self.assertRaisesRegex(ValueError, "recovery verification"):
|
||||
ActionDefinition(
|
||||
action_key="invalid.recovery",
|
||||
owner_module="test",
|
||||
description="Invalid recovery declaration",
|
||||
input_schema_ref="schema:invalid.recovery@1",
|
||||
recovery_verification=(),
|
||||
)
|
||||
with self.assertRaisesRegex(ValueError, "recovery mode"):
|
||||
ActionDefinition(
|
||||
action_key="invalid.recovery-mode",
|
||||
owner_module="test",
|
||||
description="Invalid recovery mode",
|
||||
input_schema_ref="schema:invalid.recovery-mode@1",
|
||||
recovery_mode="best_effort", # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -372,3 +372,49 @@ def test_unknown_provider_outcome_can_be_resolved_from_external_evidence(
|
||||
assert verify_recovery_evidence_chain(session, operation.id)
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
|
||||
def test_unknown_resolution_commits_domain_projection_and_evidence_together() -> None:
|
||||
engine, factory = _fixture()
|
||||
metadata = MetaData()
|
||||
projection = Table(
|
||||
"test_unknown_resolution_projection",
|
||||
metadata,
|
||||
Column("id", String(36), primary_key=True),
|
||||
)
|
||||
metadata.create_all(engine)
|
||||
try:
|
||||
started = _start(factory, _identity("worker-1", "incarnation-1"))
|
||||
assert started.operation is not None
|
||||
started.operation.unresolved(
|
||||
status=RecoveryStatus.OUTCOME_UNKNOWN,
|
||||
summary="Provider outcome is unknown",
|
||||
evidence={"effect_started": True},
|
||||
failure_summary="Inspect the provider before retrying",
|
||||
)
|
||||
recovery = claim_durable_recovery_operation(
|
||||
factory,
|
||||
identity=_identity("worker-2", "incarnation-2"),
|
||||
operation_id=started.operation_id,
|
||||
)
|
||||
with factory() as session:
|
||||
session.execute(projection.insert().values(id="confirmed-effect"))
|
||||
recovery.commit_unknown_resolution(
|
||||
session,
|
||||
effect_occurred=True,
|
||||
summary="Operator verified the provider outcome",
|
||||
evidence={
|
||||
"verified": True,
|
||||
"checks": {"provider_evidence": "case-1"},
|
||||
"effect_occurred": True,
|
||||
},
|
||||
)
|
||||
|
||||
with factory() as session:
|
||||
assert session.scalar(select(projection.c.id)) == "confirmed-effect"
|
||||
operation = session.get(RecoveryOperation, started.operation_id)
|
||||
assert operation is not None
|
||||
assert operation.status == RecoveryStatus.SUCCEEDED.value
|
||||
assert verify_recovery_evidence_chain(session, operation.id)
|
||||
finally:
|
||||
engine.dispose()
|
||||
|
||||
Reference in New Issue
Block a user