Refactor campaign delivery decision paths
This commit is contained in:
@@ -4,6 +4,8 @@ import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from govoplan_campaign.backend.campaign.models import DeliveryChannelPolicy
|
||||
from govoplan_campaign.backend.db.models import JobSendStatus
|
||||
from govoplan_campaign.backend.sending.jobs import (
|
||||
@@ -285,5 +287,114 @@ class PostboxFallbackOrchestrationTests(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy", list(DeliveryChannelPolicy))
|
||||
@pytest.mark.parametrize(
|
||||
("mail", "postbox"),
|
||||
[
|
||||
(_MailChannelOutcome(outcome_unknown=True), PostboxChannelOutcome()),
|
||||
(_MailChannelOutcome(accepted=True), PostboxChannelOutcome(outcome_unknown=1)),
|
||||
(_MailChannelOutcome(outcome_unknown=True), PostboxChannelOutcome(accepted=1)),
|
||||
],
|
||||
)
|
||||
def test_outcome_unknown_has_precedence_for_every_delivery_policy(
|
||||
policy: DeliveryChannelPolicy,
|
||||
mail: _MailChannelOutcome,
|
||||
postbox: PostboxChannelOutcome,
|
||||
) -> None:
|
||||
assert _final_multichannel_status(channel_policy=policy, mail=mail, postbox=postbox) == JobSendStatus.OUTCOME_UNKNOWN.value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy", list(DeliveryChannelPolicy))
|
||||
@pytest.mark.parametrize(
|
||||
("mail", "postbox", "expected"),
|
||||
[
|
||||
(
|
||||
_MailChannelOutcome(rejected_permanent=True),
|
||||
PostboxChannelOutcome(rejected_permanent=1),
|
||||
JobSendStatus.FAILED_PERMANENT.value,
|
||||
),
|
||||
(
|
||||
_MailChannelOutcome(rejected_temporary=True),
|
||||
PostboxChannelOutcome(rejected_permanent=1),
|
||||
JobSendStatus.FAILED_TEMPORARY.value,
|
||||
),
|
||||
(
|
||||
_MailChannelOutcome(rejected_permanent=True),
|
||||
PostboxChannelOutcome(rejected_temporary=1),
|
||||
JobSendStatus.FAILED_TEMPORARY.value,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_rejection_precedence_is_exhaustive_for_every_delivery_policy(
|
||||
policy: DeliveryChannelPolicy,
|
||||
mail: _MailChannelOutcome,
|
||||
postbox: PostboxChannelOutcome,
|
||||
expected: str,
|
||||
) -> None:
|
||||
assert _final_multichannel_status(channel_policy=policy, mail=mail, postbox=postbox) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("policy", "mail", "postbox", "expected"),
|
||||
[
|
||||
(
|
||||
DeliveryChannelPolicy.MAIL,
|
||||
_MailChannelOutcome(accepted=True),
|
||||
PostboxChannelOutcome(),
|
||||
JobSendStatus.SMTP_ACCEPTED.value,
|
||||
),
|
||||
(
|
||||
DeliveryChannelPolicy.POSTBOX,
|
||||
None,
|
||||
PostboxChannelOutcome(accepted=1),
|
||||
JobSendStatus.POSTBOX_ACCEPTED.value,
|
||||
),
|
||||
(
|
||||
DeliveryChannelPolicy.POSTBOX,
|
||||
None,
|
||||
PostboxChannelOutcome(accepted=1, rejected_permanent=1),
|
||||
JobSendStatus.PARTIALLY_ACCEPTED.value,
|
||||
),
|
||||
(
|
||||
DeliveryChannelPolicy.MAIL_AND_POSTBOX,
|
||||
_MailChannelOutcome(accepted=True),
|
||||
PostboxChannelOutcome(accepted=1),
|
||||
JobSendStatus.DELIVERED.value,
|
||||
),
|
||||
(
|
||||
DeliveryChannelPolicy.MAIL_AND_POSTBOX,
|
||||
_MailChannelOutcome(accepted=True),
|
||||
PostboxChannelOutcome(rejected_permanent=1),
|
||||
JobSendStatus.PARTIALLY_ACCEPTED.value,
|
||||
),
|
||||
(
|
||||
DeliveryChannelPolicy.MAIL_AND_POSTBOX,
|
||||
_MailChannelOutcome(rejected_permanent=True),
|
||||
PostboxChannelOutcome(accepted=1),
|
||||
JobSendStatus.PARTIALLY_ACCEPTED.value,
|
||||
),
|
||||
(
|
||||
DeliveryChannelPolicy.MAIL_THEN_POSTBOX,
|
||||
_MailChannelOutcome(rejected_permanent=True),
|
||||
PostboxChannelOutcome(accepted=1),
|
||||
JobSendStatus.POSTBOX_ACCEPTED.value,
|
||||
),
|
||||
(
|
||||
DeliveryChannelPolicy.POSTBOX_THEN_MAIL,
|
||||
_MailChannelOutcome(accepted=True),
|
||||
PostboxChannelOutcome(rejected_permanent=1),
|
||||
JobSendStatus.PARTIALLY_ACCEPTED.value,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_accepted_delivery_decision_table(
|
||||
policy: DeliveryChannelPolicy,
|
||||
mail: _MailChannelOutcome | None,
|
||||
postbox: PostboxChannelOutcome,
|
||||
expected: str,
|
||||
) -> None:
|
||||
assert _final_multichannel_status(channel_policy=policy, mail=mail, postbox=postbox) == expected
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -42,6 +42,16 @@ def test_report_email_recipient_schema_rejects_unsafe_addresses(recipients: list
|
||||
ReportEmailRequest.model_validate({"to": recipients})
|
||||
|
||||
|
||||
def test_report_email_recipient_error_identifies_the_invalid_list_item() -> None:
|
||||
with pytest.raises(ValidationError) as captured:
|
||||
ReportEmailRequest.model_validate({
|
||||
"to": ["first@example.test", "invalid recipient", "third@example.test"],
|
||||
})
|
||||
|
||||
assert captured.value.errors()[0]["loc"] == ("to", 1)
|
||||
assert "email addresses" in captured.value.errors()[0]["msg"]
|
||||
|
||||
|
||||
def test_report_jobs_csv_attachment_requires_recipient_export_permission() -> None:
|
||||
payload = ReportEmailRequest(to=["recipient@example.test"], attach_jobs_csv=True)
|
||||
principal = SimpleNamespace(tenant_id="tenant-1")
|
||||
|
||||
Reference in New Issue
Block a user