diff --git a/src/notifier/notifier.test.ts b/src/notifier/notifier.test.ts index 8573d81..71e7bf1 100644 --- a/src/notifier/notifier.test.ts +++ b/src/notifier/notifier.test.ts @@ -9,11 +9,14 @@ import { TIMEZONE } from "./schedule.ts"; const zdt = (s: string) => Temporal.ZonedDateTime.from(`${s}[${TIMEZONE}]`); +// Start/end dates use the UTC `Z` designator, matching what the OpenMeet API +// actually returns (see slack.test.ts fixtures). The notifier parses these as +// instants and renders them in TIMEZONE for digest-window comparisons. const ev = ( slug: string, - startDate = "2026-06-10T18:00:00", + startDate = "2026-06-10T18:00:00Z", name = slug, - endDate = "2026-06-15T20:00:00", + endDate = "2026-06-15T20:00:00Z", ): OpenMeetEvent => ({ name, slug, startDate, endDate }); // A Config with only the fields the notifier reads. The injected client @@ -172,10 +175,10 @@ test("start: new-event notifications and the weekly digest both fire in the same let t = zdt("2026-06-08T06:00:00"); const now = () => t; - const prime = [ev("known", "2026-06-10T18:00:00")]; + const prime = [ev("known", "2026-06-10T18:00:00Z")]; const withNew = [ - ev("known", "2026-06-10T18:00:00"), - ev("fresh", "2026-06-11T18:00:00"), // new this iteration, and in the digest window + ev("known", "2026-06-10T18:00:00Z"), + ev("fresh", "2026-06-11T18:00:00Z"), // new this iteration, and in the digest window ]; const client = fakeClient([prime, withNew], aborter, (i) => { if (i === 1) t = zdt("2026-06-08T08:00:00"); // advance past the 07:30 digest @@ -241,13 +244,15 @@ test("start: weekly digest fires at the scheduled time and only includes events let t = zdt("2026-06-08T06:00:00"); const now = () => t; + // now advances to 2026-06-08T08:00:00 [America/Chicago] = 13:00Z (CDT, UTC-5), + // so now+7d = 2026-06-15T13:00:00Z. const events = [ - ev("past", "2026-06-07T18:00:00"), // before now -> excluded - ev("today", "2026-06-08T18:00:00"), // in window -> included - ev("midweek", "2026-06-10T18:00:00"), // in window -> included - ev("lateweek", "2026-06-14T18:00:00"), // in window -> included - ev("boundary", "2026-06-15T08:00:00"), // == now+7d, excluded (strict <) - ev("beyond", "2026-06-20T18:00:00"), // outside window -> excluded + ev("past", "2026-06-07T18:00:00Z"), // before now -> excluded + ev("today", "2026-06-08T18:00:00Z"), // in window -> included + ev("midweek", "2026-06-10T18:00:00Z"), // in window -> included + ev("lateweek", "2026-06-14T18:00:00Z"), // in window -> included + ev("boundary", "2026-06-15T13:00:00Z"), // == now+7d, excluded (strict <) + ev("beyond", "2026-06-20T18:00:00Z"), // outside window -> excluded ]; const client = fakeClient([events, events], aborter, (i) => { diff --git a/src/notifier/notifier.ts b/src/notifier/notifier.ts index 59214d8..d01d049 100644 --- a/src/notifier/notifier.ts +++ b/src/notifier/notifier.ts @@ -69,7 +69,10 @@ export async function newNotifier({ const nowZdt = now(); const oneWeekFromNow = nowZdt.add({ days: 7 }); const eventsInNextWeek = events.filter((e) => { - const start = Temporal.PlainDateTime.from(e.startDate).toZonedDateTime(TIMEZONE); + // OpenMeet returns startDate as a UTC instant (e.g. "2026-06-22T12:31:00Z"); + // PlainDateTime.from rejects the Z designator, so parse as an Instant + // and render it in our local timezone for the digest-window compare. + const start = Temporal.Instant.from(e.startDate).toZonedDateTimeISO(TIMEZONE); return ( Temporal.ZonedDateTime.compare(start, nowZdt) >= 0 && Temporal.ZonedDateTime.compare(start, oneWeekFromNow) < 0