Merge remote-tracking branch 'origin/release/v1.46.0' into feat/v146-ui

# Conflicts:
#	src/__tests__/baselines/source-function-lines.json
#	src/__tests__/baselines/version-stamp-comments.json
This commit is contained in:
Marc-André Bombeck committed 2026-10-23 19:54:08 +02:00
commit 879473305c
115 files changed
+5037 -599

No files matched your search

@@ -194,6 +194,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:03.000Z",
@@ -217,6 +218,7 @@
"sleepStage": "DEEP",
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:04.000Z",
@@ -240,6 +242,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 4,
"deletedAt": "<tombstone>",
"createdAt": "2026-05-01T08:20:01.000Z",
@@ -263,6 +266,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": "rt-Measurement-deviceType",
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:00.000Z",
@@ -286,6 +290,7 @@
"sleepStage": null,
"rhythmClassification": "IRREGULAR",
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:02.000Z",
@@ -2629,6 +2634,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:03.000Z",
@@ -2652,6 +2658,7 @@
"sleepStage": "DEEP",
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:04.000Z",
@@ -2675,6 +2682,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 4,
"deletedAt": "<tombstone>",
"createdAt": "2026-05-01T08:20:01.000Z",
@@ -2698,6 +2706,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": "rt-Measurement-deviceType",
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:00.000Z",
@@ -2721,6 +2730,7 @@
"sleepStage": null,
"rhythmClassification": "IRREGULAR",
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:02.000Z",
@@ -428,7 +428,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:03.000Z",
"updatedAt": "2026-05-01T08:21:03.000Z"
"updatedAt": "2026-05-01T08:21:03.000Z",
"ingestTokenId": null
},
{
"id": "<id:2>",
@@ -452,7 +453,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:02.000Z",
"updatedAt": "2026-05-01T08:21:02.000Z"
"updatedAt": "2026-05-01T08:21:02.000Z",
"ingestTokenId": null
},
{
"id": "<id:3>",
@@ -476,7 +478,8 @@
"syncVersion": 4,
"deletedAt": "<tombstone>",
"createdAt": "2026-05-01T08:20:01.000Z",
"updatedAt": "2026-05-01T08:21:01.000Z"
"updatedAt": "2026-05-01T08:21:01.000Z",
"ingestTokenId": null
},
{
"id": "<id:4>",
@@ -500,7 +503,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:04.000Z",
"updatedAt": "2026-05-01T08:21:04.000Z"
"updatedAt": "2026-05-01T08:21:04.000Z",
"ingestTokenId": null
},
{
"id": "<id:5>",
@@ -524,7 +528,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:00.000Z",
"updatedAt": "2026-05-01T08:21:00.000Z"
"updatedAt": "2026-05-01T08:21:00.000Z",
"ingestTokenId": null
}
],
"IntradayCumulativeProfile": [
@@ -3091,6 +3096,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:03.000Z",
@@ -3114,6 +3120,7 @@
"sleepStage": "DEEP",
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:04.000Z",
@@ -3137,6 +3144,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 4,
"deletedAt": "<tombstone>",
"createdAt": "2026-05-01T08:20:01.000Z",
@@ -3160,6 +3168,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": "rt-Measurement-deviceType",
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:00.000Z",
@@ -3183,6 +3192,7 @@
"sleepStage": null,
"rhythmClassification": "IRREGULAR",
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:02.000Z",
@@ -520,7 +520,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
},
{
"id": "<id:2>",
@@ -544,7 +545,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
},
{
"id": "<id:3>",
@@ -568,7 +570,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
},
{
"id": "<id:4>",
@@ -592,7 +595,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
}
],
"IntradayCumulativeProfile": [
@@ -550,7 +550,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
}
],
"IntradayCumulativeProfile": [],
@@ -426,7 +426,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
},
{
"id": "<id:2>",
@@ -450,7 +451,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
},
{
"id": "<id:3>",
@@ -474,7 +476,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
},
{
"id": "<id:4>",
@@ -498,7 +501,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
"updatedAt": "<now>"
"updatedAt": "<now>",
"ingestTokenId": null
}
],
"IntradayCumulativeProfile": [],
@@ -1289,6 +1293,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
@@ -1312,6 +1317,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
@@ -1335,6 +1341,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
@@ -1358,6 +1365,7 @@
"sleepStage": null,
"rhythmClassification": null,
"deviceType": null,
"ingestTokenId": null,
"syncVersion": 1,
"deletedAt": null,
"createdAt": "<now>",
@@ -26,7 +26,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:03.000Z",
"updatedAt": "2026-05-01T08:21:03.000Z"
"updatedAt": "2026-05-01T08:21:03.000Z",
"ingestTokenId": null
},
{
"id": "<id:2>",
@@ -50,7 +51,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:02.000Z",
"updatedAt": "2026-05-01T08:21:02.000Z"
"updatedAt": "2026-05-01T08:21:02.000Z",
"ingestTokenId": null
},
{
"id": "<id:3>",
@@ -74,7 +76,8 @@
"syncVersion": 4,
"deletedAt": "<tombstone>",
"createdAt": "2026-05-01T08:20:01.000Z",
"updatedAt": "2026-05-01T08:21:01.000Z"
"updatedAt": "2026-05-01T08:21:01.000Z",
"ingestTokenId": null
},
{
"id": "<id:4>",
@@ -98,7 +101,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:04.000Z",
"updatedAt": "2026-05-01T08:21:04.000Z"
"updatedAt": "2026-05-01T08:21:04.000Z",
"ingestTokenId": null
},
{
"id": "<id:5>",
@@ -122,7 +126,8 @@
"syncVersion": 1,
"deletedAt": null,
"createdAt": "2026-05-01T08:20:00.000Z",
"updatedAt": "2026-05-01T08:21:00.000Z"
"updatedAt": "2026-05-01T08:21:00.000Z",
"ingestTokenId": null
}
],
"IntradayCumulativeProfile": [
@@ -0,0 +1,351 @@
/**
* Per-token Apple Health write-back (#1272), end to end against Postgres.
*
* A reading a `measurements:write` token carries keeps its `EXTERNAL` source
* and is linked to the token. Every measurement row answers a resolved
* `appleHealthMirror`: WITHINGS, IMPORT and MANUAL true, EXTERNAL whatever the
* person chose for the token, everything else false. Switching the setting is
* one statement on the token row: the readings themselves are not touched
* (no `updatedAt` or `syncVersion` bump, so nothing keyed on them recomputes),
* and the `measurementMirror` domain of `/api/sync/changes` re-delivers the
* token's rows with the new value.
*/
import { beforeEach, describe, expect, it } from "vitest";
import {
GET as listMeasurements,
POST as createMeasurement,
} from "@/app/api/measurements/route";
import { POST as batchMeasurements } from "@/app/api/measurements/batch/route";
import { GET as readMeasurement } from "@/app/api/measurements/[id]/route";
import { GET as syncChanges } from "@/app/api/sync/changes/route";
import {
PATCH as patchToken,
DELETE as revokeToken,
} from "@/app/api/tokens/[id]/route";
import { GET as listTokens } from "@/app/api/tokens/route";
import { MEASUREMENTS_WRITE_SCOPE } from "@/lib/measurements/scopes";
import { MEASUREMENT_SOURCES } from "@/lib/measurements/sources";
import { callRoute, type RouteAuth } from "../kit/call-route";
import {
makeMeasurement,
makeSession,
makeToken,
makeUser,
type BearerFixture,
} from "../kit/factories";
import { getPrismaClient, truncateAllTables } from "./setup";
const prisma = getPrismaClient();
let userId: string;
let owner: RouteAuth;
let scale: BearerFixture;
beforeEach(async () => {
await truncateAllTables(prisma);
const user = await makeUser({ timezone: "UTC" });
userId = user.id;
owner = await makeSession(user);
scale = await makeToken(user, {
permissions: [MEASUREMENTS_WRITE_SCOPE],
name: "Scale",
});
});
async function postReading(
value: number,
at: string,
headers?: Record<string, string>,
) {
return callRoute<{ id: string }>(createMeasurement, {
method: "POST",
route: "/api/measurements",
body: { type: "WEIGHT", value, unit: "kg", measuredAt: at },
auth: scale,
headers,
});
}
async function list(query: Record<string, string | number> = {}) {
const res = await callRoute<{
measurements: Array<{
id: string;
source: string;
appleHealthMirror: boolean;
}>;
}>(listMeasurements, {
method: "GET",
route: "/api/measurements",
query: { limit: 50, ...query },
auth: owner,
});
expect(res.status).toBe(200);
return res.data.measurements;
}
async function setMirror(id: string, on: boolean, auth: RouteAuth = owner) {
return callRoute<{ id: string; mirrorToAppleHealth: boolean }>(patchToken, {
method: "PATCH",
route: "/api/tokens/{id}",
params: { id },
body: { mirrorToAppleHealth: on },
auth,
});
}
describe("a token's readings and the resolved write-back", () => {
it("links the reading to its token and resolves each source", async () => {
const res = await postReading(81.2, "2026-10-14T06:30:00.000Z");
expect(res.status).toBe(201);
const stored = await prisma.measurement.findFirstOrThrow({
where: { userId, source: "EXTERNAL" },
select: { ingestTokenId: true },
});
expect(stored.ingestTokenId).toBe(scale.tokenId);
await makeMeasurement(
{ id: userId },
{
source: "MANUAL",
measuredAt: new Date("2026-10-13T06:30:00.000Z"),
},
);
await makeMeasurement(
{ id: userId },
{
source: "APPLE_HEALTH",
measuredAt: new Date("2026-10-12T06:30:00.000Z"),
externalId: "hk-1",
},
);
const rows = await list({ type: "WEIGHT" });
const bySource = Object.fromEntries(
rows.map((r) => [r.source, r.appleHealthMirror]),
);
expect(bySource).toEqual({
EXTERNAL: false,
MANUAL: true,
APPLE_HEALTH: false,
});
// The token never reaches the wire.
for (const row of rows) {
expect(row).not.toHaveProperty("ingestTokenId");
expect(row).not.toHaveProperty("ingestToken");
}
});
it("links every row of a token batch", async () => {
const res = await callRoute(batchMeasurements, {
method: "POST",
route: "/api/measurements/batch",
body: {
entries: [
{
hkIdentifier: "HKQuantityTypeIdentifierBodyMass",
value: 80.1,
unit: "kg",
startDate: "2026-10-14T06:30:00.000Z",
endDate: "2026-10-14T06:30:00.000Z",
externalId: "scale-1",
},
],
},
auth: scale,
});
expect(res.status).toBe(200);
const rows = await prisma.measurement.findMany({
where: { userId },
select: { source: true, ingestTokenId: true },
});
expect(rows).toEqual([
{ source: "EXTERNAL", ingestTokenId: scale.tokenId },
]);
});
});
describe("the list filter", () => {
it("returns exactly the rows that resolve true, for every source", async () => {
const on = await makeToken(
{ id: userId },
{ permissions: [MEASUREMENTS_WRITE_SCOPE], name: "On" },
);
await prisma.apiToken.update({
where: { id: on.tokenId },
data: { mirrorToAppleHealth: true },
});
let minute = 0;
for (const source of MEASUREMENT_SOURCES) {
for (const ingestTokenId of [null, scale.tokenId, on.tokenId]) {
minute += 1;
await makeMeasurement(
{ id: userId },
{
source,
ingestTokenId,
measuredAt: new Date(Date.UTC(2026, 9, 1, 6, minute)),
externalId: `row-${minute}`,
},
);
}
}
const all = await list({ limit: 500 });
expect(all).toHaveLength(MEASUREMENT_SOURCES.length * 3);
const expected = all
.filter((r) => r.appleHealthMirror)
.map((r) => r.id)
.sort();
expect(expected.length).toBeGreaterThan(0);
const filtered = await list({ limit: 500, appleHealthMirror: "true" });
expect(filtered.map((r) => r.id).sort()).toEqual(expected);
});
it.each([
{ groupBy: "day" },
{ dayKey: "2026-10-14" },
{ aggregate: "daily" },
{ type: "SLEEP_DURATION" },
])("is refused beside %j", async (mode) => {
const res = await callRoute(listMeasurements, {
method: "GET",
route: "/api/measurements",
query: { type: "WEIGHT", ...mode, appleHealthMirror: "true" },
auth: owner,
});
expect(res.status).toBe(422);
});
});
describe("switching the setting", () => {
it("turns the token's rows true everywhere, and leaves the rows themselves alone", async () => {
const created = await postReading(81.2, "2026-10-14T06:30:00.000Z");
const id = created.data.id;
const before = await prisma.measurement.findUniqueOrThrow({
where: { id },
select: { updatedAt: true, syncVersion: true },
});
const first = await callRoute<{ cursor: string }>(syncChanges, {
method: "GET",
route: "/api/sync/changes",
auth: owner,
});
const res = await setMirror(scale.tokenId, true);
expect(res.status).toBe(200);
expect(res.data).toEqual({ id: scale.tokenId, mirrorToAppleHealth: true });
const after = await prisma.measurement.findUniqueOrThrow({
where: { id },
select: { updatedAt: true, syncVersion: true },
});
expect(after).toEqual(before);
expect((await list())[0].appleHealthMirror).toBe(true);
expect(
(await list({ appleHealthMirror: "true" })).map((r) => r.id),
).toEqual([id]);
const detail = await callRoute<{ appleHealthMirror: boolean }>(
readMeasurement,
{
method: "GET",
route: "/api/measurements/{id}",
params: { id },
auth: owner,
},
);
expect(detail.data.appleHealthMirror).toBe(true);
const next = await callRoute<{
changes: {
measurements: { upserts: unknown[] };
measurementMirror: {
upserts: Array<{ id: string; appleHealthMirror: boolean }>;
};
};
}>(syncChanges, {
method: "GET",
route: "/api/sync/changes",
query: { cursor: first.data.cursor },
auth: owner,
});
expect(next.data.changes.measurements.upserts).toEqual([]);
expect(next.data.changes.measurementMirror.upserts).toEqual([
{ id, appleHealthMirror: true },
]);
const audit = await prisma.auditLog.findMany({
where: { userId, action: "tokens.measurements.mirror.update" },
select: { details: true },
});
expect(audit).toHaveLength(1);
expect(JSON.parse(audit[0].details ?? "{}")).toMatchObject({
tokenId: scale.tokenId,
mirrorToAppleHealth: true,
});
});
it("keeps a revoked token's choice, and shows it on the token list", async () => {
await postReading(81.2, "2026-10-14T06:30:00.000Z");
await setMirror(scale.tokenId, true);
const revoked = await callRoute(revokeToken, {
method: "DELETE",
route: "/api/tokens/{id}",
params: { id: scale.tokenId },
auth: owner,
});
expect(revoked.status).toBe(200);
expect((await list())[0].appleHealthMirror).toBe(true);
// A revoked token can still be switched.
expect((await setMirror(scale.tokenId, false)).status).toBe(200);
expect((await list())[0].appleHealthMirror).toBe(false);
const tokens = await callRoute<
Array<{ id: string; mirrorToAppleHealth: boolean }>
>(listTokens, { method: "GET", route: "/api/tokens", auth: owner });
expect(tokens.data.find((t) => t.id === scale.tokenId)).toMatchObject({
mirrorToAppleHealth: false,
});
});
it("answers a retried create from its idempotency record, unchanged", async () => {
const headers = { "idempotency-key": "scale-reading-1" };
const first = await postReading(81.2, "2026-10-14T06:30:00.000Z", headers);
await setMirror(scale.tokenId, true);
const replay = await postReading(81.2, "2026-10-14T06:30:00.000Z", headers);
expect(replay.status).toBe(first.status);
expect(replay.body).toEqual(first.body);
expect(
await prisma.measurement.count({ where: { userId, source: "EXTERNAL" } }),
).toBe(1);
});
});
describe("what the switch refuses", () => {
it("refuses a token that is not a measurement token", async () => {
const wildcard = await makeToken({ id: userId }, { permissions: ["*"] });
const res = await setMirror(wildcard.tokenId, true);
expect(res.status).toBe(422);
expect(res.meta?.errorCode).toBe("tokens.mirror.not_measurement_token");
});
it("answers another account's token as missing", async () => {
const stranger = await makeUser({ timezone: "UTC" });
const theirs = await makeToken(stranger, {
permissions: [MEASUREMENTS_WRITE_SCOPE],
});
expect((await setMirror(theirs.tokenId, true)).status).toBe(404);
});
it("refuses a Bearer caller, the token's own included", async () => {
const res = await setMirror(scale.tokenId, true, scale);
expect([401, 403]).toContain(res.status);
const stored = await prisma.apiToken.findUniqueOrThrow({
where: { id: scale.tokenId },
select: { mirrorToAppleHealth: true },
});
expect(stored.mirrorToAppleHealth).toBe(false);
});
});
@@ -0,0 +1,131 @@
/**
* A backup carries the token link of an EXTERNAL reading, and a restore keeps
* it only where it still means something (#1272).
*
* Tokens are not part of a backup. A reading linked to a token the restoring
* account still holds comes back linked, so its Apple Health write-back is
* still that token's setting; a reading whose token is gone comes back with no
* link rather than a dangling id. The file is built by the real writer.
*/
import { beforeEach, describe, expect, it, vi } from "vitest";
process.env.ENCRYPTION_KEY =
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
import { encrypt } from "@/lib/crypto";
import { buildFullBackupPayload } from "@/lib/export/full-backup-payload";
import { parseBackupPayload } from "@/lib/validations/backup";
import { MEASUREMENTS_WRITE_SCOPE } from "@/lib/measurements/scopes";
import { POST as RESTORE } from "./restore-job-driver";
import { makeToken } from "../kit/factories";
import { cookieJar, headerJar } from "./mock-next-headers";
import { getPrismaClient, truncateAllTables } from "./setup";
vi.mock("next/headers", async () => {
const { cookieJar, headerJar } = await import("./mock-next-headers");
return {
headers: vi.fn(async () => ({
get: (name: string) => headerJar.get(name.toLowerCase()) ?? null,
})),
cookies: vi.fn(async () => ({
get: (name: string) => {
const value = cookieJar.get(name);
return value ? { name, value } : undefined;
},
set: (name: string, value: string) => cookieJar.set(name, value),
delete: (name: string) => cookieJar.delete(name),
})),
};
});
vi.mock("@/lib/db-compat", () => ({
ensureDbCompatibility: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("@/lib/cache/invalidate", async (importOriginal) => {
const actual =
await importOriginal<typeof import("@/lib/cache/invalidate")>();
return { ...actual, invalidateUserData: vi.fn() };
});
beforeEach(async () => {
await truncateAllTables(getPrismaClient());
cookieJar.clear();
headerJar.clear();
});
describe("restoring EXTERNAL readings", () => {
it("keeps the link to a token the account holds and drops the rest", async () => {
const prisma = getPrismaClient();
const owner = await prisma.user.create({
data: {
username: "token-link-owner",
email: "token-link-owner@example.test",
role: "ADMIN",
},
});
const session = await prisma.session.create({
data: { userId: owner.id, expiresAt: new Date(Date.now() + 60_000) },
});
cookieJar.set("healthlog_session", session.id);
const kept = await makeToken(owner, {
permissions: [MEASUREMENTS_WRITE_SCOPE],
});
const gone = await makeToken(owner, {
permissions: [MEASUREMENTS_WRITE_SCOPE],
});
const reading = (day: string, ingestTokenId: string | null) => ({
userId: owner.id,
type: "WEIGHT" as const,
source: "EXTERNAL" as const,
value: 80,
unit: "kg",
measuredAt: new Date(`${day}T06:00:00.000Z`),
ingestTokenId,
});
await prisma.measurement.createMany({
data: [
reading("2026-10-01", kept.tokenId),
reading("2026-10-02", gone.tokenId),
reading("2026-10-03", null),
],
});
const { payload: built } = await buildFullBackupPayload(prisma, owner.id, {
purpose: "disaster-recovery",
});
const parsed = parseBackupPayload(JSON.stringify(built));
const backup = await prisma.dataBackup.create({
data: {
userId: owner.id,
type: "TOKEN_LINK_ROUND_TRIP",
data: encrypt(JSON.stringify(parsed)),
},
});
await prisma.measurement.deleteMany({ where: { userId: owner.id } });
await prisma.apiToken.delete({ where: { id: gone.tokenId } });
const response = await RESTORE(
new Request(`http://localhost/api/admin/backups/${backup.id}/restore`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ confirm: "RESTORE" }),
}) as unknown as Parameters<typeof RESTORE>[0],
{ params: Promise.resolve({ id: backup.id }) },
);
expect(response.status).toBe(200);
const restored = await prisma.measurement.findMany({
where: { userId: owner.id },
select: { measuredAt: true, ingestTokenId: true },
orderBy: { measuredAt: "asc" },
});
expect(restored.map((r) => r.ingestTokenId)).toEqual([
kept.tokenId,
null,
null,
]);
});
});
@@ -27,6 +27,7 @@ const token = {
expiresAt: null,
createdAt: "2026-01-01T00:00:00.000Z",
revoked: false,
mirrorToAppleHealth: false,
};
function answering(status: number, body: unknown) {
@@ -24,7 +24,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:3>",
@@ -47,7 +48,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": false
},
{
"id": "<id:4>",
@@ -70,7 +72,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": false
},
{
"id": "<id:5>",
@@ -93,7 +96,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:6>",
@@ -116,7 +120,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:7>",
@@ -139,7 +144,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:8>",
@@ -162,7 +168,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:9>",
@@ -185,7 +192,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:10>",
@@ -208,7 +216,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:11>",
@@ -231,7 +240,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": false
},
{
"id": "<id:12>",
@@ -254,7 +264,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": false
}
],
"meta": {
@@ -24,7 +24,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:3>",
@@ -47,7 +48,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
}
],
"meta": {
@@ -24,7 +24,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:3>",
@@ -47,7 +48,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
}
],
"meta": {
@@ -24,7 +24,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": false
}
],
"meta": {
@@ -22,7 +22,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
"error": null
},
@@ -73,7 +74,9 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"ingestTokenId": null,
"ingestToken": null
}
],
"manual"
@@ -22,7 +22,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
"error": null
},
@@ -73,7 +74,9 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"ingestTokenId": null,
"ingestToken": null
}
],
"manual"
@@ -23,7 +23,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:3>",
@@ -46,7 +47,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
{
"id": "<id:4>",
@@ -69,7 +71,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
}
],
"error": null
@@ -126,7 +129,9 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"ingestTokenId": null,
"ingestToken": null
},
{
"id": "<id:3>",
@@ -150,7 +155,9 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"ingestTokenId": null,
"ingestToken": null
},
{
"id": "<id:4>",
@@ -174,7 +181,9 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"ingestTokenId": null,
"ingestToken": null
}
],
"manual"
@@ -23,6 +23,7 @@
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>",
"appleHealthMirror": true,
"status": "inserted"
},
"error": null
@@ -23,6 +23,7 @@
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>",
"appleHealthMirror": true,
"status": "duplicate"
},
"error": null
@@ -23,6 +23,7 @@
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>",
"appleHealthMirror": true,
"status": "duplicate"
},
"error": null
@@ -22,7 +22,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": false
},
"error": null
},
@@ -73,7 +74,11 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"ingestTokenId": "<id:3>",
"ingestToken": {
"mirrorToAppleHealth": false
}
}
],
"manual"
@@ -111,7 +116,7 @@
],
"stored": [
{
"id": "<id:3>",
"id": "<id:4>",
"type": "WEIGHT",
"value": 80.4,
"unit": "kg",
@@ -141,7 +146,7 @@
"sealedNote": false
},
{
"id": "<id:4>",
"id": "<id:5>",
"type": "BLOOD_PRESSURE_SYS",
"value": 128,
"unit": "mmHg",
@@ -156,7 +161,7 @@
"sealedNote": false
},
{
"id": "<id:5>",
"id": "<id:6>",
"type": "BLOOD_PRESSURE_DIA",
"value": 82,
"unit": "mmHg",
@@ -171,7 +176,7 @@
"sealedNote": false
},
{
"id": "<id:6>",
"id": "<id:7>",
"type": "PULSE",
"value": 66,
"unit": "bpm",
@@ -186,7 +191,7 @@
"sealedNote": true
},
{
"id": "<id:7>",
"id": "<id:8>",
"type": "SLEEP_DURATION",
"value": 90,
"unit": "minutes",
@@ -201,7 +206,7 @@
"sealedNote": false
},
{
"id": "<id:8>",
"id": "<id:9>",
"type": "BLOOD_GLUCOSE",
"value": 104,
"unit": "mg/dL",
@@ -22,7 +22,8 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"appleHealthMirror": true
},
"error": null
},
@@ -73,7 +74,9 @@
"syncVersion": "<syncVersion>",
"deletedAt": null,
"createdAt": "<createdAt>",
"updatedAt": "<updatedAt>"
"updatedAt": "<updatedAt>",
"ingestTokenId": null,
"ingestToken": null
}
],
"manual"
@@ -18,6 +18,7 @@
"measuredAt": "2026-10-14T06:30:00.000Z",
"source": "MANUAL",
"notes": "after the run",
"appleHealthMirror": true,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
},
@@ -30,6 +31,7 @@
"measuredAt": "2026-10-14T07:00:00.000Z",
"source": "WITHINGS",
"notes": null,
"appleHealthMirror": true,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
}
@@ -197,6 +199,10 @@
}
],
"tombstones": []
},
"measurementMirror": {
"upserts": [],
"tombstones": []
}
}
},
@@ -219,6 +225,7 @@
"cycle_day_tombstones": 0,
"cycle_upserts": 2,
"cycle_tombstones": 0,
"measurement_mirror_upserts": 0,
"cycle_module_enabled": true,
"has_more": true,
"cursor_present": false
@@ -18,6 +18,7 @@
"measuredAt": "2026-06-17T09:30:00.000Z",
"source": "APPLE_HEALTH",
"notes": null,
"appleHealthMirror": false,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
}
@@ -69,6 +70,10 @@
"updatedAt": "<updatedAt>"
}
]
},
"measurementMirror": {
"upserts": [],
"tombstones": []
}
}
},
@@ -91,6 +96,7 @@
"cycle_day_tombstones": 1,
"cycle_upserts": 0,
"cycle_tombstones": 1,
"measurement_mirror_upserts": 0,
"cycle_module_enabled": true,
"has_more": true,
"cursor_present": true
@@ -18,6 +18,7 @@
"measuredAt": "2026-10-14T08:00:00.000Z",
"source": "MANUAL",
"notes": null,
"appleHealthMirror": true,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
}
@@ -39,6 +40,10 @@
"cycles": {
"upserts": [],
"tombstones": []
},
"measurementMirror": {
"upserts": [],
"tombstones": []
}
}
},
@@ -61,6 +66,7 @@
"cycle_day_tombstones": 0,
"cycle_upserts": 0,
"cycle_tombstones": 0,
"measurement_mirror_upserts": 0,
"cycle_module_enabled": true,
"has_more": false,
"cursor_present": true
@@ -26,6 +26,10 @@
"cycles": {
"upserts": [],
"tombstones": []
},
"measurementMirror": {
"upserts": [],
"tombstones": []
}
}
},
@@ -48,6 +52,7 @@
"cycle_day_tombstones": 0,
"cycle_upserts": 0,
"cycle_tombstones": 0,
"measurement_mirror_upserts": 0,
"cycle_module_enabled": true,
"has_more": false,
"cursor_present": true
@@ -26,6 +26,10 @@
"cycles": {
"upserts": [],
"tombstones": []
},
"measurementMirror": {
"upserts": [],
"tombstones": []
}
}
},
@@ -26,6 +26,10 @@
"cycles": {
"upserts": [],
"tombstones": []
},
"measurementMirror": {
"upserts": [],
"tombstones": []
}
}
},
@@ -18,6 +18,7 @@
"measuredAt": "2026-10-14T06:30:00.000Z",
"source": "MANUAL",
"notes": "after the run",
"appleHealthMirror": true,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
},
@@ -30,6 +31,7 @@
"measuredAt": "2026-10-14T07:00:00.000Z",
"source": "WITHINGS",
"notes": null,
"appleHealthMirror": true,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
},
@@ -42,6 +44,7 @@
"measuredAt": "2026-06-17T09:30:00.000Z",
"source": "APPLE_HEALTH",
"notes": null,
"appleHealthMirror": false,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
},
@@ -54,6 +57,7 @@
"measuredAt": "2026-10-14T08:00:00.000Z",
"source": "MANUAL",
"notes": null,
"appleHealthMirror": true,
"syncVersion": "<syncVersion>",
"updatedAt": "<updatedAt>"
}
@@ -158,6 +162,10 @@
"cycles": {
"upserts": [],
"tombstones": []
},
"measurementMirror": {
"upserts": [],
"tombstones": []
}
}
},
@@ -180,6 +188,7 @@
"cycle_day_tombstones": 0,
"cycle_upserts": 0,
"cycle_tombstones": 0,
"measurement_mirror_upserts": 0,
"cycle_module_enabled": false,
"has_more": false,
"cursor_present": false
@@ -0,0 +1,68 @@
{
"status": 200,
"body": {
"data": {
"unattributedReadings": 1
},
"error": null
},
"events": [
{
"status": 200,
"action": {
"name": "tokens.measurements.summary"
},
"meta": {
"unattributed_readings": 1
},
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
}
],
"audit": [],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,78 @@
{
"status": 200,
"body": {
"data": {
"id": "<id:1>",
"mirrorToAppleHealth": true
},
"error": null
},
"events": [
{
"status": 200,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": {
"mirror_to_apple_health": true,
"changed": true
},
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": false,
"mirrorToAppleHealth": true,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,78 @@
{
"status": 200,
"body": {
"data": {
"id": "<id:1>",
"mirrorToAppleHealth": true
},
"error": null
},
"events": [
{
"status": 200,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": {
"mirror_to_apple_health": true,
"changed": false
},
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": false,
"mirrorToAppleHealth": true,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,109 @@
{
"status": 200,
"body": {
"data": [
{
"id": "<id:1>",
"name": "Scale",
"permissions": [
"measurements:write"
],
"lastUsedAt": null,
"expiresAt": "2027-01-13T12:00:00.000Z",
"createdAt": "<createdAt>",
"revoked": false,
"mirrorToAppleHealth": true
},
{
"id": "<id:2>",
"name": "Phone",
"permissions": [
"*"
],
"lastUsedAt": null,
"expiresAt": "2027-01-13T12:00:00.000Z",
"createdAt": "<createdAt>",
"revoked": false,
"mirrorToAppleHealth": false
},
{
"id": "<id:3>",
"name": "Connector",
"permissions": [
"health:read"
],
"lastUsedAt": null,
"expiresAt": "2027-01-13T12:00:00.000Z",
"createdAt": "<createdAt>",
"revoked": false,
"mirrorToAppleHealth": false
}
],
"error": null
},
"events": [
{
"status": 200,
"action": {
"name": "tokens.list"
},
"meta": null,
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": false,
"mirrorToAppleHealth": true,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,85 @@
{
"status": 200,
"body": {
"data": {
"id": "<id:1>",
"mirrorToAppleHealth": false
},
"error": null
},
"events": [
{
"status": 200,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": {
"mirror_to_apple_health": false,
"changed": true
},
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,83 @@
{
"status": 422,
"body": {
"data": null,
"error": "Only a measurement token has this setting",
"meta": {
"errorCode": "tokens.mirror.not_measurement_token",
"requestId": "<requestId>"
}
},
"events": [
{
"status": 422,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": null,
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,83 @@
{
"status": 422,
"body": {
"data": null,
"error": "Only a measurement token has this setting",
"meta": {
"errorCode": "tokens.mirror.not_measurement_token",
"requestId": "<requestId>"
}
},
"events": [
{
"status": 422,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": null,
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,83 @@
{
"status": 404,
"body": {
"data": null,
"error": "Token not found",
"meta": {
"errorCode": "http.not_found",
"requestId": "<requestId>"
}
},
"events": [
{
"status": 404,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": null,
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,83 @@
{
"status": 404,
"body": {
"data": null,
"error": "Token not found",
"meta": {
"errorCode": "http.not_found",
"requestId": "<requestId>"
}
},
"events": [
{
"status": 404,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": null,
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,100 @@
{
"status": 422,
"body": {
"data": null,
"error": "Validation failed",
"details": {
"issues": [
{
"path": "mirrorToAppleHealth",
"code": "invalid_type",
"message": "Invalid input: expected boolean, received string"
},
{
"path": "",
"code": "unrecognized_keys",
"message": "Unrecognized key: \"name\"",
"keys": [
"name"
]
}
]
},
"meta": {
"errorCode": "http.invalid",
"requestId": "<requestId>"
}
},
"events": [
{
"status": 422,
"action": {
"name": "tokens.measurements.mirror.update"
},
"meta": null,
"warnings": null,
"error": null
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,84 @@
{
"status": 401,
"body": {
"data": null,
"error": "Not authenticated",
"meta": {
"errorCode": "auth.missing",
"requestId": "<requestId>"
}
},
"events": [
{
"status": 401,
"action": null,
"meta": null,
"warnings": null,
"error": {
"type": "HttpError",
"message": "Not authenticated"
}
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,84 @@
{
"status": 401,
"body": {
"data": null,
"error": "Not authenticated",
"meta": {
"errorCode": "auth.missing",
"requestId": "<requestId>"
}
},
"events": [
{
"status": 401,
"action": null,
"meta": null,
"warnings": null,
"error": {
"type": "HttpError",
"message": "Not authenticated"
}
}
],
"stored": {
"tokens": [
{
"name": "Connector",
"permissions": [
"health:read"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Phone",
"permissions": [
"*"
],
"revoked": false,
"mirrorToAppleHealth": false,
"mirrorChangedAt": null
},
{
"name": "Scale",
"permissions": [
"measurements:write"
],
"revoked": true,
"mirrorToAppleHealth": false,
"mirrorChangedAt": "<mirrorChangedAt>"
}
],
"audit": [
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": true
}
},
{
"action": "tokens.measurements.mirror.update",
"details": {
"tokenId": "<id:1>",
"mirrorToAppleHealth": false
}
}
],
"readings": [
{
"source": "EXTERNAL",
"ingestTokenId": null,
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
},
{
"source": "EXTERNAL",
"ingestTokenId": "<id:1>",
"stamp": "2026-10-10T08:00:00.000Z",
"version": 1
}
]
}
}
@@ -0,0 +1,238 @@
/**
* Parity for the Apple Health write-back of measurement tokens (#1272):
* `PATCH /api/tokens/{id}`, the summary `GET /api/tokens/measurements` and
* the token list that carries the setting.
*
* One account walks through the cases the routes tell apart, in order: the
* summary with readings no token reaches, the switch turned on, the same
* value again (nothing written, nothing audited), turned off on a revoked
* token, a wildcard and an MCP token refused, another account's token, an
* unknown id, bodies the schema refuses, and a Bearer caller. After each
* request the snapshot holds the response, the wide event, the token rows,
* the audit rows and the readings' stamps, which must never move.
*/
import { beforeAll, describe, expect, it, vi } from "vitest";
import { PATCH, DELETE as revoke } from "@/app/api/tokens/[id]/route";
import { GET as listTokens } from "@/app/api/tokens/route";
import { GET as summary } from "@/app/api/tokens/measurements/route";
import { MEASUREMENTS_WRITE_SCOPE } from "@/lib/measurements/scopes";
import { callRoute, type RouteAuth } from "../../../kit/call-route";
import {
makeMeasurement,
makeSession,
makeToken,
makeUser,
type BearerFixture,
} from "../../../kit/factories";
import {
eventFacts,
expectParity,
stable,
wideEventRecorder,
} from "../../../kit/parity";
import { getPrismaClient, truncateAllTables } from "../../setup";
vi.mock("@/lib/logging/transports", async (importOriginal) => {
const actual =
await importOriginal<typeof import("@/lib/logging/transports")>();
const { wideEventRecorder: recorder } = await import("../../../kit/parity");
return {
...actual,
emitIfSampled: (event: (typeof recorder.events)[number]) => {
recorder.events.push(event);
},
};
});
const prisma = getPrismaClient();
const NOW = new Date("2026-10-15T12:00:00.000Z");
let userId: string;
let owner: RouteAuth;
let scale: BearerFixture;
let wildcard: BearerFixture;
let connector: BearerFixture;
let theirs: BearerFixture;
async function stored() {
const [tokens, audit, readings] = await Promise.all([
prisma.apiToken.findMany({
where: { userId },
orderBy: { name: "asc" },
select: {
name: true,
permissions: true,
revoked: true,
mirrorToAppleHealth: true,
mirrorChangedAt: true,
},
}),
prisma.auditLog.findMany({
where: { userId, action: { startsWith: "tokens." } },
orderBy: { createdAt: "asc" },
select: { action: true, details: true },
}),
prisma.measurement.findMany({
where: { userId },
orderBy: { measuredAt: "asc" },
select: {
source: true,
ingestTokenId: true,
updatedAt: true,
syncVersion: true,
},
}),
]);
// Renamed so the snapshot keeps the values (`updatedAt` and `syncVersion`
// are blanked as volatile): a switch must never move them.
return {
tokens,
// Parsed, so the token id inside reads as its placeholder.
audit: audit.map((a) => ({
action: a.action,
details: a.details ? JSON.parse(a.details) : null,
})),
readings: readings.map((r) => ({
source: r.source,
ingestTokenId: r.ingestTokenId,
stamp: r.updatedAt.toISOString(),
version: r.syncVersion,
})),
};
}
async function step(
name: string,
call:
| { kind: "patch"; id: string; body: unknown; auth?: RouteAuth }
| { kind: "summary" | "list"; auth?: RouteAuth },
): Promise<void> {
wideEventRecorder.take();
const res =
call.kind === "patch"
? await callRoute(PATCH, {
method: "PATCH",
route: "/api/tokens/{id}",
params: { id: call.id },
body: call.body,
auth: call.auth ?? owner,
})
: await callRoute(call.kind === "summary" ? summary : listTokens, {
method: "GET",
route:
call.kind === "summary"
? "/api/tokens/measurements"
: "/api/tokens",
auth: call.auth ?? owner,
});
const events = wideEventRecorder.take();
expect(events.length).toBeGreaterThan(0);
await expectParity(
`tokens-mirror.${name}`,
stable(
{
status: res.status,
body: res.body,
events: events.map(eventFacts),
stored: await stored(),
},
{ since: NOW, volatileKeys: ["mirrorChangedAt"] },
),
);
}
beforeAll(async () => {
vi.useFakeTimers({ toFake: ["Date"], now: NOW });
await truncateAllTables(prisma);
const user = await makeUser({ timezone: "UTC" });
userId = user.id;
owner = await makeSession(user);
scale = await makeToken(user, {
permissions: [MEASUREMENTS_WRITE_SCOPE],
name: "Scale",
});
wildcard = await makeToken(user, { permissions: ["*"], name: "Phone" });
connector = await makeToken(user, {
permissions: ["health:read"],
name: "Connector",
});
theirs = await makeToken(await makeUser({ timezone: "UTC" }), {
permissions: [MEASUREMENTS_WRITE_SCOPE],
name: "Theirs",
});
const stamp = new Date("2026-10-10T08:00:00.000Z");
await makeMeasurement(user, {
source: "EXTERNAL",
ingestTokenId: scale.tokenId,
measuredAt: new Date("2026-10-10T07:00:00.000Z"),
updatedAt: stamp,
});
await makeMeasurement(user, {
source: "EXTERNAL",
measuredAt: new Date("2026-10-09T07:00:00.000Z"),
updatedAt: stamp,
});
});
describe("the Apple Health write-back of a measurement token", () => {
it("walks the cases in order", async () => {
await step("01-summary", { kind: "summary" });
await step("02-on", {
kind: "patch",
id: scale.tokenId,
body: { mirrorToAppleHealth: true },
});
await step("03-on-again", {
kind: "patch",
id: scale.tokenId,
body: { mirrorToAppleHealth: true },
});
await step("04-list", { kind: "list" });
const revoked = await callRoute(revoke, {
method: "DELETE",
route: "/api/tokens/{id}",
params: { id: scale.tokenId },
auth: owner,
});
expect(revoked.status).toBe(200);
await step("05-off-revoked", {
kind: "patch",
id: scale.tokenId,
body: { mirrorToAppleHealth: false },
});
await step("06-wildcard", {
kind: "patch",
id: wildcard.tokenId,
body: { mirrorToAppleHealth: true },
});
await step("07-connector", {
kind: "patch",
id: connector.tokenId,
body: { mirrorToAppleHealth: true },
});
await step("08-another-account", {
kind: "patch",
id: theirs.tokenId,
body: { mirrorToAppleHealth: true },
});
await step("09-unknown", {
kind: "patch",
id: "missing-token",
body: { mirrorToAppleHealth: true },
});
await step("10-invalid-body", {
kind: "patch",
id: scale.tokenId,
body: { mirrorToAppleHealth: "yes", name: "Renamed" },
});
await step("11-bearer", {
kind: "patch",
id: scale.tokenId,
body: { mirrorToAppleHealth: true },
auth: wildcard,
});
await step("12-summary-bearer", { kind: "summary", auth: wildcard });
});
});
@@ -0,0 +1,132 @@
/**
* Migration 0476 links existing EXTERNAL readings to the token that carried
* them only where that is provable (#1272): the account holds exactly one
* measurement token, revoked ones counted, and the reading is no older than
* the token. Everything else stays unlinked. The pass writes nothing but the
* link, so no reading's `updatedAt` or `syncVersion` moves, and a rerun
* changes nothing.
*/
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { beforeEach, describe, expect, it } from "vitest";
import { MEASUREMENTS_WRITE_SCOPE } from "@/lib/measurements/scopes";
import { makeToken, makeUser } from "../kit/factories";
import { getPrismaClient, truncateAllTables } from "./setup";
const prisma = getPrismaClient();
const MIGRATION = readFileSync(
resolve(
process.cwd(),
"prisma",
"migrations",
"0476_token_apple_health_mirror",
"migration.sql",
),
"utf8",
);
/** The file is three `DO` blocks; each goes through as its own statement. */
async function runMigration(): Promise<void> {
const blocks = MIGRATION.match(/DO \$migration\$[\s\S]*?\$migration\$;/g);
expect(blocks).toHaveLength(3);
for (const block of blocks!) await prisma.$executeRawUnsafe(block);
}
const TOKEN_AT = new Date("2026-06-01T00:00:00.000Z");
async function externalReading(
userId: string,
createdAt: Date,
source: "EXTERNAL" | "MANUAL" = "EXTERNAL",
) {
return prisma.measurement.create({
data: {
userId,
type: "WEIGHT",
value: 80,
unit: "kg",
source,
measuredAt: createdAt,
createdAt,
updatedAt: new Date("2026-07-01T00:00:00.000Z"),
},
select: { id: true },
});
}
async function measurementToken(userId: string, revoked = false) {
const token = await makeToken(
{ id: userId },
{ permissions: [MEASUREMENTS_WRITE_SCOPE] },
);
await prisma.apiToken.update({
where: { id: token.tokenId },
data: { createdAt: TOKEN_AT, revoked },
});
return token.tokenId;
}
beforeEach(async () => {
await truncateAllTables(prisma);
});
describe("migration 0476 backfill", () => {
it("links only what one token provably carried", async () => {
const sole = await makeUser({ timezone: "UTC" });
const soleToken = await measurementToken(sole.id, true);
// A wildcard sign-in token does not make the measurement token ambiguous.
await makeToken(sole, { permissions: ["*"] });
const after = await externalReading(
sole.id,
new Date("2026-06-02T08:00:00.000Z"),
);
const before = await externalReading(
sole.id,
new Date("2026-05-30T08:00:00.000Z"),
);
const manual = await externalReading(
sole.id,
new Date("2026-06-03T08:00:00.000Z"),
"MANUAL",
);
const two = await makeUser({ timezone: "UTC" });
await measurementToken(two.id);
await measurementToken(two.id);
const ambiguous = await externalReading(
two.id,
new Date("2026-06-02T08:00:00.000Z"),
);
const stamps = await prisma.measurement.findMany({
select: { id: true, updatedAt: true, syncVersion: true },
orderBy: { id: "asc" },
});
await runMigration();
await runMigration();
const links = Object.fromEntries(
(
await prisma.measurement.findMany({
select: { id: true, ingestTokenId: true },
})
).map((r) => [r.id, r.ingestTokenId]),
);
expect(links).toEqual({
[after.id]: soleToken,
[before.id]: null,
[manual.id]: null,
[ambiguous.id]: null,
});
expect(
await prisma.measurement.findMany({
select: { id: true, updatedAt: true, syncVersion: true },
orderBy: { id: "asc" },
}),
).toEqual(stamps);
});
});