Files
HealthLog/src/lib/sync/__tests__/cursor.test.ts
T
Marc-André BombeckandClaude Opus 4.8 824dcbb01a perf(cycle): scope the reminder cron to the in-window cohort and harden the period write
Push the cycle-reminder cron's gating into the query instead of loading
every CyclePrediction row each tick: filter on the user's
cycleTrackingEnabled / predictionEnabled relation and restrict to rows
whose nextPeriodStart falls inside a tz-generous [today-grace, today+lead]
string window. The per-tick cost now scales with the in-window cohort, not
the whole table; the exact per-user-timezone window still runs in JS.

Make the period-boundary shortcut transactional and idempotent: wrap the
close-prior + open-new (or stamp-current) cycle mutations in a single
$transaction so two concurrent taps cannot double-close a prior cycle or
compute a length against a stale read, and add withIdempotency so an exact
key replay is a no-op. The boundary day-log now ranks the incoming flow
against any existing same-day entry and never downgrades a richer flow.

Decode sync cursors additively: a live beta v1 token keeps its
measurement / mood / intake watermarks and simply fresh-scans the two
domains added in v2 (cycleDays, cycles) rather than forcing a full
re-init. Correct the stale "no live consumer" comment.

Thread the user's timezone through the Coach cycle snapshot so "today",
day-of-cycle and days-until-period derive from the user's zone — matching
the calendar and insights routes — instead of wall-clock Berlin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 12:17:07 +02:00

115 lines
4.2 KiB
TypeScript

/**
* v1.7.0 — opaque multi-domain keyset cursor codec for `/api/sync/changes`.
*/
import { describe, it, expect } from "vitest";
import { encodeCursor, decodeCursor, type SyncCursor } from "../cursor";
describe("sync cursor codec", () => {
it("round-trips a per-domain keyset map through encode/decode", () => {
const cursor: SyncCursor = {
measurements: { updatedAtMs: 1_717_000_000_000, id: "clx0meas" },
mood: { updatedAtMs: 1_717_000_500_000, id: "clx0mood" },
intakes: { updatedAtMs: 1_717_000_900_000, id: "clx0intk" },
};
const token = encodeCursor(cursor);
expect(typeof token).toBe("string");
expect(decodeCursor(token)).toEqual(cursor);
});
it("round-trips a partial map (only some domains advanced)", () => {
const cursor: SyncCursor = {
measurements: { updatedAtMs: 1_717_000_000_000, id: "clx0meas" },
};
expect(decodeCursor(encodeCursor(cursor))).toEqual(cursor);
});
it("round-trips an empty map (every domain a fresh scan)", () => {
expect(decodeCursor(encodeCursor({}))).toEqual({});
});
it("produces a base64url token with no JSON-visible structure", () => {
const token = encodeCursor({
measurements: { updatedAtMs: 1, id: "z" },
});
// Opaque to the client — must not contain `{` / `:` / `updatedAt`.
expect(token).not.toMatch(/[{}:"]/);
expect(token).not.toContain("updatedAt");
});
it("returns null for a garbage / unparseable token", () => {
expect(decodeCursor("not-a-cursor!!!")).toBeNull();
expect(decodeCursor("")).toBeNull();
// valid base64url but not the expected JSON shape
expect(decodeCursor(Buffer.from("[]").toString("base64url"))).toBeNull();
// unsupported version (v3 is not yet a thing) → clean re-init.
expect(
decodeCursor(Buffer.from('{"v":3,"d":{}}').toString("base64url")),
).toBeNull();
// a non-numeric version is garbage too.
expect(
decodeCursor(Buffer.from('{"v":"1","d":{}}').toString("base64url")),
).toBeNull();
// missing the `d` envelope
expect(
decodeCursor(Buffer.from('{"v":2}').toString("base64url")),
).toBeNull();
});
it("decodes a live v1 beta token additively — keeps known domains, fresh-scans the two new ones", () => {
// A pre-cycle v1 cursor only ever carried measurements/mood/intakes.
// It must NOT force a full re-init now that v2 added cycleDays/cycles;
// the known watermarks survive and the new domains decode as absent
// (a fresh scan of just those two).
const v1Token = Buffer.from(
JSON.stringify({
v: 1,
d: {
measurements: { u: 1_717_000_000_000, i: "clx0meas" },
mood: { u: 1_717_000_500_000, i: "clx0mood" },
intakes: { u: 1_717_000_900_000, i: "clx0intk" },
},
}),
).toString("base64url");
const decoded = decodeCursor(v1Token);
expect(decoded).toEqual({
measurements: { updatedAtMs: 1_717_000_000_000, id: "clx0meas" },
mood: { updatedAtMs: 1_717_000_500_000, id: "clx0mood" },
intakes: { updatedAtMs: 1_717_000_900_000, id: "clx0intk" },
});
// cycleDays + cycles absent → caller fresh-scans only those domains.
expect(decoded?.cycleDays).toBeUndefined();
expect(decoded?.cycles).toBeUndefined();
});
it("decodes a current-version empty envelope to a fresh-scan map", () => {
expect(
decodeCursor(Buffer.from('{"v":2,"d":{}}').toString("base64url")),
).toEqual({});
});
it("round-trips the v1.15.0 cycle domains", () => {
const cursor: SyncCursor = {
cycleDays: { updatedAtMs: 1_717_001_000_000, id: "clx0cday" },
cycles: { updatedAtMs: 1_717_001_500_000, id: "clx0cyc" },
};
expect(decodeCursor(encodeCursor(cursor))).toEqual(cursor);
});
it("drops a domain whose watermark is malformed but keeps valid ones", () => {
// measurements has a string `u` (invalid); mood is well-formed.
const token = Buffer.from(
JSON.stringify({
v: 2,
d: {
measurements: { u: "x", i: "z" },
mood: { u: 5, i: "clx0mood" },
},
}),
).toString("base64url");
expect(decodeCursor(token)).toEqual({
mood: { updatedAtMs: 5, id: "clx0mood" },
});
});
});