Integrate Distribution Lists with Campaign recipients
This commit is contained in:
@@ -130,6 +130,125 @@ class CampaignPartialValidationTests(unittest.TestCase):
|
||||
self.assertEqual(response.excluded[0].reason_code, "addresses.channel.opted_out")
|
||||
self.assertTrue(response.provenance["governance_applied"])
|
||||
|
||||
def test_distribution_list_preview_preserves_provider_and_route_evidence(self) -> None:
|
||||
class ExpansionCapability:
|
||||
def expand(self, _session, _principal, *, request):
|
||||
self.request = request
|
||||
return _distribution_expansion_payload()
|
||||
|
||||
capability = ExpansionCapability()
|
||||
payload = router.CampaignDistributionListExpansionRequest(
|
||||
list_id="11111111-1111-1111-1111-111111111111",
|
||||
revision=3,
|
||||
requested_channels=["email", "postal"],
|
||||
parameters={"district": "north"},
|
||||
)
|
||||
with patch.object(router, "_get_campaign_for_principal", return_value=None), patch.object(
|
||||
router, "_registry_capability", return_value=capability
|
||||
):
|
||||
response = router.preview_campaign_recipient_distribution_list(
|
||||
"campaign-1", payload, session=object(), principal=object()
|
||||
)
|
||||
|
||||
self.assertTrue(capability.request.preview)
|
||||
self.assertFalse(capability.request.freeze)
|
||||
self.assertEqual(capability.request.parameters, {"district": "north"})
|
||||
self.assertEqual(response.source.revision, 3)
|
||||
self.assertEqual(response.recipients[0].source_entry_ids, ["entry-1"])
|
||||
self.assertEqual(response.recipients[0].channels[0].decision_provenance["policy"], "allow")
|
||||
self.assertEqual(response.provider_evidence[0].actual_revision, "provider-r7")
|
||||
|
||||
def test_distribution_list_snapshot_requires_and_forwards_idempotency(self) -> None:
|
||||
class ExpansionCapability:
|
||||
def expand(self, _session, _principal, *, request):
|
||||
self.request = request
|
||||
payload = _distribution_expansion_payload()
|
||||
payload["snapshot_id"] = "snapshot-1"
|
||||
return payload
|
||||
|
||||
capability = ExpansionCapability()
|
||||
payload = router.CampaignDistributionListExpansionRequest(
|
||||
list_id="11111111-1111-1111-1111-111111111111",
|
||||
requested_channels=["email"],
|
||||
idempotency_key="campaign:one:list:one",
|
||||
)
|
||||
with patch.object(router, "_get_campaign_for_principal", return_value=None), patch.object(
|
||||
router, "_registry_capability", return_value=capability
|
||||
), patch.object(router, "audit_from_principal") as audit:
|
||||
response = router.snapshot_campaign_recipient_distribution_list(
|
||||
"campaign-1", payload, session=object(), principal=object()
|
||||
)
|
||||
|
||||
self.assertTrue(capability.request.freeze)
|
||||
self.assertFalse(capability.request.preview)
|
||||
self.assertEqual(capability.request.idempotency_key, "campaign:one:list:one")
|
||||
self.assertEqual(response.snapshot_id, "snapshot-1")
|
||||
self.assertTrue(audit.call_args.kwargs["commit"])
|
||||
|
||||
|
||||
def _distribution_expansion_payload() -> dict[str, object]:
|
||||
source = {
|
||||
"id": "11111111-1111-1111-1111-111111111111",
|
||||
"tenant_id": "tenant-1",
|
||||
"name": "Residents",
|
||||
"revision_id": "revision-id-3",
|
||||
"revision": 3,
|
||||
"definition_hash": "definition-hash",
|
||||
"definition_kind": "parameterized",
|
||||
"entry_count": 1,
|
||||
"parameters": [],
|
||||
"provenance": {},
|
||||
"metadata": {},
|
||||
}
|
||||
reference = {
|
||||
"provider": "addresses",
|
||||
"resource_type": "address_list",
|
||||
"resource_id": "list-1",
|
||||
"revision": "provider-r7",
|
||||
"metadata": {},
|
||||
}
|
||||
recipient = {
|
||||
"recipient_key": "contact:1",
|
||||
"display_name": "Ada Lovelace",
|
||||
"status": "usable",
|
||||
"channels": [
|
||||
{
|
||||
"channel": "email",
|
||||
"target": "ada@example.local",
|
||||
"target_key": "email:ada@example.local",
|
||||
"status": "usable",
|
||||
"preferred": True,
|
||||
"source": reference,
|
||||
"decision_provenance": {"policy": "allow"},
|
||||
}
|
||||
],
|
||||
"contact_id": "contact-1",
|
||||
"source_entry_ids": ["entry-1"],
|
||||
"explanations": [],
|
||||
"attributes": {"district": "north"},
|
||||
"provenance": {"provider": "addresses"},
|
||||
}
|
||||
return {
|
||||
"source": source,
|
||||
"request": {"parameters": {"district": "north"}},
|
||||
"recipients": [recipient],
|
||||
"excluded": [],
|
||||
"diagnostics": [],
|
||||
"provider_evidence": [
|
||||
{
|
||||
"provider": "addresses",
|
||||
"source": reference,
|
||||
"actual_revision": "provider-r7",
|
||||
"stale": False,
|
||||
"details": {},
|
||||
}
|
||||
],
|
||||
"expansion_hash": "expansion-hash",
|
||||
"generated_at": "2026-08-02T10:00:00+00:00",
|
||||
"stale": False,
|
||||
"truncated": False,
|
||||
}
|
||||
|
||||
|
||||
class CampaignSemanticValidationTests(unittest.TestCase):
|
||||
def test_send_mode_requires_campaign_owned_sender_for_each_inline_entry(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user