from __future__ import annotations from unittest.mock import patch from govoplan_core.core.postbox import ( PostboxDeliveryCatalogRef, PostboxDeliveryTemplateRef, PostboxDirectoryEntryRef, PostboxOrganizationFunctionTargetRef, PostboxOrganizationUnitTargetRef, ) from govoplan_campaign.backend.campaign.models import CampaignConfig from govoplan_campaign.backend.campaign.postbox_targets import ( resolve_entry_postbox_targets, ) from govoplan_campaign.backend.campaign.validation import ( validate_campaign_config, ) from govoplan_campaign.backend.messages.models import MessageValidationStatus def _config() -> CampaignConfig: return CampaignConfig.model_validate( { "version": "1.0", "campaign": { "id": "campaign-1", "name": "Postbox campaign", "mode": "send", }, "fields": [ {"name": "org_key", "type": "organization_unit"}, {"name": "function_key", "type": "organization_function"}, {"name": "case_key", "type": "string"}, ], "template": { "subject": "Decision", "text": "A decision is available.", "body_mode": "text", }, "entries": { "inline": [ { "id": "recipient-1", "fields": { "org_key": "finance", "function_key": "caseworker", "case_key": "case-42", }, } ] }, "delivery": { "channel_policy": "postbox", "postbox": { "targets": [ { "id": "direct", "mode": "direct", "address_key": "central-intake", }, { "id": "derived", "mode": "derived", "template_id": "template-1", "organization_unit_field": "org_key", "organization_unit_match": "slug", "function_field": "function_key", "function_match": "slug", "context_field": "case_key", }, ] }, }, } ) def _catalog() -> PostboxDeliveryCatalogRef: return PostboxDeliveryCatalogRef( templates=( PostboxDeliveryTemplateRef( id="template-1", slug="case-inbox", name="Case inbox", description=None, published_revision_id="revision-1", function_type_id=None, scope_kind="tenant", scope_id=None, classification="internal", allow_vacant_delivery=True, ), ), organization_units=( PostboxOrganizationUnitTargetRef( id="unit-1", slug="finance", name="Finance", functions=( PostboxOrganizationFunctionTargetRef( id="function-1", slug="caseworker", name="Caseworker", ), ), ), ), ) class _PostboxIntegration: def __init__(self) -> None: self.targets: list[tuple[object, bool]] = [] def delivery_catalog(self, _session, *, tenant_id: str): assert tenant_id == "tenant-1" return _catalog() def resolve_postbox( self, _session, *, tenant_id: str, target, materialize: bool, ): self.targets.append((target, materialize)) if target.address_key == "central-intake": return PostboxDirectoryEntryRef( id="postbox-direct", tenant_id=tenant_id, address="central-intake@postbox", address_key="central-intake", name="Central intake", status="active", classification="internal", holder_count=2, vacant=False, ) assert target.template_id == "template-1" assert target.organization_unit_id == "unit-1" assert target.function_id == "function-1" assert target.context_key == "case-42" return PostboxDirectoryEntryRef( id="postbox-derived", tenant_id=tenant_id, address="finance.caseworker.case-42@postbox", address_key="finance.caseworker.case-42", name="Finance caseworker", status="active", classification="internal", organization_unit_id="unit-1", function_id="function-1", context_key="case-42", holder_count=1, vacant=False, ) def test_postbox_only_campaign_does_not_require_mail() -> None: config = _config() available = validate_campaign_config(config, postbox_available=True) unavailable = validate_campaign_config(config, postbox_available=False) assert { issue.code for issue in available.issues if issue.severity.value == "error" } == set() assert "missing_mail_profile" not in { issue.code for issue in unavailable.issues } assert "missing_sender" not in { issue.code for issue in unavailable.issues } assert "postbox_unavailable" in { issue.code for issue in unavailable.issues } def test_row_resolves_multiple_direct_and_field_derived_postboxes() -> None: config = _config() integration = _PostboxIntegration() with patch( "govoplan_campaign.backend.campaign.postbox_targets.postbox_integration", return_value=integration, ): targets, issues, status = resolve_entry_postbox_targets( object(), # type: ignore[arg-type] tenant_id="tenant-1", config=config, entry=config.entries.inline[0], # type: ignore[index] validation_status=MessageValidationStatus.READY, materialize=True, ) assert issues == [] assert status == MessageValidationStatus.READY assert [target["postbox_id"] for target in targets] == [ "postbox-direct", "postbox-derived", ] assert targets[1]["context_key"] == "case-42" assert len(integration.targets) == 2 assert all(materialize for _target, materialize in integration.targets)