mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
test: date fixtures that feed a clock-relative window relative to now
A fixture dated near the day it was written passes until the date slides out of a window the route counts back from the clock. Five did: a snooze until a fixed date that becomes the past in January, a prognosis read against the wall clock over days seeded at a fixed date, sleep rows and a weight streak read through windows that end now, and a sync token that would expire on a fixed date. The e2e mood heatmap mock and a timeline URL are dated from today as well. A guard scans the integration and e2e trees for near-now absolute dates within reach of a window parameter, with an allowlist for files whose windows are fixed on both ends.
This commit is contained in:
1 parent
73c4dda224
commit
7be05608f8
8 files changed
+263
-15
No files matched your search
@@ -42,7 +42,9 @@ async function seedConnection(userId: string): Promise<void> {
|
||||
googleUserId: `google-${userId}`,
|
||||
accessToken: "encrypted-access",
|
||||
refreshToken: "encrypted-refresh",
|
||||
tokenExpiresAt: new Date("2027-01-01T00:00:00.000Z"),
|
||||
// Valid for a year from whenever the suite runs: a fixed date would
|
||||
// expire and send the sync down the refresh path.
|
||||
tokenExpiresAt: new Date(Date.now() + 365 * 86_400_000),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* tooltip.
|
||||
*/
|
||||
import { NextRequest } from "next/server";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { cookieJar } from "./mock-next-headers";
|
||||
import { getPrismaClient, switchSessionTo, truncateAllTables } from "./setup";
|
||||
@@ -46,7 +46,14 @@ vi.mock("@/lib/db-compat", () => ({
|
||||
}));
|
||||
|
||||
/** Fixed clock: a ladder test that slides with the calendar is a date bomb. */
|
||||
const NOW = new Date("2026-08-08T09:00:00.000Z");
|
||||
const FIXED_NOW = new Date("2026-08-08T09:00:00.000Z");
|
||||
/**
|
||||
* The instant the seed and the job are dated against. The ladder cases keep
|
||||
* the fixed clock; the route cases move it to the real one, because the route
|
||||
* reads the forecast against the wall clock and a forecast seeded at a fixed
|
||||
* date goes stale under it as the calendar moves on.
|
||||
*/
|
||||
let NOW = FIXED_NOW;
|
||||
|
||||
function dayKey(daysAgo: number): string {
|
||||
return new Date(NOW.getTime() - daysAgo * DAY_MS).toISOString().slice(0, 10);
|
||||
@@ -271,6 +278,13 @@ describe("the minimum-n ladder, end to end", () => {
|
||||
});
|
||||
|
||||
describe("GET /api/mood/prognosis", () => {
|
||||
beforeEach(() => {
|
||||
NOW = new Date();
|
||||
});
|
||||
afterEach(() => {
|
||||
NOW = FIXED_NOW;
|
||||
});
|
||||
|
||||
/**
|
||||
* A signed-in browser session.
|
||||
*
|
||||
|
||||
@@ -795,7 +795,12 @@ manageContract("POST /api/measurement-reminders/[id]/snooze", {
|
||||
POST as Handler,
|
||||
"POST",
|
||||
`/api/measurement-reminders/${row.id}/snooze`,
|
||||
{ until: "2027-01-15" },
|
||||
// A snooze must end in the future; a fixed date stops being one.
|
||||
{
|
||||
until: new Date(Date.now() + 90 * 86_400_000)
|
||||
.toISOString()
|
||||
.slice(0, 10),
|
||||
},
|
||||
{ id: row.id },
|
||||
);
|
||||
},
|
||||
|
||||
@@ -34,6 +34,15 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { cookieJar, headerJar } from "./mock-next-headers";
|
||||
import { getPrismaClient, truncateAllTables } from "./setup";
|
||||
|
||||
/**
|
||||
* The night the fixtures sleep, ten days back from whenever the suite runs.
|
||||
* The analytics route summarises a window that ends now; a fixed date slides
|
||||
* out of it as the calendar moves on.
|
||||
*/
|
||||
const SLEEP_DAY = new Date(Date.now() - 10 * 86_400_000)
|
||||
.toISOString()
|
||||
.slice(0, 10);
|
||||
|
||||
vi.mock("next/headers", async () => {
|
||||
const { cookieJar, headerJar } = await import("./mock-next-headers");
|
||||
return {
|
||||
@@ -130,7 +139,7 @@ describe("GET /api/analytics — two-axis canonical source resolution (W8c)", ()
|
||||
value: 420,
|
||||
unit: "minutes",
|
||||
source: "WITHINGS",
|
||||
measuredAt: new Date("2026-05-12T07:00:00.000Z"),
|
||||
measuredAt: new Date(`${SLEEP_DAY}T07:00:00.000Z`),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -162,7 +171,7 @@ describe("GET /api/analytics — two-axis canonical source resolution (W8c)", ()
|
||||
value: 480,
|
||||
unit: "minutes",
|
||||
source: "APPLE_HEALTH",
|
||||
measuredAt: new Date("2026-05-12T06:00:00.000Z"),
|
||||
measuredAt: new Date(`${SLEEP_DAY}T06:00:00.000Z`),
|
||||
externalId: "ah-sleep-1",
|
||||
},
|
||||
});
|
||||
@@ -174,7 +183,7 @@ describe("GET /api/analytics — two-axis canonical source resolution (W8c)", ()
|
||||
value: 410,
|
||||
unit: "minutes",
|
||||
source: "WITHINGS",
|
||||
measuredAt: new Date("2026-05-12T07:00:00.000Z"),
|
||||
measuredAt: new Date(`${SLEEP_DAY}T07:00:00.000Z`),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -200,7 +209,7 @@ describe("GET /api/analytics — two-axis canonical source resolution (W8c)", ()
|
||||
value: 380,
|
||||
unit: "minutes",
|
||||
source: "APPLE_HEALTH",
|
||||
measuredAt: new Date("2026-05-12T06:00:00.000Z"),
|
||||
measuredAt: new Date(`${SLEEP_DAY}T06:00:00.000Z`),
|
||||
externalId: "ah-sleep-watch",
|
||||
deviceType: "watch",
|
||||
},
|
||||
@@ -212,7 +221,7 @@ describe("GET /api/analytics — two-axis canonical source resolution (W8c)", ()
|
||||
value: 480,
|
||||
unit: "minutes",
|
||||
source: "APPLE_HEALTH",
|
||||
measuredAt: new Date("2026-05-12T06:30:00.000Z"),
|
||||
measuredAt: new Date(`${SLEEP_DAY}T06:30:00.000Z`),
|
||||
externalId: "ah-sleep-phone",
|
||||
deviceType: "phone",
|
||||
},
|
||||
@@ -240,7 +249,7 @@ describe("GET /api/analytics — two-axis canonical source resolution (W8c)", ()
|
||||
value: 480,
|
||||
unit: "minutes",
|
||||
source: "APPLE_HEALTH",
|
||||
measuredAt: new Date("2026-05-12T06:00:00.000Z"),
|
||||
measuredAt: new Date(`${SLEEP_DAY}T06:00:00.000Z`),
|
||||
externalId: "ah-sleep-watch",
|
||||
deviceType: "watch",
|
||||
},
|
||||
@@ -252,7 +261,7 @@ describe("GET /api/analytics — two-axis canonical source resolution (W8c)", ()
|
||||
value: 380,
|
||||
unit: "minutes",
|
||||
source: "APPLE_HEALTH",
|
||||
measuredAt: new Date("2026-05-12T06:30:00.000Z"),
|
||||
measuredAt: new Date(`${SLEEP_DAY}T06:30:00.000Z`),
|
||||
externalId: "ah-sleep-phone",
|
||||
deviceType: "phone",
|
||||
},
|
||||
|
||||
@@ -420,8 +420,15 @@ describe("per-user timezone — Pacific/Auckland end-to-end", () => {
|
||||
// doesn't trigger a tz divergence on its own. The point is to
|
||||
// confirm the route returns a non-zero streak and goes through
|
||||
// the user-tz path without crashing.
|
||||
// Three consecutive days ending a week back: the summary reads its
|
||||
// streaks from a window that ends now, so fixed dates would slide out.
|
||||
const firstDay = Date.UTC(
|
||||
new Date().getUTCFullYear(),
|
||||
new Date().getUTCMonth(),
|
||||
new Date().getUTCDate() - 9,
|
||||
);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const dt = new Date(`2026-05-${12 + i}T11:30:00.000Z`);
|
||||
const dt = new Date(firstDay + i * 86_400_000 + 11.5 * 3_600_000);
|
||||
await prisma.measurement.create({
|
||||
data: {
|
||||
userId: me.id,
|
||||
|
||||
Reference in new issue
Block a user