Add correlated calendar invitation capability
This commit is contained in:
+109
-2
@@ -3,8 +3,25 @@ from __future__ import annotations
|
||||
import unittest
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from govoplan_calendar.backend.capabilities import SqlCalendarSchedulingProvider
|
||||
from govoplan_core.core.calendar import CalendarCapabilityError, CalendarEventRequest
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from govoplan_access.backend.db import models as access_models # noqa: F401
|
||||
from govoplan_calendar.backend.capabilities import (
|
||||
SqlCalendarInvitationProvider,
|
||||
SqlCalendarSchedulingProvider,
|
||||
)
|
||||
from govoplan_calendar.backend.schemas import CalendarCollectionCreateRequest
|
||||
from govoplan_calendar.backend.service import create_calendar
|
||||
from govoplan_core.core.calendar import (
|
||||
CalendarCapabilityError,
|
||||
CalendarEventRequest,
|
||||
CalendarInvitationAttendeeRequest,
|
||||
CalendarInvitationRequest,
|
||||
)
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_core.tenancy.scope import create_scope_tables
|
||||
from govoplan_tenancy.backend.db.models import Tenant
|
||||
|
||||
|
||||
class CalendarSchedulingCapabilityTests(unittest.TestCase):
|
||||
@@ -23,5 +40,95 @@ class CalendarSchedulingCapabilityTests(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class CalendarInvitationCapabilityTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.engine = create_engine("sqlite:///:memory:")
|
||||
create_scope_tables(self.engine)
|
||||
Base.metadata.create_all(bind=self.engine)
|
||||
self.Session = sessionmaker(bind=self.engine)
|
||||
self.session = self.Session()
|
||||
self.session.add(Tenant(id="tenant-1", slug="tenant-1", name="Tenant"))
|
||||
self.calendar = create_calendar(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
user_id=None,
|
||||
payload=CalendarCollectionCreateRequest(name="Invitations"),
|
||||
)
|
||||
self.provider = SqlCalendarInvitationProvider()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.session.close()
|
||||
Base.metadata.drop_all(bind=self.engine)
|
||||
self.engine.dispose()
|
||||
|
||||
def request(self, *, summary: str = "Planning") -> CalendarInvitationRequest:
|
||||
return CalendarInvitationRequest(
|
||||
correlation_id="campaign-1:recipient-1",
|
||||
source_module="campaigns",
|
||||
source_resource_type="campaign_recipient",
|
||||
source_resource_id="recipient-1",
|
||||
calendar_id=self.calendar.id,
|
||||
summary=summary,
|
||||
start_at=datetime(2026, 8, 5, 9, tzinfo=timezone.utc),
|
||||
end_at=datetime(2026, 8, 5, 10, tzinfo=timezone.utc),
|
||||
attendees=(
|
||||
CalendarInvitationAttendeeRequest(
|
||||
address="ada@example.test",
|
||||
name="Ada",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
def test_upsert_and_response_reconciliation_preserve_correlation(self) -> None:
|
||||
created = self.provider.upsert_invitation(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
user_id=None,
|
||||
request=self.request(),
|
||||
)
|
||||
response = self.provider.record_response(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
correlation_id=created.correlation_id,
|
||||
attendee_address="ADA@example.test",
|
||||
participation_status="accepted",
|
||||
evidence={"transport": "ical-reply"},
|
||||
)
|
||||
updated = self.provider.upsert_invitation(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
user_id=None,
|
||||
request=self.request(summary="Updated planning"),
|
||||
)
|
||||
loaded = self.provider.get_invitation(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
correlation_id=created.correlation_id,
|
||||
)
|
||||
|
||||
self.assertEqual(created.event_id, updated.event_id)
|
||||
self.assertEqual("ACCEPTED", response.attendees[0]["params"]["PARTSTAT"][0])
|
||||
self.assertEqual("ACCEPTED", updated.attendees[0]["params"]["PARTSTAT"][0])
|
||||
self.assertEqual(updated, loaded)
|
||||
self.assertFalse(updated.recurrence_supported)
|
||||
self.assertTrue(updated.degraded_reasons)
|
||||
|
||||
def test_reply_must_match_an_existing_attendee(self) -> None:
|
||||
created = self.provider.upsert_invitation(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
user_id=None,
|
||||
request=self.request(),
|
||||
)
|
||||
with self.assertRaisesRegex(CalendarCapabilityError, "not an attendee"):
|
||||
self.provider.record_response(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
uid=created.uid,
|
||||
attendee_address="mallory@example.test",
|
||||
participation_status="DECLINED",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user