mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
test(coach): pin all-time step months across history lengths and read paths
This commit is contained in:
1 parent
de2200bd5c
commit
49df950043
1 file changed
+106
@@ -13,6 +13,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { getPrismaClient, truncateAllTables } from "./setup";
|
||||
import { readDailySeries } from "@/lib/measurements/daily-series-read";
|
||||
import { loadUserSourcePriority } from "@/lib/rollups/measurement-read";
|
||||
import { recomputeUserRollups } from "@/lib/rollups/measurement-rollups";
|
||||
import { bucketTimeSeries } from "@/lib/charts/bucket-time-series";
|
||||
import { userDayKey } from "@/lib/tz/format";
|
||||
import {
|
||||
@@ -218,6 +219,111 @@ describe("get_metric_table — the chart's numbers", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("get_metric_table — all time", () => {
|
||||
const WEST = "America/Los_Angeles";
|
||||
// 10:00 on 20 September in Los Angeles.
|
||||
const WEST_NOW = new Date("2026-09-20T17:00:00.000Z");
|
||||
|
||||
async function seedStepsUser() {
|
||||
seq += 1;
|
||||
return getPrismaClient().user.create({
|
||||
data: {
|
||||
username: `metric-table-steps-${seq}`,
|
||||
email: `metric-table-steps-${seq}@example.test`,
|
||||
role: "USER",
|
||||
timezone: WEST,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** One step row a day at 18:00 UTC from `from` for `days` days. */
|
||||
function stepDays(from: string, days: number, value = 8_000) {
|
||||
const start = Date.parse(`${from}T18:00:00.000Z`);
|
||||
return Array.from({ length: days }, (_, i) => ({
|
||||
at: new Date(start + i * 86_400_000),
|
||||
value: value + (i % 5) * 100,
|
||||
}));
|
||||
}
|
||||
|
||||
async function seedSteps(
|
||||
userId: string,
|
||||
rows: Array<{ at: Date; value: number }>,
|
||||
) {
|
||||
await getPrismaClient().measurement.createMany({
|
||||
data: rows.map((r) => ({
|
||||
userId,
|
||||
type: "ACTIVITY_STEPS" as const,
|
||||
value: r.value,
|
||||
unit: "count",
|
||||
source: "APPLE_HEALTH" as const,
|
||||
measuredAt: r.at,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
function utcMonthTotals(rows: Array<{ at: Date; value: number }>) {
|
||||
const totals = new Map<string, number>();
|
||||
for (const r of rows) {
|
||||
const month = r.at.toISOString().slice(0, 7);
|
||||
totals.set(month, (totals.get(month) ?? 0) + r.value);
|
||||
}
|
||||
return totals;
|
||||
}
|
||||
|
||||
const read = (userId: string) =>
|
||||
readMetricTable({
|
||||
userId,
|
||||
metric: "steps",
|
||||
window: "allTime",
|
||||
period: "current",
|
||||
granularity: undefined,
|
||||
timeZone: WEST,
|
||||
locale: "en",
|
||||
ref: "r1",
|
||||
now: WEST_NOW,
|
||||
});
|
||||
|
||||
it("gives each month its step total, the same for a short and a long history, from the tier or the live table", async () => {
|
||||
const recent = stepDays("2026-05-01", 120);
|
||||
const old = stepDays("2023-01-10", 60, 5_000);
|
||||
|
||||
const short = await seedStepsUser();
|
||||
await seedSteps(short.id, recent);
|
||||
const long = await seedStepsUser();
|
||||
await seedSteps(long.id, [...old, ...recent]);
|
||||
for (const user of [short, long]) {
|
||||
await recomputeUserRollups(user.id, {
|
||||
from: new Date("2022-12-01T00:00:00.000Z"),
|
||||
to: WEST_NOW,
|
||||
});
|
||||
}
|
||||
|
||||
const expected = utcMonthTotals(recent);
|
||||
const shortTable = (await read(short.id))!;
|
||||
const longTable = (await read(long.id))!;
|
||||
expect(shortTable.columns[1].labelKey).toBe("coach.result.column.total");
|
||||
for (const table of [shortTable, longTable]) {
|
||||
const byMonth = new Map(table.rows.map((row) => [row[0], row[1]]));
|
||||
for (const [month, total] of expected) {
|
||||
expect(byMonth.get(month), month).toBe(total);
|
||||
}
|
||||
// 1 May 18:00 UTC is still 1 May in Los Angeles; nothing lands in April.
|
||||
expect(byMonth.get("2026-04") ?? null).toBeNull();
|
||||
}
|
||||
const longByMonth = new Map(longTable.rows.map((row) => [row[0], row[1]]));
|
||||
for (const [month, total] of utcMonthTotals(old)) {
|
||||
expect(longByMonth.get(month), month).toBe(total);
|
||||
}
|
||||
|
||||
// Without the tier the live table serves the same months.
|
||||
await getPrismaClient().measurementRollup.deleteMany({
|
||||
where: { userId: long.id },
|
||||
});
|
||||
const liveTable = (await read(long.id))!;
|
||||
expect(liveTable.rows).toEqual(longTable.rows);
|
||||
});
|
||||
});
|
||||
|
||||
describe("show_result — only within the conversation", () => {
|
||||
it("resolves a stored table of this conversation and nothing of another", async () => {
|
||||
const user = await seedUser();
|
||||
|
||||
Reference in new issue
Block a user