Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions backend/InTheKnow.Tests/CheckerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,29 @@ public async Task GenericFeed()
Assert.True(await h.Db.ScalarAsync("SELECT count(*) FROM event_snapshot WHERE calendar_id = $1", id) > before);
}

// Google Calendar's shape: X-WR-TIMEZONE, all-day VALUE=DATE, UTC times, and a moved occurrence as a RECURRENCE-ID override.
[Fact]
public async Task GoogleFeedTimeMovesAreRecordedAndFannedOut()
{
var (h, id, url) = await BaselineAsync("google.ics", "calendar.google.com");
await using var _h = h;
Assert.Equal(6, await h.Db.ScalarAsync("SELECT count(*) FROM event_snapshot WHERE calendar_id = $1", id));
var channel = await ChannelAsync(h.Db, h.S1);

h.Http.Map[url] = "google-edited.ics";
await NextIntervalAsync(h);
Assert.Equal(
[
("allday@google.com", "changed", "time"),
("single@google.com", "changed", "time"),
("weekly@google.com|2026-11-09T08:00:00Z", "changed", "time"),
], Sorted(await h.ChangesAsync(id)));
var single = await h.Db.RowsAsync("SELECT old_value::text, new_value::text FROM detected_change WHERE event_key = 'single@google.com'");
Assert.Equal([["""{"end": "2026-11-12T16:30:00Z", "start": "2026-11-12T16:00:00Z"}""",
"""{"end": "2026-11-12T17:30:00Z", "start": "2026-11-12T17:00:00Z"}"""]], single);
Assert.Equal([[channel, 3L]], await h.Db.RowsAsync("SELECT channel_id, count(*) FROM delivery WHERE status = 'pending' GROUP BY channel_id"));
}

[Fact]
public async Task UnchangedFeedWritesNoSnapshotRows()
{
Expand Down
76 changes: 76 additions & 0 deletions backend/InTheKnow.Tests/Fixtures/google-edited.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Synthetic Google test calendar
X-WR-TIMEZONE:Europe/Brussels
BEGIN:VTIMEZONE
TZID:Europe/Brussels
X-LIC-LOCATION:Europe/Brussels
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;VALUE=DATE:20261111
DTEND;VALUE=DATE:20261112
DTSTAMP:20260924T120000Z
UID:allday@google.com
CREATED:20260901T100000Z
LAST-MODIFIED:20260901T100000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:All-day event
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART:20261112T170000Z
DTEND:20261112T173000Z
DTSTAMP:20260924T120000Z
UID:single@google.com
CREATED:20260901T100000Z
LAST-MODIFIED:20260901T100000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Single event
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Europe/Brussels:20261102T090000
DTEND;TZID=Europe/Brussels:20261102T100000
DTSTAMP:20260924T120000Z
UID:weekly@google.com
RRULE:FREQ=WEEKLY;COUNT=4
CREATED:20260901T100000Z
LAST-MODIFIED:20260901T100000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Weekly lesson
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Europe/Brussels:20261109T110000
DTEND;TZID=Europe/Brussels:20261109T120000
DTSTAMP:20260924T120000Z
UID:weekly@google.com
RECURRENCE-ID;TZID=Europe/Brussels:20261109T090000
CREATED:20260901T100000Z
LAST-MODIFIED:20260901T100000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Weekly lesson
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
63 changes: 63 additions & 0 deletions backend/InTheKnow.Tests/Fixtures/google.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Synthetic Google test calendar
X-WR-TIMEZONE:Europe/Brussels
BEGIN:VTIMEZONE
TZID:Europe/Brussels
X-LIC-LOCATION:Europe/Brussels
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;VALUE=DATE:20261110
DTEND;VALUE=DATE:20261111
DTSTAMP:20260924T120000Z
UID:allday@google.com
CREATED:20260901T100000Z
LAST-MODIFIED:20260901T100000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:All-day event
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART:20261112T160000Z
DTEND:20261112T163000Z
DTSTAMP:20260924T120000Z
UID:single@google.com
CREATED:20260901T100000Z
LAST-MODIFIED:20260901T100000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Single event
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Europe/Brussels:20261102T090000
DTEND;TZID=Europe/Brussels:20261102T100000
DTSTAMP:20260924T120000Z
UID:weekly@google.com
RRULE:FREQ=WEEKLY;COUNT=4
CREATED:20260901T100000Z
LAST-MODIFIED:20260901T100000Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Weekly lesson
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
Loading