mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +00:00
test(mood-rollups): pin DST fall-back Berlin midnight slip
QA Specialist-H1 (v1.4.39): the wave-report noon-anchored parity tests miss the DST-boundary case where the UTC-anchored rollup day-key and the user's local wall-clock day-key diverge. Pinned with a route-parity test that logs at `2025-10-25T23:30:00Z` (Berlin local `2025-10-26` 00:30, post fall-back) and asserts the rollup tier emits `2025-10-25` — documents the UTC-anchor trade-off the route's header comment now spells out. v1.5 per-user-tz bucketing closes the gap; until then this is the contract.
This commit is contained in:
1 parent
3c1a50cbb2
commit
ff4e5faf5f
2 files changed
+52
No files matched your search
@@ -216,6 +216,48 @@ describe("GET /api/mood/analytics", () => {
|
||||
expect(body.data.summary.latest).toBe(5);
|
||||
});
|
||||
|
||||
it("pins the DST fall-back Berlin midnight slip — UTC bucket vs local day diverge by one calendar day", async () => {
|
||||
// QA Specialist-H1 (v1.4.39): 2025-10-25T23:30:00Z is 00:30 local
|
||||
// in Europe/Berlin on 2025-10-26 (one hour AFTER the fall-back
|
||||
// transition). The mood rollup writer anchors `bucketStart` on
|
||||
// UTC midnight (mirroring the measurement-rollup convention), so
|
||||
// a write logged at this instant materialises a rollup row keyed
|
||||
// on `2025-10-25T00:00:00Z` — the UTC-anchored day — even though
|
||||
// the user's local wall clock reads 2025-10-26. The route's
|
||||
// `utcDayLabel` emits the same day-key the rollup row carries.
|
||||
//
|
||||
// This pins the documented behaviour. The legacy live-fallback
|
||||
// path uses `MoodEntry.date` (TZ-anchored) and therefore emits
|
||||
// `2025-10-26` for the same instant — a slip we document in the
|
||||
// route's header comment as the cost of the cache-tier semantics.
|
||||
// The v1.5 per-user-tz bucketing (P7 in the v1.4.38 perf audit)
|
||||
// closes the gap by anchoring the rollup table on the same day-
|
||||
// key the legacy path uses. Until then this is the contract.
|
||||
vi.mocked(prisma.moodEntryRollup.findMany).mockResolvedValue([
|
||||
{
|
||||
userId: "user-mood-1",
|
||||
granularity: "DAY",
|
||||
bucketStart: new Date("2025-10-25T00:00:00.000Z"),
|
||||
count: 1,
|
||||
mean: 4,
|
||||
minScore: 4,
|
||||
maxScore: 4,
|
||||
sd: null,
|
||||
computedAt: new Date(),
|
||||
},
|
||||
] as never);
|
||||
|
||||
const res = await callGet();
|
||||
expect(res.status).toBe(200);
|
||||
const body = (await res.json()) as MoodAnalyticsBody;
|
||||
// The rollup tier pins the entry on the UTC-anchored day-key,
|
||||
// NOT the user's local 2025-10-26 — same convention as the
|
||||
// measurement-rollup tier.
|
||||
expect(body.data.entries).toEqual([
|
||||
{ date: "2025-10-25", score: 4, samples: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("serves the cached envelope on a warm read without touching the DB", async () => {
|
||||
// First read populates the cache.
|
||||
vi.mocked(prisma.moodEntryRollup.findMany).mockResolvedValue([
|
||||
|
||||
@@ -56,6 +56,16 @@ interface MoodAnalyticsResult {
|
||||
* (Berlin year-round) the two labels agree on every entry whose
|
||||
* timestamp doesn't straddle the UTC boundary — i.e. every realistic
|
||||
* mood log, which a human submits during waking hours.
|
||||
*
|
||||
* Trade-off (QA Specialist-H1, v1.4.39): on DST fall-back nights the
|
||||
* UTC anchor and the user's local wall-clock day-key can diverge by
|
||||
* one calendar day. Example: `2025-10-25T23:30:00Z` is 00:30 local
|
||||
* in Europe/Berlin on `2025-10-26` (one hour after the fall-back
|
||||
* transition); the rollup row is keyed on `2025-10-25` (UTC) while
|
||||
* the legacy live-fallback path would emit `2025-10-26`. This is
|
||||
* pinned by the route-parity DST test. v1.5 per-user-tz bucketing
|
||||
* (audit P7) anchors the rollup on the same day-key the legacy path
|
||||
* uses, closing the gap.
|
||||
*/
|
||||
function utcDayLabel(d: Date): string {
|
||||
const yyyy = d.getUTCFullYear();
|
||||
|
||||
Reference in new issue
Block a user