user-engine/tests/test_mail_bindings.py

74 lines
3.6 KiB
Python
Raw Normal View History

"""Scoped mail migration uses synthetic identities and a mocked HTTP boundary."""
import json
import unittest
from unittest.mock import patch
from user_engine.adapters.delivery import HTTPOutboxDeliveryAdapter, DeliveryError
from user_engine.domain import OutboxEvent
from user_engine.mail_bindings import load_mail_client_bindings, mail_client_for
class MailBindingsTests(unittest.TestCase):
def test_whole_case_sensitive_globs_and_literal_clients(self):
bindings = load_mail_client_bindings('[ ["tenant:*-family", "coulomb-social"] ]')
self.assertEqual(mail_client_for("tenant:synthetic-family", bindings), "coulomb-social")
for tenant in ("tenant:platform", "tenant:Synthetic-Family", "tenant:x-family/path"):
with self.assertRaisesRegex(ValueError, "mail_binding_denied"):
mail_client_for(tenant, bindings)
def test_invalid_configuration_never_falls_back(self):
for raw in ('', '[]', '{}', '[["*", "*"]]', '[["[a]", "client"]]',
'[["*", "one"], ["*", "two"]]', '[[true, "client"]]'):
with self.assertRaisesRegex(ValueError, "mail_client_bindings_invalid"):
load_mail_client_bindings(raw)
self.assertIsNone(load_mail_client_bindings(None))
def test_conflicting_overlaps_are_denied(self):
bindings = load_mail_client_bindings('[["tenant:*", "one"], ["tenant:x", "two"]]')
with self.assertRaisesRegex(ValueError, "mail_binding_denied"):
mail_client_for("tenant:x", bindings)
def adapter(self, bindings=None):
return HTTPOutboxDeliveryAdapter(event_url="http://events.example.test",
event_bearer_token="synthetic-event-token", mail_url="http://mail.example.test",
mail_bearer_token="synthetic-mail-token", mail_client_bindings=bindings)
def event(self, kind="family_member.invited", tenant="tenant:synthetic-family"):
return OutboxEvent(event_id="evt-synthetic", event_type=kind, aggregate_id="inv-synthetic",
payload={"primary_email": "member@example.test", "invitation_id": "inv-synthetic"},
tenant=tenant, correlation_id="trace-synthetic")
def test_binding_only_changes_mail_transport_not_event_or_outbox(self):
event = self.event()
adapter = self.adapter('[["tenant:*-family", "coulomb-social"]]')
with patch.object(adapter, "_post") as post:
adapter(event)
mail, audit = [call.args[1] for call in post.call_args_list]
self.assertEqual(mail["client_id"], "coulomb-social")
self.assertNotIn("client_id", audit)
self.assertNotIn("primary_email", audit["data"])
self.assertNotIn("client_id", event.payload)
self.assertEqual(mail["id"], event.event_id)
def test_legacy_mode_and_non_mail_events_preserve_envelopes(self):
adapter = self.adapter()
with patch.object(adapter, "_post") as post:
adapter(self.event())
self.assertNotIn("client_id", post.call_args_list[0].args[1])
adapter = self.adapter('[["tenant:*-family", "coulomb-social"]]')
with patch.object(adapter, "_post") as post:
adapter(self.event("membership.added", "tenant:platform"))
self.assertEqual(post.call_count, 1)
def test_unadmitted_mail_tenant_contacts_neither_lane(self):
adapter = self.adapter('[["tenant:*-family", "coulomb-social"]]')
with patch.object(adapter, "_post") as post:
with self.assertRaisesRegex(DeliveryError, "mail_binding_denied"):
adapter(self.event(tenant="tenant:platform"))
post.assert_not_called()
if __name__ == "__main__":
unittest.main()