Files
govoplan-calendar/tests/test_service.py

177 lines
6.3 KiB
Python

from __future__ import annotations
import unittest
from datetime import datetime, timedelta, timezone
from types import SimpleNamespace
from unittest.mock import patch
from govoplan_calendar.backend.schemas import CalendarCollectionDeleteRequest, CalendarEventResponse
from govoplan_calendar.backend.service import calendar_is_visible_to_principal, delete_calendar, event_response
class FakeSession:
def __init__(self) -> None:
self.flushed = False
def flush(self) -> None:
self.flushed = True
class CalendarServiceResponseTests(unittest.TestCase):
def test_event_response_marks_naive_database_datetimes_as_utc(self) -> None:
event = SimpleNamespace(
id="event-1",
tenant_id="tenant-1",
calendar_id="calendar-1",
uid="event-1@example.test",
recurrence_id=None,
sequence=0,
summary="Planning",
description=None,
location=None,
status="CONFIRMED",
transparency="OPAQUE",
classification="PUBLIC",
start_at=datetime(2026, 7, 7, 18, 0),
end_at=datetime(2026, 7, 7, 19, 0),
duration_seconds=None,
all_day=False,
timezone=None,
organizer=None,
attendees=[],
categories=[],
rrule=None,
rdate=[],
exdate=[],
reminders=[],
attachments=[],
related_to=[],
source_kind="local",
source_href=None,
etag=None,
icalendar={},
created_at=datetime(2026, 7, 7, 17, 55),
updated_at=datetime(2026, 7, 7, 17, 55),
metadata_={},
)
payload = event_response(event)
response = CalendarEventResponse.model_validate(payload)
self.assertEqual(response.start_at.utcoffset(), timedelta(0))
self.assertEqual(response.end_at.utcoffset(), timedelta(0))
self.assertEqual(response.created_at.utcoffset(), timedelta(0))
self.assertEqual(response.updated_at.utcoffset(), timedelta(0))
self.assertEqual(response.start_at.tzinfo, timezone.utc)
class CalendarVisibilityTests(unittest.TestCase):
@staticmethod
def calendar(**overrides):
values = {
"visibility": "private",
"owner_type": "user",
"owner_id": "owner-1",
"created_by_user_id": "creator-1",
}
values.update(overrides)
return SimpleNamespace(**values)
def test_tenant_shared_and_public_calendars_are_visible_to_readers(self) -> None:
for visibility in ("tenant", "shared", "public"):
with self.subTest(visibility=visibility):
self.assertTrue(
calendar_is_visible_to_principal(
self.calendar(visibility=visibility),
user_id="reader-1",
)
)
def test_private_calendar_is_visible_to_user_owner_creator_or_group_member(self) -> None:
self.assertTrue(
calendar_is_visible_to_principal(
self.calendar(),
user_id="owner-1",
)
)
self.assertTrue(
calendar_is_visible_to_principal(
self.calendar(),
user_id="creator-1",
)
)
self.assertTrue(
calendar_is_visible_to_principal(
self.calendar(owner_type="group", owner_id="group-1"),
user_id="member-1",
group_ids=("group-1",),
)
)
def test_private_calendar_is_hidden_from_other_readers_but_visible_to_admin(self) -> None:
calendar = self.calendar()
self.assertFalse(
calendar_is_visible_to_principal(
calendar,
user_id="other-1",
group_ids=("group-2",),
)
)
self.assertTrue(
calendar_is_visible_to_principal(
calendar,
user_id="other-1",
can_admin=True,
)
)
class CalendarServiceDeleteTests(unittest.TestCase):
def test_delete_default_calendar_soft_deletes_calendar_and_events(self) -> None:
session = FakeSession()
event = SimpleNamespace(calendar_id="calendar-1", deleted_at=None)
calendar = SimpleNamespace(id="calendar-1", is_default=True, deleted_at=None, events=[event])
with patch("govoplan_calendar.backend.service.get_calendar", return_value=calendar):
delete_calendar(
session,
tenant_id="tenant-1",
calendar_id="calendar-1",
payload=CalendarCollectionDeleteRequest(event_action="delete"),
)
self.assertTrue(session.flushed)
self.assertFalse(calendar.is_default)
self.assertIsNotNone(calendar.deleted_at)
self.assertEqual(event.deleted_at, calendar.deleted_at)
self.assertEqual(event.calendar_id, "calendar-1")
def test_delete_calendar_can_move_events_to_another_calendar_and_make_it_default(self) -> None:
session = FakeSession()
event = SimpleNamespace(calendar_id="calendar-1", deleted_at=None)
calendar = SimpleNamespace(id="calendar-1", is_default=True, deleted_at=None, events=[event])
target = SimpleNamespace(id="calendar-2", is_default=False, deleted_at=None, events=[])
with (
patch("govoplan_calendar.backend.service.get_calendar", side_effect=[calendar, target]),
patch("govoplan_calendar.backend.service.clear_default_calendar") as clear_default_calendar,
):
delete_calendar(
session,
tenant_id="tenant-1",
calendar_id="calendar-1",
payload=CalendarCollectionDeleteRequest(event_action="move", target_calendar_id="calendar-2", make_target_default=True),
)
self.assertTrue(session.flushed)
clear_default_calendar.assert_called_once_with(session, tenant_id="tenant-1")
self.assertFalse(calendar.is_default)
self.assertTrue(target.is_default)
self.assertIsNotNone(calendar.deleted_at)
self.assertIsNone(event.deleted_at)
self.assertEqual(event.calendar_id, "calendar-2")
if __name__ == "__main__":
unittest.main()