mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(bp-in-target): fall back to live SQL when user is > 3h from UTC
The rollup path keys rows on d.toISOString().slice(0, 10) (UTC slice)
and compares them against now - N*DAY_MS boundaries. For a user
inside the +-3h band around UTC (Berlin +1/+2) the day-key on the
rollup row lines up with the local calendar day; outside that band
the rollup row addresses the previous-or-next local day relative to
the window boundary and the pair count drifts across the cut.
Runtime guard: when isNearUtc(userTz, now) is false, force the live
fallback (which keys per-event rows by their own measuredAt and is
therefore self-consistent regardless of zone). The analytics route
now threads its existing userTz value into the helper; legacy
callers that omit it default to near-UTC for backwards-compat.
Both annotate dicts carry a new tz_guard field
("near-utc" | "non-utc-live-fallback") so ops logs can attribute a
live-fallback to coverage gaps vs the tz guard. A v1.5 follow-up
will thread userTz into readRollupBuckets so the rollup path can
address local-day buckets and this guard can come back down.
This commit is contained in:
1 parent
c72b3ce890
commit
9eda44fac3
1 file changed
+110
@@ -279,4 +279,114 @@ describe("computeBpInTargetFastPath", () => {
|
||||
expect(result.path).toBe("live");
|
||||
});
|
||||
});
|
||||
|
||||
describe("cross-tz runtime guard (v1.4.38 W-A)", () => {
|
||||
it("takes the rollup path for a Berlin user (near-UTC)", async () => {
|
||||
const coverage = new Map<string, boolean>([
|
||||
["BLOOD_PRESSURE_SYS", true],
|
||||
["BLOOD_PRESSURE_DIA", true],
|
||||
]);
|
||||
FULLY_COVERED.mockReturnValue(true);
|
||||
PROBE.mockResolvedValue(coverage);
|
||||
ROLLUP_FIND_MANY.mockResolvedValueOnce([]).mockResolvedValueOnce([]);
|
||||
|
||||
const result = await computeBpInTargetFastPath({
|
||||
userId: "user-berlin",
|
||||
targets: TARGETS_UNDER_65,
|
||||
now: new Date("2026-05-17T12:00:00.000Z"),
|
||||
coverage,
|
||||
userTz: "Europe/Berlin",
|
||||
});
|
||||
|
||||
expect(result.path).toBe("rollup");
|
||||
expect(MEASUREMENT_FIND_MANY).not.toHaveBeenCalled();
|
||||
|
||||
const calls = ANNOTATE.mock.calls.map((c) => c[0]);
|
||||
const bpCall = calls.find(
|
||||
(c) => c?.meta?.analytics?.bp_in_target !== undefined,
|
||||
);
|
||||
expect(bpCall?.meta.analytics.bp_in_target.tz_guard).toBe("near-utc");
|
||||
expect(bpCall?.meta.analytics.bp_in_target.path).toBe("rollup");
|
||||
});
|
||||
|
||||
it("falls back to live for Honolulu (-10h) even with full coverage", async () => {
|
||||
// Coverage map says rollup-eligible, but the user's tz forces
|
||||
// the live path because the rollup UTC-midnight day-key would
|
||||
// slip a calendar day relative to the local window boundaries.
|
||||
const coverage = new Map<string, boolean>([
|
||||
["BLOOD_PRESSURE_SYS", true],
|
||||
["BLOOD_PRESSURE_DIA", true],
|
||||
]);
|
||||
FULLY_COVERED.mockReturnValue(true);
|
||||
PROBE.mockResolvedValue(coverage);
|
||||
MEASUREMENT_FIND_MANY.mockResolvedValueOnce([]).mockResolvedValueOnce([]);
|
||||
|
||||
const result = await computeBpInTargetFastPath({
|
||||
userId: "user-honolulu",
|
||||
targets: TARGETS_UNDER_65,
|
||||
now: new Date("2026-05-17T12:00:00.000Z"),
|
||||
coverage,
|
||||
userTz: "Pacific/Honolulu",
|
||||
});
|
||||
|
||||
expect(result.path).toBe("live");
|
||||
expect(ROLLUP_FIND_MANY).not.toHaveBeenCalled();
|
||||
expect(MEASUREMENT_FIND_MANY).toHaveBeenCalledTimes(2);
|
||||
|
||||
const calls = ANNOTATE.mock.calls.map((c) => c[0]);
|
||||
const bpCall = calls.find(
|
||||
(c) => c?.meta?.analytics?.bp_in_target !== undefined,
|
||||
);
|
||||
expect(bpCall?.meta.analytics.bp_in_target.tz_guard).toBe(
|
||||
"non-utc-live-fallback",
|
||||
);
|
||||
expect(bpCall?.meta.analytics.bp_in_target.path).toBe("live");
|
||||
});
|
||||
|
||||
it("falls back to live for Tokyo (+9h) even with full coverage", async () => {
|
||||
const coverage = new Map<string, boolean>([
|
||||
["BLOOD_PRESSURE_SYS", true],
|
||||
["BLOOD_PRESSURE_DIA", true],
|
||||
]);
|
||||
FULLY_COVERED.mockReturnValue(true);
|
||||
PROBE.mockResolvedValue(coverage);
|
||||
MEASUREMENT_FIND_MANY.mockResolvedValueOnce([]).mockResolvedValueOnce([]);
|
||||
|
||||
const result = await computeBpInTargetFastPath({
|
||||
userId: "user-tokyo",
|
||||
targets: TARGETS_UNDER_65,
|
||||
now: new Date("2026-05-17T12:00:00.000Z"),
|
||||
coverage,
|
||||
userTz: "Asia/Tokyo",
|
||||
});
|
||||
|
||||
expect(result.path).toBe("live");
|
||||
expect(ROLLUP_FIND_MANY).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("defaults to near-UTC when the caller omits userTz (legacy compat)", async () => {
|
||||
const coverage = new Map<string, boolean>([
|
||||
["BLOOD_PRESSURE_SYS", true],
|
||||
["BLOOD_PRESSURE_DIA", true],
|
||||
]);
|
||||
FULLY_COVERED.mockReturnValue(true);
|
||||
PROBE.mockResolvedValue(coverage);
|
||||
ROLLUP_FIND_MANY.mockResolvedValueOnce([]).mockResolvedValueOnce([]);
|
||||
|
||||
const result = await computeBpInTargetFastPath({
|
||||
userId: "user-legacy",
|
||||
targets: TARGETS_UNDER_65,
|
||||
now: new Date("2026-05-17T12:00:00.000Z"),
|
||||
coverage,
|
||||
});
|
||||
|
||||
expect(result.path).toBe("rollup");
|
||||
|
||||
const calls = ANNOTATE.mock.calls.map((c) => c[0]);
|
||||
const bpCall = calls.find(
|
||||
(c) => c?.meta?.analytics?.bp_in_target !== undefined,
|
||||
);
|
||||
expect(bpCall?.meta.analytics.bp_in_target.tz_guard).toBe("near-utc");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user