Implement correlated campaign invitations

This commit is contained in:
2026-08-02 16:38:40 +02:00
parent 77a40ec2f9
commit 56d7b2108d
4 changed files with 404 additions and 3 deletions
+91
View File
@@ -12,6 +12,7 @@ from govoplan_calendar.backend.capabilities import (
SqlCalendarInvitationProvider,
SqlCalendarSchedulingProvider,
)
from govoplan_calendar.backend.db.models import CalendarEvent
from govoplan_calendar.backend.schemas import CalendarCollectionCreateRequest
from govoplan_calendar.backend.service import create_calendar
from govoplan_core.core.calendar import (
@@ -131,6 +132,10 @@ class CalendarInvitationCapabilityTests(unittest.TestCase):
summary=summary,
start_at=datetime(2026, 8, 5, 9, tzinfo=timezone.utc),
end_at=datetime(2026, 8, 5, 10, tzinfo=timezone.utc),
organizer={
"value": "mailto:organizer@example.test",
"params": {"CN": ["Organizer"]},
},
attendees=(
CalendarInvitationAttendeeRequest(
address="ada@example.test",
@@ -173,6 +178,92 @@ class CalendarInvitationCapabilityTests(unittest.TestCase):
self.assertFalse(updated.recurrence_supported)
self.assertTrue(updated.degraded_reasons)
def test_invitation_render_batch_lookup_and_summary(self) -> None:
request = self.request()
payload = self.provider.render_invitation(request)
created = self.provider.upsert_invitation(
self.session,
tenant_id="tenant-1",
user_id=None,
request=request,
)
self.assertIn("METHOD:REQUEST", payload)
self.assertIn(f"UID:{created.uid}", payload)
self.assertEqual(
created,
self.provider.get_invitations(
self.session,
tenant_id="tenant-1",
correlation_ids=(created.correlation_id,),
)[created.correlation_id],
)
summary = self.provider.summarize_invitations(
self.session,
tenant_id="tenant-1",
source_module="campaigns",
source_resource_type="campaign_recipient",
source_resource_id="recipient-1",
)
self.assertEqual(1, summary["invitation_count"])
self.assertEqual(1, summary["pending_response_count"])
calendars = self.provider.list_calendars(
self.session,
tenant_id="tenant-1",
)
self.assertEqual((self.calendar.id,), tuple(item.id for item in calendars))
self.assertTrue(calendars[0].writable)
def test_icalendar_reply_updates_attendee_status(self) -> None:
created = self.provider.upsert_invitation(
self.session,
tenant_id="tenant-1",
user_id=None,
request=self.request(),
)
reply = "\r\n".join(
(
"BEGIN:VCALENDAR",
"VERSION:2.0",
"METHOD:REPLY",
"BEGIN:VEVENT",
f"UID:{created.uid}",
"DTSTART:20260805T090000Z",
"ATTENDEE;CN=Ada;PARTSTAT=TENTATIVE:mailto:ada@example.test",
"END:VEVENT",
"END:VCALENDAR",
"",
)
)
recorded = self.provider.record_icalendar_reply(
self.session,
tenant_id="tenant-1",
icalendar=reply,
evidence={"profile_id": "mail-profile-1", "uid": "42"},
)
sequence_after_first_reply = self.session.get(
CalendarEvent,
created.event_id,
).sequence
replayed = self.provider.record_icalendar_reply(
self.session,
tenant_id="tenant-1",
icalendar=reply,
evidence={"profile_id": "mail-profile-1", "uid": "42"},
)
self.assertEqual(1, len(recorded))
self.assertEqual(1, len(replayed))
self.assertEqual(
"TENTATIVE",
recorded[0].attendees[0]["params"]["PARTSTAT"][0],
)
self.assertEqual(
sequence_after_first_reply,
self.session.get(CalendarEvent, created.event_id).sequence,
)
def test_reply_must_match_an_existing_attendee(self) -> None:
created = self.provider.upsert_invitation(
self.session,