diff --git a/src/app/api/medications/[id]/intake/__tests__/route.test.ts b/src/app/api/medications/[id]/intake/__tests__/route.test.ts index fc87a09b3..984f5fb73 100644 --- a/src/app/api/medications/[id]/intake/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/intake/__tests__/route.test.ts @@ -96,7 +96,14 @@ import { getSession } from "@/lib/auth/session"; const SESSION_OK = { session: { id: "sess-1", expiresAt: new Date(Date.now() + 3_600_000) }, - user: { id: "user-1", username: "marc", role: "USER" as const }, + user: { + id: "user-1", + username: "marc", + role: "USER" as const, + // Pin the user timezone so slot-instant resolution is deterministic + // regardless of the host TZ (CI runs in UTC, local in Europe/Berlin). + timezone: "Europe/Berlin", + }, }; const MED_OK = { id: "med-1", userId: "user-1" }; diff --git a/tests/integration/analytics-health-score.test.ts b/tests/integration/analytics-health-score.test.ts index b6e8a5cea..cfaec655b 100644 --- a/tests/integration/analytics-health-score.test.ts +++ b/tests/integration/analytics-health-score.test.ts @@ -163,7 +163,12 @@ describe("GET /api/analytics — Health Score", () => { }, }); for (let i = 0; i < 30; i++) { + // Anchor the take to a fixed wall-clock instant that lands inside the + // 08:00–10:00 Berlin window (07:30 UTC ≈ 09:30 Berlin) so the slot + // matcher reads "on-time" deterministically regardless of host TZ — + // an unaligned `now - i*DAY` shifts its Berlin hour with the runner TZ. const scheduledFor = new Date(now - i * DAY); + scheduledFor.setUTCHours(7, 30, 0, 0); await prisma.medicationIntakeEvent.create({ data: { userId: user.id, @@ -283,7 +288,12 @@ describe("GET /api/analytics — Health Score", () => { }, }); for (let i = 0; i < 30; i++) { + // Anchor the take to a fixed wall-clock instant that lands inside the + // 08:00–10:00 Berlin window (07:30 UTC ≈ 09:30 Berlin) so the slot + // matcher reads "on-time" deterministically regardless of host TZ — + // an unaligned `now - i*DAY` shifts its Berlin hour with the runner TZ. const scheduledFor = new Date(now - i * DAY); + scheduledFor.setUTCHours(7, 30, 0, 0); await prisma.medicationIntakeEvent.create({ data: { userId: user.id, diff --git a/tests/integration/analytics-sleep-stages.test.ts b/tests/integration/analytics-sleep-stages.test.ts index 983d06b6f..2b8fdd13d 100644 --- a/tests/integration/analytics-sleep-stages.test.ts +++ b/tests/integration/analytics-sleep-stages.test.ts @@ -100,7 +100,11 @@ describe("GET /api/analytics — sleep-stage aggregation", () => { // One night with the four iOS-16+ stages tagged. The analytics // route should surface a single Measurement-summary datapoint // (~480 minutes total) plus a per-stage breakdown. - const baseDate = new Date("2026-05-09T01:00:00.000Z"); + // Anchor the night comfortably inside the route's trailing 30-day + // window (relative to `now`) so the test never drifts onto the + // window edge as the calendar advances or the host TZ shifts. + const baseDate = new Date(Date.now() - 5 * 24 * 60 * 60 * 1000); + baseDate.setUTCHours(1, 0, 0, 0); const stages: Array<{ stage: "IN_BED" | "CORE" | "DEEP" | "REM"; min: number; diff --git a/tests/integration/medication-weekly-compliance.test.ts b/tests/integration/medication-weekly-compliance.test.ts index 6ba684e23..11a9004c7 100644 --- a/tests/integration/medication-weekly-compliance.test.ts +++ b/tests/integration/medication-weekly-compliance.test.ts @@ -212,15 +212,20 @@ describe("weekly injectable compliance (Mounjaro 0% regression)", () => { rrule: "FREQ=WEEKLY;BYDAY=MO", windowStart: "07:30", }); - // Log only 12 of the trailing 13 Mondays (skip the most recent). + // Take the most-recent Monday on time, then skip ONE older Monday + // (`lastMonday − 7d`, which sits 9 days before the pinned now — past + // the weekly 4-day overdue tail, so it counts as definitively missed + // rather than still-takeable) and take the 11 Mondays before it. The + // gap is a real, settled miss, not a head-of-window overdue slot. + await logWeeklyIntakes(id, lastMonday, 1); await logWeeklyIntakes( id, - new Date(lastMonday.getTime() - 7 * DAY_MS), - 12, + new Date(lastMonday.getTime() - 14 * DAY_MS), + 11, ); const { compliance90 } = await readCompliance(id); - // 12 of 13 expected → ~92%, definitively non-zero and below 100. + // 12 taken of 13 expected → ~92%, definitively non-zero and below 100. expect(compliance90.rate).toBeGreaterThan(80); expect(compliance90.rate).toBeLessThan(100); expect(compliance90.missed).toBeGreaterThanOrEqual(1);