feat: pause campaign batches on SMTP failure

This commit is contained in:
2026-08-20 17:39:08 +02:00
parent 69e6588a89
commit c846c249b8
12 changed files with 385 additions and 33 deletions
+58 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import unittest
from types import SimpleNamespace
from unittest.mock import patch
from unittest.mock import ANY, patch
from govoplan_campaign.backend.db.models import (
JobBuildStatus,
@@ -17,6 +17,7 @@ from govoplan_campaign.backend.sending.jobs import (
_select_campaign_jobs_for_queue,
_send_claimed_campaign_job,
)
from govoplan_campaign.backend.integrations import SmtpSendError
class FakeSession:
@@ -220,6 +221,62 @@ class CampaignQueueSelectionTests(unittest.TestCase):
self.assertTrue(session.rolled_back)
self.assertIn("Automatic retry is stopped", mark_unknown.call_args.kwargs["reason"])
def test_systemic_smtp_failure_pauses_remaining_campaign_jobs(self):
job = SimpleNamespace(
id="job-1",
tenant_id="tenant-1",
campaign_id="campaign-1",
campaign_version_id="version-1",
delivery_channel_policy="mail",
resolved_recipients={"from": {"email": "sender@example.test"}},
)
context = SimpleNamespace(
snapshot=SimpleNamespace(
mail_profile_id="profile-1",
smtp_server_id=None,
smtp_credential_id=None,
smtp_transport_revision="revision-1",
delivery=SimpleNamespace(rate_limit=SimpleNamespace(messages_per_minute=60)),
),
message_bytes=b"message",
envelope_from="sender@example.test",
envelope_recipients=["recipient@example.test"],
)
class Mail:
def wait_for_rate_limit(self, **_kwargs):
return None
def send_campaign_email_bytes(self, *_args, **_kwargs):
raise SmtpSendError(
"SMTP authentication failed.",
systemic=True,
reason_code="smtp_authentication_failed",
)
with (
patch("govoplan_campaign.backend.sending.jobs.mail_integration", return_value=Mail()),
patch("govoplan_campaign.backend.sending.jobs._record_attempt_start", return_value=SimpleNamespace(attempt_number=1)),
patch("govoplan_campaign.backend.sending.jobs._record_smtp_send_error", return_value=None),
patch("govoplan_campaign.backend.sending.jobs._pause_jobs_after_systemic_smtp_failure", return_value=4) as pause,
self.assertRaises(SmtpSendError),
):
_send_claimed_campaign_job(
object(), # type: ignore[arg-type]
job=job, # type: ignore[arg-type]
claim_token="claim-1",
context=context, # type: ignore[arg-type]
use_rate_limit=False,
enqueue_imap_task=False,
)
pause.assert_called_once_with(
ANY,
campaign_id="campaign-1",
exclude_job_id="job-1",
reason_code="smtp_authentication_failed",
)
if __name__ == "__main__":
unittest.main()