Release govoplan-campaign v0.1.28: stabilize saving, review and delivery recovery
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
@@ -8,6 +8,7 @@ import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from govoplan_campaign.backend import router as campaign_api
|
||||
from govoplan_core.tenancy.scope import Tenant
|
||||
from govoplan_campaign.backend.routes import delivery as router
|
||||
from govoplan_campaign.backend.delivery_policy import (
|
||||
CampaignDeliveryPolicyError,
|
||||
@@ -20,8 +21,14 @@ from govoplan_campaign.backend.db.models import (
|
||||
JobSendStatus,
|
||||
JobValidationStatus,
|
||||
)
|
||||
from govoplan_campaign.backend.integrations import (
|
||||
MailProfileError,
|
||||
SmtpConfigurationError,
|
||||
SmtpSendError,
|
||||
)
|
||||
from govoplan_campaign.backend.sending.jobs import (
|
||||
QueueCampaignResult,
|
||||
SendJobError,
|
||||
SynchronousSendRejected,
|
||||
_ensure_synchronous_send_count_allowed,
|
||||
_pause_jobs_after_systemic_smtp_failure,
|
||||
@@ -39,7 +46,7 @@ class _PolicySession:
|
||||
self.tenant = SimpleNamespace(settings=settings or {})
|
||||
|
||||
def get(self, _model, _id):
|
||||
return self.tenant
|
||||
return self.tenant if _model is Tenant else None
|
||||
|
||||
|
||||
def _version() -> SimpleNamespace:
|
||||
@@ -218,6 +225,65 @@ def test_post_queue_growth_is_rejected_before_batch_or_provider_preflight() -> N
|
||||
batch_preflight.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("error", "reason", "message", "fails_on_exit"),
|
||||
[
|
||||
(MailProfileError("private credential detail"), "mail_profile_preflight_failed", "credential selection", False),
|
||||
(SmtpConfigurationError("private credential detail"), "smtp_configuration_preflight_failed", "SMTP configuration", False),
|
||||
(SmtpSendError("private credential detail", reason_code="smtp_authentication_failed"), "smtp_authentication_failed", "SMTP authentication failed", False),
|
||||
(SmtpSendError("private credential detail", reason_code="smtp_preflight_rejected"), "smtp_preflight_rejected", "server rejected", False),
|
||||
(SmtpSendError("private credential detail", reason_code="private provider code"), "smtp_connectivity_unavailable", "connectivity", False),
|
||||
(OSError("private credential detail"), "smtp_connectivity_unavailable", "connectivity", False),
|
||||
(OSError("private credential detail"), None, "Messages may already have been sent", True),
|
||||
],
|
||||
)
|
||||
def test_smtp_preflight_errors_distinguish_policy_configuration_and_connection_without_secrets(
|
||||
error: Exception, reason: str | None, message: str, fails_on_exit: bool,
|
||||
) -> None:
|
||||
session = Mock()
|
||||
campaign = SimpleNamespace(id="campaign-1")
|
||||
job = _job("one", queue_status="queued", send_status="queued")
|
||||
policy = effective_synchronous_send_policy(_PolicySession(), tenant_id="tenant-1", environ={})
|
||||
|
||||
@contextmanager
|
||||
def rejected_connection():
|
||||
if fails_on_exit:
|
||||
yield SimpleNamespace(connection_count=1, reconnect_count=0)
|
||||
raise error
|
||||
|
||||
with (
|
||||
patch("govoplan_campaign.backend.sending.jobs._get_campaign_for_tenant", return_value=campaign),
|
||||
patch("govoplan_campaign.backend.sending.jobs._get_current_version", return_value=_version()),
|
||||
patch("govoplan_campaign.backend.sending.jobs._ensure_version_validated_and_locked"),
|
||||
patch("govoplan_campaign.backend.sending.jobs._ensure_campaign_execution_snapshot"),
|
||||
patch("govoplan_campaign.backend.sending.jobs.effective_synchronous_send_policy", return_value=policy),
|
||||
patch("govoplan_campaign.backend.sending.jobs._campaign_jobs_for_queue", return_value=[job]),
|
||||
patch("govoplan_campaign.backend.sending.jobs.queue_campaign_jobs") as queue,
|
||||
patch("govoplan_campaign.backend.sending.jobs._campaign_jobs_for_version", return_value=[job]),
|
||||
patch("govoplan_campaign.backend.sending.jobs._preflight_synchronous_send_batch", return_value={"one": Mock()}),
|
||||
patch("govoplan_campaign.backend.sending.jobs._synchronous_smtp_batch_manager", return_value=rejected_connection()),
|
||||
patch("govoplan_campaign.backend.sending.jobs._deliver_job_with_recovery", return_value=SimpleNamespace(status="smtp_accepted", as_dict=lambda: {"status": "smtp_accepted"})) as deliver,
|
||||
pytest.raises(SendJobError if fails_on_exit else SynchronousSendRejected) as rejected,
|
||||
):
|
||||
send_campaign_now(session, tenant_id="tenant-1", campaign_id=campaign.id)
|
||||
|
||||
assert message in str(rejected.value)
|
||||
assert "private" not in str(rejected.value)
|
||||
assert queue.call_args.kwargs["commit_queue"] is False
|
||||
session.rollback.assert_called_once_with()
|
||||
if fails_on_exit:
|
||||
assert "no message was sent" not in str(rejected.value)
|
||||
session.commit.assert_called_once_with()
|
||||
deliver.assert_called_once()
|
||||
else:
|
||||
assert rejected.value.reason == reason
|
||||
assert "no message was sent" in str(rejected.value)
|
||||
assert "private" not in str(rejected.value.audit_details())
|
||||
assert rejected.value.eligible_count == 1
|
||||
session.commit.assert_not_called()
|
||||
deliver.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("workers_available", "expected_mode", "expected_enqueued"),
|
||||
((False, "database_queue", 0), (True, "worker_queue", 1)),
|
||||
|
||||
Reference in New Issue
Block a user