Govern distribution delivery channels
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
import unittest
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.core.distribution_lists import (
|
||||
DistributionChannelCandidate,
|
||||
DistributionChannelPolicyRequest,
|
||||
DistributionRecipientRef,
|
||||
)
|
||||
from govoplan_policy.backend.db.models import PolicyOverride
|
||||
from govoplan_policy.backend.distribution_channels import (
|
||||
DistributionChannelPolicyProvider,
|
||||
resolve_distribution_channel_policy_rows,
|
||||
validate_distribution_channel_policy,
|
||||
)
|
||||
|
||||
|
||||
class _Principal:
|
||||
account_id = "account-1"
|
||||
membership_id = "membership-1"
|
||||
group_ids = frozenset({"group-1"})
|
||||
|
||||
|
||||
def _request(channel: str = "email") -> DistributionChannelPolicyRequest:
|
||||
candidate = DistributionChannelCandidate(
|
||||
channel=channel, # type: ignore[arg-type]
|
||||
target="recipient@example.test",
|
||||
target_key=f"{channel}:recipient@example.test",
|
||||
)
|
||||
return DistributionChannelPolicyRequest(
|
||||
tenant_id="tenant-1",
|
||||
list_id="list-1",
|
||||
purpose="monthly-notice",
|
||||
effective_at=datetime(2026, 1, 1, tzinfo=UTC),
|
||||
recipient=DistributionRecipientRef(
|
||||
recipient_key="recipient-1",
|
||||
display_name="Recipient",
|
||||
status="usable",
|
||||
channels=(candidate,),
|
||||
),
|
||||
candidate=candidate,
|
||||
)
|
||||
|
||||
|
||||
class DistributionChannelPolicyTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.engine = create_engine("sqlite:///:memory:")
|
||||
PolicyOverride.__table__.create(self.engine)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.engine.dispose()
|
||||
|
||||
def test_hierarchy_can_only_reduce_permitted_channels(self) -> None:
|
||||
with Session(self.engine) as session:
|
||||
session.add_all(
|
||||
(
|
||||
PolicyOverride(
|
||||
policy_family="distribution_channels",
|
||||
target_key="*",
|
||||
tenant_id=None,
|
||||
scope_type="system",
|
||||
scope_id=None,
|
||||
scope_key="system",
|
||||
policy={"allowed_channels": ["email", "postal"]},
|
||||
),
|
||||
PolicyOverride(
|
||||
policy_family="distribution_channels",
|
||||
target_key="purpose:monthly-notice",
|
||||
tenant_id="tenant-1",
|
||||
scope_type="tenant",
|
||||
scope_id="tenant-1",
|
||||
scope_key="tenant:tenant-1",
|
||||
policy={"blocked_channels": ["postal"]},
|
||||
),
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
provider = DistributionChannelPolicyProvider()
|
||||
|
||||
email = provider.resolve_distribution_channel(
|
||||
session,
|
||||
_Principal(),
|
||||
request=_request("email"),
|
||||
)
|
||||
postal = provider.resolve_distribution_channel(
|
||||
session,
|
||||
_Principal(),
|
||||
request=_request("postal"),
|
||||
)
|
||||
|
||||
self.assertTrue(email.allowed)
|
||||
self.assertFalse(postal.allowed)
|
||||
self.assertEqual("policy.channel_blocked", postal.reason_code)
|
||||
self.assertEqual(2, len(postal.source_path))
|
||||
|
||||
def test_malformed_policy_fails_closed_with_diagnostic(self) -> None:
|
||||
row = type(
|
||||
"Row",
|
||||
(),
|
||||
{
|
||||
"policy": {"allowed_channels": "email"},
|
||||
"target_key": "*",
|
||||
"scope_type": "tenant",
|
||||
"scope_id": "tenant-1",
|
||||
"scope_key": "tenant:tenant-1",
|
||||
},
|
||||
)()
|
||||
result = resolve_distribution_channel_policy_rows((row,))
|
||||
|
||||
self.assertEqual(frozenset(), result.allowed_channels)
|
||||
self.assertEqual("distribution_channel_policy.invalid", result.diagnostics[0]["code"])
|
||||
|
||||
def test_schema_rejects_unknown_channels_and_fields(self) -> None:
|
||||
self.assertEqual(
|
||||
({"allowed_channels": ("email",)}, False),
|
||||
validate_distribution_channel_policy({"allowed_channels": ["email"]}),
|
||||
)
|
||||
self.assertTrue(validate_distribution_channel_policy({"allowed_channels": ["fax"]})[1])
|
||||
self.assertTrue(validate_distribution_channel_policy({"unknown": []})[1])
|
||||
|
||||
def test_long_purpose_is_resolved_without_exceeding_storage_key_limit(self) -> None:
|
||||
request = _request("email")
|
||||
request = DistributionChannelPolicyRequest(
|
||||
tenant_id=request.tenant_id,
|
||||
list_id=request.list_id,
|
||||
purpose="purpose-" + "x" * 120,
|
||||
effective_at=request.effective_at,
|
||||
recipient=request.recipient,
|
||||
candidate=request.candidate,
|
||||
)
|
||||
with Session(self.engine) as session:
|
||||
decision = DistributionChannelPolicyProvider().resolve_distribution_channel(
|
||||
session,
|
||||
_Principal(),
|
||||
request=request,
|
||||
)
|
||||
|
||||
self.assertTrue(decision.allowed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -11,6 +11,9 @@ from govoplan_core.core.policy import (
|
||||
CAPABILITY_POLICY_SCHEDULING_PARTICIPANT_PRIVACY,
|
||||
CAPABILITY_POLICY_VIEW_GOVERNANCE,
|
||||
)
|
||||
from govoplan_core.core.distribution_lists import (
|
||||
CAPABILITY_POLICY_DISTRIBUTION_CHANNELS,
|
||||
)
|
||||
from govoplan_policy.backend.manifest import manifest
|
||||
|
||||
|
||||
@@ -44,6 +47,7 @@ class PolicyModuleContractTests(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
{
|
||||
CAPABILITY_POLICY_DEFINITION_GOVERNANCE,
|
||||
CAPABILITY_POLICY_DISTRIBUTION_CHANNELS,
|
||||
CAPABILITY_POLICY_FUNCTION_ASSIGNMENT_GOVERNANCE,
|
||||
CAPABILITY_POLICY_PRIVACY_RETENTION,
|
||||
CAPABILITY_POLICY_SCHEDULING_PARTICIPANT_PRIVACY,
|
||||
|
||||
Reference in New Issue
Block a user