Files
reflector/server/tests/test_ics_dedup_integration.py

132 lines
4.6 KiB
Python

"""Integration test: ICS dedup against Max's real calendar feed.
Fetches the live ICS feed, picks an upcoming event, then simulates
a duplicate (different UID, same time window) to verify dedup logic.
"""
from datetime import datetime, timedelta, timezone
from unittest.mock import AsyncMock, patch
import pytest
from icalendar import Calendar
from reflector.db import get_database
from reflector.db.calendar_events import CalendarEvent, calendar_events_controller
from reflector.db.meetings import meetings
from reflector.db.rooms import rooms_controller
from reflector.services.ics_sync import ICSFetchService
from reflector.worker.ics_sync import create_upcoming_meetings_for_event
MAX_ICS_URL = (
"https://user.fm/calendar/v1-929f6269635c3c3bebd57c32b23bf448/Max%20Calendar.ics"
)
def _find_upcoming_event(
fetch_service: ICSFetchService, calendar: Calendar
) -> dict | None:
"""Find any upcoming event within next 48h (ignoring room matching)."""
now = datetime.now(timezone.utc)
window_end = now + timedelta(hours=48)
for component in calendar.walk():
if component.name != "VEVENT":
continue
status = component.get("STATUS", "").upper()
if status == "CANCELLED":
continue
event_data = fetch_service._parse_event(component)
if event_data and now < event_data["start_time"] < window_end:
return event_data
return None
@pytest.mark.asyncio
async def test_dedup_with_real_ics_feed():
"""Fetch Max's real ICS, pick an upcoming event, inject duplicate UID, verify dedup."""
# 1. Fetch and parse real ICS feed
fetch_service = ICSFetchService()
ics_content = await fetch_service.fetch_ics(MAX_ICS_URL)
calendar = fetch_service.parse_ics(ics_content)
upcoming = _find_upcoming_event(fetch_service, calendar)
if upcoming is None:
pytest.skip("No upcoming events in Max's calendar within 48h window")
# 2. Create a test room
room = await rooms_controller.add(
name="max-dedup-integration-test",
user_id="test-user",
zulip_auto_post=False,
zulip_stream="",
zulip_topic="",
is_locked=False,
room_mode="normal",
recording_type="cloud",
recording_trigger="automatic-2nd-participant",
is_shared=False,
ics_url=MAX_ICS_URL,
ics_enabled=True,
)
start_time = upcoming["start_time"]
end_time = upcoming["end_time"]
title = upcoming["title"]
# 3. Insert two calendar events with different UIDs but same time window
event1 = await calendar_events_controller.upsert(
CalendarEvent(
room_id=room.id,
ics_uid=upcoming["ics_uid"], # real UID from the feed
title=title,
start_time=start_time,
end_time=end_time,
)
)
event2 = await calendar_events_controller.upsert(
CalendarEvent(
room_id=room.id,
ics_uid=f"SYNTHETIC-DUPLICATE-{upcoming['ics_uid']}",
title=title,
start_time=start_time,
end_time=end_time,
)
)
create_window = datetime.now(timezone.utc) - timedelta(minutes=6)
call_count = 0
# 4. Mock the platform client, run both events through meeting creation
with patch(
"reflector.worker.ics_sync.create_platform_client"
) as mock_platform_factory:
mock_client = AsyncMock()
async def mock_create_meeting(room_name_prefix, *, end_date, room):
nonlocal call_count
call_count += 1
return AsyncMock(
meeting_id=f"integ-meeting-{call_count}",
room_name=f"max-dedup-integration-test-{call_count}",
room_url=f"https://mock.video/max-dedup-integration-test-{call_count}",
host_room_url=f"https://mock.video/max-dedup-integration-test-{call_count}?host=true",
)
mock_client.create_meeting = mock_create_meeting
mock_client.upload_logo = AsyncMock()
mock_platform_factory.return_value = mock_client
await create_upcoming_meetings_for_event(event1, create_window, room)
await create_upcoming_meetings_for_event(event2, create_window, room)
# 5. Verify: only 1 meeting created
results = await get_database().fetch_all(
meetings.select().where(meetings.c.room_id == room.id)
)
assert len(results) == 1, (
f"Expected 1 meeting (dedup should block duplicate), got {len(results)}. "
f"Event: {title} @ {start_time} - {end_time}"
)
assert call_count == 1, f"create_meeting called {call_count} times, expected 1"