feat: add calendar subscription token functionality and ICS generation
- 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.
This commit is contained in:
@@ -248,3 +248,50 @@ def test_duties_200_with_unknown_event_type_mapped_to_duty(client):
|
||||
assert len(data) == 1
|
||||
assert data[0]["event_type"] == "duty"
|
||||
assert data[0]["full_name"] == "User A"
|
||||
|
||||
|
||||
@patch("duty_teller.api.app.get_user_by_calendar_token")
|
||||
def test_calendar_ical_404_unknown_token(mock_get_user, client):
|
||||
"""GET /api/calendar/ical/{token}.ics with unknown token returns 404."""
|
||||
mock_get_user.return_value = None
|
||||
r = client.get("/api/calendar/ical/unknown-token-xyz.ics")
|
||||
assert r.status_code == 404
|
||||
assert "not found" in r.text.lower()
|
||||
mock_get_user.assert_called_once()
|
||||
|
||||
|
||||
@patch("duty_teller.api.app.build_personal_ics")
|
||||
@patch("duty_teller.api.app.get_duties_for_user")
|
||||
@patch("duty_teller.api.app.get_user_by_calendar_token")
|
||||
def test_calendar_ical_200_returns_only_that_users_duties(
|
||||
mock_get_user, mock_get_duties, mock_build_ics, client
|
||||
):
|
||||
"""GET /api/calendar/ical/{token}.ics returns ICS with only the token owner's duties."""
|
||||
from types import SimpleNamespace
|
||||
|
||||
mock_user = SimpleNamespace(id=1, full_name="User A")
|
||||
mock_get_user.return_value = mock_user
|
||||
duty = SimpleNamespace(
|
||||
id=10,
|
||||
user_id=1,
|
||||
start_at="2026-06-15T09:00:00Z",
|
||||
end_at="2026-06-15T18:00:00Z",
|
||||
event_type="duty",
|
||||
)
|
||||
mock_get_duties.return_value = [(duty, "User A")]
|
||||
mock_build_ics.return_value = (
|
||||
b"BEGIN:VCALENDAR\r\nVEVENT\r\n2026-06-15\r\nEND:VCALENDAR"
|
||||
)
|
||||
|
||||
r = client.get("/api/calendar/ical/valid-token.ics")
|
||||
assert r.status_code == 200
|
||||
assert r.headers.get("content-type", "").startswith("text/calendar")
|
||||
assert b"BEGIN:VCALENDAR" in r.content
|
||||
mock_get_user.assert_called_once()
|
||||
mock_get_duties.assert_called_once_with(ANY, 1, from_date=ANY, to_date=ANY)
|
||||
mock_build_ics.assert_called_once()
|
||||
# Only User A's duty was passed to build_personal_ics
|
||||
duties_arg = mock_build_ics.call_args[0][0]
|
||||
assert len(duties_arg) == 1
|
||||
assert duties_arg[0][0].user_id == 1
|
||||
assert duties_arg[0][1] == "User A"
|
||||
|
||||
Reference in New Issue
Block a user