mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
fix(withings): read the sleep stages night by night
Sleep v2 Get answers only the first 24 hours after its startdate. The sync asked for the whole 30-day window, so it got the night a month back and nothing since: every night's stages arrived when the sliding window reached it, weeks late. The summary now runs first and lists the nights; each night's stages are read over its own span (at most 24 hours). Every run re-reads the nights that ended, or that Withings changed, in the last 48 hours, and fills older nights that hold no stage row yet, eight per run with a rotation so a night without stages cannot hold up the rest; a full sync reads them all. Without the night list the last 24 hours are read as one slice and nothing is swept, since a slice can cut a night. A summary failure used to be a warning beside a green leg. It is now the sleep leg's failure, recorded after the stages land.
This commit is contained in:
1 parent
3b1cdd7a1f
commit
c1921d1dd1
3 files changed
+536
-158
No files matched your search
@@ -51,26 +51,70 @@ afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
function stubWithingsSleep(series: unknown[]) {
|
||||
type Segment = {
|
||||
startdate: number;
|
||||
enddate: number;
|
||||
state: number;
|
||||
id: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Answer Sleep v2 the way Withings does: `getsummary` lists the nights,
|
||||
* `get` returns the segments inside the span asked for, at most its first
|
||||
* 24 hours.
|
||||
*/
|
||||
function stubWithingsSleep(series: Segment[]) {
|
||||
const nights = new Map<
|
||||
number,
|
||||
{ id: number; startdate: number; enddate: number }
|
||||
>();
|
||||
for (const seg of series) {
|
||||
const night = nights.get(seg.id);
|
||||
nights.set(seg.id, {
|
||||
id: seg.id,
|
||||
startdate: Math.min(night?.startdate ?? seg.startdate, seg.startdate),
|
||||
enddate: Math.max(night?.enddate ?? seg.enddate, seg.enddate),
|
||||
});
|
||||
}
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => ({
|
||||
status: 200,
|
||||
json: async () => ({
|
||||
status: 0,
|
||||
body: { series },
|
||||
}),
|
||||
})),
|
||||
vi.fn(async (_url: string, init: { body: string }) => {
|
||||
const params = new URLSearchParams(init.body);
|
||||
if (params.get("action") === "getsummary") {
|
||||
return {
|
||||
status: 200,
|
||||
json: async () => ({
|
||||
status: 0,
|
||||
body: { series: [...nights.values()] },
|
||||
}),
|
||||
};
|
||||
}
|
||||
const from = Number(params.get("startdate"));
|
||||
const to = Math.min(Number(params.get("enddate")), from + 24 * 3600);
|
||||
return {
|
||||
status: 200,
|
||||
json: async () => ({
|
||||
status: 0,
|
||||
body: {
|
||||
series: series.filter(
|
||||
(seg) => seg.startdate >= from && seg.enddate <= to,
|
||||
),
|
||||
},
|
||||
}),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/** Last night: it started ten hours ago, so a run reads it again for a re-score. */
|
||||
const base = Math.floor(Date.now() / 1000) - 10 * 3600;
|
||||
|
||||
describe("syncUserSleep — integration", () => {
|
||||
it("persists one Measurement row per stage segment for a single night", async () => {
|
||||
// Sleep session #99: 4 segments — CORE 60m, DEEP 30m, REM 30m, AWAKE 10m.
|
||||
// All segments share the same parent night but each carries a
|
||||
// distinct sleepStage, so the W17b/c composite (Migration 0055)
|
||||
// keeps every row.
|
||||
const base = 1715000000; // 2024-05-06 13:33:20 UTC (arbitrary)
|
||||
stubWithingsSleep([
|
||||
{ startdate: base, enddate: base + 3600, state: 1, id: 99 },
|
||||
{ startdate: base + 3600, enddate: base + 5400, state: 2, id: 99 },
|
||||
@@ -106,7 +150,6 @@ describe("syncUserSleep — integration", () => {
|
||||
});
|
||||
|
||||
it("re-syncing the same night updates rows in place (no duplicates)", async () => {
|
||||
const base = 1715000000;
|
||||
// First sync: DEEP 30 minutes.
|
||||
stubWithingsSleep([
|
||||
{ startdate: base, enddate: base + 1800, state: 2, id: 100 },
|
||||
|
||||
Reference in new issue
Block a user