- Introduced a new database model for calendar subscription tokens, allowing users to generate unique tokens for accessing their personal calendar. - Implemented API endpoint to return ICS files containing only the subscribing user's duties, enhancing user experience with personalized calendar access. - Added utility functions for generating ICS files from user duties, ensuring proper formatting and timezone handling. - Updated command handlers to support the new calendar link feature, providing users with easy access to their personal calendar subscriptions. - Included unit tests for the new functionality, ensuring reliability and correctness of token generation and ICS file creation.
61 lines
2.1 KiB
Python
61 lines
2.1 KiB
Python
"""Unit tests for personal calendar ICS generation."""
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from icalendar import Calendar as ICalendar
|
|
|
|
from duty_teller.api.personal_calendar_ics import build_personal_ics
|
|
|
|
|
|
# Minimal Duty-like object for build_personal_ics(duties_with_name: list[tuple[Duty, str]])
|
|
def _duty(id_, start_at, end_at, event_type="duty"):
|
|
return (
|
|
SimpleNamespace(
|
|
id=id_, start_at=start_at, end_at=end_at, event_type=event_type
|
|
),
|
|
"Test User",
|
|
)
|
|
|
|
|
|
def test_build_personal_ics_empty_returns_valid_calendar():
|
|
"""Empty list produces VCALENDAR with no VEVENTs."""
|
|
ics = build_personal_ics([])
|
|
assert ics.startswith(b"BEGIN:VCALENDAR")
|
|
assert ics.rstrip().endswith(b"END:VCALENDAR")
|
|
cal = ICalendar.from_ical(ics)
|
|
assert cal is not None
|
|
events = [c for c in cal.walk() if c.name == "VEVENT"]
|
|
assert len(events) == 0
|
|
|
|
|
|
def test_build_personal_ics_one_duty():
|
|
"""One duty produces one VEVENT with correct DTSTART/DTEND/SUMMARY."""
|
|
duties = [
|
|
_duty(1, "2025-02-20T09:00:00Z", "2025-02-20T18:00:00Z", "duty"),
|
|
]
|
|
ics = build_personal_ics(duties)
|
|
cal = ICalendar.from_ical(ics)
|
|
assert cal is not None
|
|
events = [c for c in cal.walk() if c.name == "VEVENT"]
|
|
assert len(events) == 1
|
|
ev = events[0]
|
|
assert ev.get("summary") == "Duty"
|
|
assert "2025-02-20" in str(ev.get("dtstart").dt)
|
|
assert "2025-02-20" in str(ev.get("dtend").dt)
|
|
assert b"duty-1@duty-teller" in ics or "duty-1@duty-teller" in ics.decode("utf-8")
|
|
|
|
|
|
def test_build_personal_ics_event_types():
|
|
"""Unavailable and vacation get correct SUMMARY."""
|
|
duties = [
|
|
_duty(1, "2025-02-20T09:00:00Z", "2025-02-21T09:00:00Z", "unavailable"),
|
|
_duty(2, "2025-02-25T00:00:00Z", "2025-03-01T00:00:00Z", "vacation"),
|
|
]
|
|
ics = build_personal_ics(duties)
|
|
cal = ICalendar.from_ical(ics)
|
|
events = [c for c in cal.walk() if c.name == "VEVENT"]
|
|
assert len(events) == 2
|
|
summaries = [str(ev.get("summary")) for ev in events]
|
|
assert "Unavailable" in summaries
|
|
assert "Vacation" in summaries
|