Fence Campaign delivery effects in recovery ledger
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from govoplan_core.core import runtime_coordination
|
||||
from govoplan_core.core.runtime_coordination import (
|
||||
RuntimeIdentity,
|
||||
bind_process_runtime_identity,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def _bind_test_runtime_identity():
|
||||
previous = runtime_coordination._process_runtime_identity
|
||||
bind_process_runtime_identity(
|
||||
RuntimeIdentity(
|
||||
installation_id="campaign-tests",
|
||||
node_id="campaign-test-process",
|
||||
incarnation="campaign-test-incarnation",
|
||||
role="test",
|
||||
software_version="test",
|
||||
composition_hash="a" * 64,
|
||||
)
|
||||
)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
bind_process_runtime_identity(previous)
|
||||
@@ -0,0 +1,119 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from govoplan_core.core.recovery import RecoveryStatus
|
||||
from govoplan_campaign.backend.sending import jobs
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("outcome", "expected_method", "expected_status"),
|
||||
[
|
||||
("succeeded", "succeed", None),
|
||||
("failed", "reject", None),
|
||||
("outcome_unknown", "unresolved", RecoveryStatus.OUTCOME_UNKNOWN),
|
||||
("recovery_required", "unresolved", RecoveryStatus.RECOVERY_REQUIRED),
|
||||
],
|
||||
)
|
||||
def test_delivery_recovery_maps_verified_job_state(
|
||||
monkeypatch,
|
||||
outcome: str,
|
||||
expected_method: str,
|
||||
expected_status: RecoveryStatus | None,
|
||||
) -> None:
|
||||
evidence = {
|
||||
"verified": outcome != "recovery_required",
|
||||
"checks": {"job_state_reloaded": True},
|
||||
"send_status": outcome,
|
||||
}
|
||||
monkeypatch.setattr(
|
||||
jobs,
|
||||
"_job_delivery_recovery_evidence",
|
||||
lambda _job_id: (outcome, evidence),
|
||||
)
|
||||
operation = Mock()
|
||||
|
||||
jobs._finish_job_delivery_recovery(operation, job_id="job-1")
|
||||
|
||||
method = getattr(operation, expected_method)
|
||||
method.assert_called_once()
|
||||
if expected_status is not None:
|
||||
assert method.call_args.kwargs["status"] == expected_status
|
||||
for other in {"succeed", "reject", "unresolved"} - {expected_method}:
|
||||
getattr(operation, other).assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("outcome", "expected_method", "expected_status"),
|
||||
[
|
||||
("succeeded", "succeed", None),
|
||||
("failed", "reject", None),
|
||||
("outcome_unknown", "unresolved", RecoveryStatus.OUTCOME_UNKNOWN),
|
||||
("recovery_required", "unresolved", RecoveryStatus.RECOVERY_REQUIRED),
|
||||
],
|
||||
)
|
||||
def test_imap_recovery_maps_verified_append_state(
|
||||
monkeypatch,
|
||||
outcome: str,
|
||||
expected_method: str,
|
||||
expected_status: RecoveryStatus | None,
|
||||
) -> None:
|
||||
evidence = {
|
||||
"verified": outcome != "recovery_required",
|
||||
"checks": {"job_state_reloaded": True},
|
||||
"imap_status": outcome,
|
||||
}
|
||||
monkeypatch.setattr(
|
||||
jobs,
|
||||
"_imap_recovery_evidence",
|
||||
lambda _job_id: (outcome, evidence),
|
||||
)
|
||||
operation = Mock()
|
||||
|
||||
jobs._finish_imap_append_recovery(operation, job_id="job-1")
|
||||
|
||||
method = getattr(operation, expected_method)
|
||||
method.assert_called_once()
|
||||
if expected_status is not None:
|
||||
assert method.call_args.kwargs["status"] == expected_status
|
||||
for other in {"succeed", "reject", "unresolved"} - {expected_method}:
|
||||
getattr(operation, other).assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("outcome", "expected_method", "expected_status"),
|
||||
[
|
||||
("succeeded", "succeed", None),
|
||||
("failed", "reject", None),
|
||||
("outcome_unknown", "unresolved", RecoveryStatus.OUTCOME_UNKNOWN),
|
||||
("recovery_required", "unresolved", RecoveryStatus.RECOVERY_REQUIRED),
|
||||
],
|
||||
)
|
||||
def test_single_action_recovery_maps_verified_action_state(
|
||||
monkeypatch,
|
||||
outcome: str,
|
||||
expected_method: str,
|
||||
expected_status: RecoveryStatus | None,
|
||||
) -> None:
|
||||
evidence = {
|
||||
"verified": outcome != "recovery_required",
|
||||
"checks": {"action_state_reloaded": True},
|
||||
"action_status": outcome,
|
||||
}
|
||||
monkeypatch.setattr(
|
||||
jobs,
|
||||
"_single_action_recovery_evidence",
|
||||
lambda _action_id: (outcome, evidence),
|
||||
)
|
||||
operation = Mock()
|
||||
|
||||
jobs._finish_single_action_delivery_recovery(operation, action_id="action-1")
|
||||
|
||||
method = getattr(operation, expected_method)
|
||||
method.assert_called_once()
|
||||
if expected_status is not None:
|
||||
assert method.call_args.kwargs["status"] == expected_status
|
||||
for other in {"succeed", "reject", "unresolved"} - {expected_method}:
|
||||
getattr(operation, other).assert_not_called()
|
||||
@@ -159,6 +159,11 @@ def test_post_provider_persistence_failure_freezes_imap_retry() -> None:
|
||||
patch("govoplan_campaign.backend.sending.jobs._load_eml_bytes_for_job", return_value=b"message"),
|
||||
patch("govoplan_campaign.backend.sending.jobs._claim_job_for_imap_append", return_value="claim-1"),
|
||||
patch("govoplan_campaign.backend.sending.jobs._record_imap_attempt_start", return_value=attempt),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs._begin_imap_append_recovery",
|
||||
return_value=SimpleNamespace(replayed=False, operation=MagicMock()),
|
||||
),
|
||||
patch("govoplan_campaign.backend.sending.jobs._finish_imap_append_recovery"),
|
||||
patch("govoplan_campaign.backend.sending.jobs.mail_integration", return_value=Mail()),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs._record_imap_append_success",
|
||||
|
||||
@@ -170,6 +170,13 @@ class CampaignSingleMessageActionTests(unittest.TestCase):
|
||||
"govoplan_campaign.backend.sending.jobs.mail_integration",
|
||||
return_value=mail,
|
||||
),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs._begin_single_action_delivery_recovery",
|
||||
return_value=SimpleNamespace(replayed=False, operation=Mock()),
|
||||
),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs._finish_single_action_delivery_recovery"
|
||||
),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs.audit_event"
|
||||
),
|
||||
@@ -269,6 +276,13 @@ class CampaignSingleMessageActionTests(unittest.TestCase):
|
||||
"govoplan_campaign.backend.sending.jobs.mail_integration",
|
||||
return_value=mail,
|
||||
),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs._begin_single_action_delivery_recovery",
|
||||
return_value=SimpleNamespace(replayed=False, operation=Mock()),
|
||||
),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs._finish_single_action_delivery_recovery"
|
||||
),
|
||||
patch(
|
||||
"govoplan_campaign.backend.sending.jobs.files_integration"
|
||||
) as files,
|
||||
|
||||
Reference in New Issue
Block a user