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 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 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()