Release govoplan-campaign v0.1.28: stabilize saving, review and delivery recovery
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-09-08 01:32:26 +02:00
parent 1b32427813
commit c51fc180fb
111 changed files with 6905 additions and 980 deletions
+55 -34
View File
@@ -223,8 +223,9 @@ def test_queue_projection_fails_closed_if_no_task_was_published() -> None:
)
@pytest.mark.parametrize("duplicate_mutates", [False, True])
def test_redelivery_orchestration_requires_same_task_and_no_second_smtp_effect(
monkeypatch,
monkeypatch, duplicate_mutates,
) -> None:
first_worker = mock.Mock()
first_worker.received_task_ids.return_value = (TASK_ID,)
@@ -232,22 +233,29 @@ def test_redelivery_orchestration_requires_same_task_and_no_second_smtp_effect(
replacement_worker.received_task_ids.return_value = (TASK_ID,)
workers = iter([first_worker, replacement_worker])
endpoint = _Endpoint()
durable_states = iter(
[
{
"job_count": 1,
"send_status_counts": {"sending": 1},
"attempt_status_counts": {"smtp_in_progress": 1},
"unfinished_attempt_count": 1,
},
{
"job_count": 1,
"send_status_counts": {"outcome_unknown": 1},
"attempt_status_counts": {"outcome_unknown": 1},
"unfinished_attempt_count": 0,
},
]
)
durable_states = [
{
"job_count": 1,
"send_status_counts": {"sending": 1},
"attempt_status_counts": {"smtp_in_progress": 1},
"unfinished_attempt_count": 1,
},
{
"job_count": 1,
"send_status_counts": {"sending": 1},
"attempt_status_counts": {"smtp_in_progress": 1},
"unfinished_attempt_count": 1,
},
{
"job_count": 1,
"send_status_counts": {"outcome_unknown": 1},
"attempt_status_counts": {"outcome_unknown": 1},
"unfinished_attempt_count": 0,
},
]
if duplicate_mutates:
durable_states[1] = durable_states[2]
durable_states = iter(durable_states)
prepared = SimpleNamespace(
campaign_id="campaign-internal",
version_id="version-internal",
@@ -277,24 +285,34 @@ def test_redelivery_orchestration_requires_same_task_and_no_second_smtp_effect(
"_wait_for_broker_drained",
lambda *args, **kwargs: runner.RedisBrokerState(0, 0, 0),
)
recover_claim = mock.Mock(return_value={"explicit_fenced_recovery": True})
evidence = runner.execute_redelivery_scenario(
_Client(),
{"Authorization": "not-retained"},
fixture_path=FIXTURE_PATH,
settings=_settings(),
endpoint=endpoint,
redis_url="redis://127.0.0.1:36379/0",
runtime_root=Path("/not-used"),
snapshot_probe=lambda _version_id: ({}, {}),
audit_probe=lambda _campaign_id, _version_id: {
"campaign.created": 1,
"campaign.validated": 1,
"campaign.messages_built": 1,
"campaign.queued": 1,
},
delivery_probe=lambda _campaign_id, _version_id: next(durable_states),
)
def execute():
return runner.execute_redelivery_scenario(
_Client(),
{"Authorization": "not-retained"},
fixture_path=FIXTURE_PATH,
settings=_settings(),
endpoint=endpoint,
redis_url="redis://127.0.0.1:36379/0",
runtime_root=Path("/not-used"),
snapshot_probe=lambda _version_id: ({}, {}),
audit_probe=lambda _campaign_id, _version_id: {
"campaign.created": 1,
"campaign.validated": 1,
"campaign.messages_built": 1,
"campaign.queued": 1,
},
delivery_probe=lambda _campaign_id, _version_id: next(durable_states),
recover_claim=recover_claim,
)
if duplicate_mutates:
with pytest.raises(runner.AcceptanceError, match="without stopped-runtime proof"):
execute()
recover_claim.assert_not_called()
return
evidence = execute()
assert evidence["broker"] == {
"transport": "redis",
@@ -310,5 +328,8 @@ def test_redelivery_orchestration_requires_same_task_and_no_second_smtp_effect(
assert evidence["recovered_durable_state"]["send_status_counts"] == {
"outcome_unknown": 1
}
assert evidence["redelivered_durable_state"] == evidence["interrupted_durable_state"]
assert evidence["supervision"]["duplicate_task_left_sending_unchanged"] is True
recover_claim.assert_called_once_with("campaign-internal", "version-internal", first_worker.process)
assert TASK_ID not in json.dumps(evidence, sort_keys=True)
assert endpoint.release_count >= 1