From 508e19f190310ad5722b6f1d0f29d6b8207bf874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 07:45:19 +0200 Subject: [PATCH] test(backup): fill the parity account by key order and match audit rows by copy A table read without an order comes back in whatever order its last updates left on disk, so the column fill could pick a different row of a two-row table from run to run. The audit rows of a restore are now found by the copy's id instead of by time, since the database clock and the test's clock do not agree to the millisecond. The golden copies are written again from the code before the restore and export moved. --- .../export-disaster-recovery.json | 56 +++++++++---------- .../backup-parity/export-portable.json | 8 +-- .../restore-disaster-recovery.json | 48 ++++++++-------- .../backup-parity/restore-portable.json | 8 +-- .../backup-parity/seeded-account.json | 28 +++++----- tests/integration/backup-fixture.ts | 1 + tests/integration/backup-parity.test.ts | 12 +++- 7 files changed, 85 insertions(+), 76 deletions(-) diff --git a/tests/integration/__golden__/backup-parity/export-disaster-recovery.json b/tests/integration/__golden__/backup-parity/export-disaster-recovery.json index 038acf059..b217a23f5 100644 --- a/tests/integration/__golden__/backup-parity/export-disaster-recovery.json +++ b/tests/integration/__golden__/backup-parity/export-disaster-recovery.json @@ -249,20 +249,20 @@ "id": "", "type": "WEIGHT", "value": 74.2, - "valueMin": null, - "valueMax": null, + "valueMin": 2.5, + "valueMax": 2.5, "unit": "kg", "measuredAt": "2026-07-01T07:00:00.000Z", "source": "MANUAL", - "notes": null, - "notesEncrypted": null, + "notes": "rt-Measurement-notes", + "notesEncrypted": "", "externalId": null, - "externalSourceVersion": null, - "aggregationProvenance": null, + "externalSourceVersion": "rt-Measurement-externalSourceVersion", + "aggregationProvenance": "HEALTHKIT_STATISTICS", "glucoseContext": null, "sleepStage": null, "rhythmClassification": null, - "deviceType": null, + "deviceType": "rt-Measurement-deviceType", "syncVersion": 1, "deletedAt": null, "createdAt": "", @@ -272,20 +272,20 @@ "id": "", "type": "IRREGULAR_RHYTHM_NOTIFICATION", "value": 1, - "valueMin": 2.5, - "valueMax": 2.5, + "valueMin": null, + "valueMax": null, "unit": "event", "measuredAt": "2026-06-15T14:22:00.000Z", "source": "WITHINGS", - "notes": "rt-Measurement-notes", - "notesEncrypted": "", + "notes": null, + "notesEncrypted": null, "externalId": null, - "externalSourceVersion": "rt-Measurement-externalSourceVersion", - "aggregationProvenance": "HEALTHKIT_STATISTICS", + "externalSourceVersion": null, + "aggregationProvenance": null, "glucoseContext": null, "sleepStage": null, "rhythmClassification": "IRREGULAR", - "deviceType": "rt-Measurement-deviceType", + "deviceType": null, "syncVersion": 1, "deletedAt": null, "createdAt": "", @@ -2268,20 +2268,20 @@ "id": "", "type": "WEIGHT", "value": 74.2, - "valueMin": null, - "valueMax": null, + "valueMin": 2.5, + "valueMax": 2.5, "unit": "kg", "measuredAt": "2026-07-01T07:00:00.000Z", "source": "MANUAL", - "notes": null, - "notesEncrypted": null, + "notes": "rt-Measurement-notes", + "notesEncrypted": "", "externalId": null, - "externalSourceVersion": null, - "aggregationProvenance": null, + "externalSourceVersion": "rt-Measurement-externalSourceVersion", + "aggregationProvenance": "HEALTHKIT_STATISTICS", "glucoseContext": null, "sleepStage": null, "rhythmClassification": null, - "deviceType": null, + "deviceType": "rt-Measurement-deviceType", "syncVersion": 1, "deletedAt": null, "createdAt": "", @@ -2291,20 +2291,20 @@ "id": "", "type": "IRREGULAR_RHYTHM_NOTIFICATION", "value": 1, - "valueMin": 2.5, - "valueMax": 2.5, + "valueMin": null, + "valueMax": null, "unit": "event", "measuredAt": "2026-06-15T14:22:00.000Z", "source": "WITHINGS", - "notes": "rt-Measurement-notes", - "notesEncrypted": "", + "notes": null, + "notesEncrypted": null, "externalId": null, - "externalSourceVersion": "rt-Measurement-externalSourceVersion", - "aggregationProvenance": "HEALTHKIT_STATISTICS", + "externalSourceVersion": null, + "aggregationProvenance": null, "glucoseContext": null, "sleepStage": null, "rhythmClassification": "IRREGULAR", - "deviceType": "rt-Measurement-deviceType", + "deviceType": null, "syncVersion": 1, "deletedAt": null, "createdAt": "", diff --git a/tests/integration/__golden__/backup-parity/export-portable.json b/tests/integration/__golden__/backup-parity/export-portable.json index d1acb8fca..35ffc7afc 100644 --- a/tests/integration/__golden__/backup-parity/export-portable.json +++ b/tests/integration/__golden__/backup-parity/export-portable.json @@ -142,7 +142,7 @@ "unit": "kg", "measuredAt": "2026-07-01T07:00:00.000Z", "source": "MANUAL", - "notes": null, + "notes": "rt-Measurement-notesEncrypted", "deletedAt": null }, { @@ -152,7 +152,7 @@ "unit": "event", "measuredAt": "2026-06-15T14:22:00.000Z", "source": "WITHINGS", - "notes": "rt-Measurement-notesEncrypted", + "notes": null, "deletedAt": null } ], @@ -1763,7 +1763,7 @@ "unit": "kg", "measuredAt": "2026-07-01T07:00:00.000Z", "source": "MANUAL", - "notes": null, + "notes": "rt-Measurement-notesEncrypted", "deletedAt": null }, { @@ -1773,7 +1773,7 @@ "unit": "event", "measuredAt": "2026-06-15T14:22:00.000Z", "source": "WITHINGS", - "notes": "rt-Measurement-notesEncrypted", + "notes": null, "deletedAt": null } ], diff --git a/tests/integration/__golden__/backup-parity/restore-disaster-recovery.json b/tests/integration/__golden__/backup-parity/restore-disaster-recovery.json index b1c6ce959..c254dae87 100644 --- a/tests/integration/__golden__/backup-parity/restore-disaster-recovery.json +++ b/tests/integration/__golden__/backup-parity/restore-disaster-recovery.json @@ -435,20 +435,20 @@ "userId": "round-trip-owner", "type": "IRREGULAR_RHYTHM_NOTIFICATION", "value": 1, - "valueMin": 2.5, - "valueMax": 2.5, + "valueMin": null, + "valueMax": null, "unit": "event", "source": "WITHINGS", "measuredAt": "2026-06-15T14:22:00.000Z", "notes": null, - "notesEncrypted": "", + "notesEncrypted": null, "externalId": null, - "externalSourceVersion": "rt-Measurement-externalSourceVersion", - "aggregationProvenance": "HEALTHKIT_STATISTICS", + "externalSourceVersion": null, + "aggregationProvenance": null, "glucoseContext": null, "sleepStage": null, "rhythmClassification": "IRREGULAR", - "deviceType": "rt-Measurement-deviceType", + "deviceType": null, "syncVersion": 1, "deletedAt": null, "createdAt": "", @@ -507,20 +507,20 @@ "userId": "round-trip-owner", "type": "WEIGHT", "value": 74.2, - "valueMin": null, - "valueMax": null, + "valueMin": 2.5, + "valueMax": 2.5, "unit": "kg", "source": "MANUAL", "measuredAt": "2026-07-01T07:00:00.000Z", "notes": null, - "notesEncrypted": null, + "notesEncrypted": "", "externalId": null, - "externalSourceVersion": null, - "aggregationProvenance": null, + "externalSourceVersion": "rt-Measurement-externalSourceVersion", + "aggregationProvenance": "HEALTHKIT_STATISTICS", "glucoseContext": null, "sleepStage": null, "rhythmClassification": null, - "deviceType": null, + "deviceType": "rt-Measurement-deviceType", "syncVersion": 1, "deletedAt": null, "createdAt": "", @@ -2755,20 +2755,20 @@ "id": "", "type": "WEIGHT", "value": 74.2, - "valueMin": null, - "valueMax": null, + "valueMin": 2.5, + "valueMax": 2.5, "unit": "kg", "measuredAt": "2026-07-01T07:00:00.000Z", "source": "MANUAL", "notes": null, - "notesEncrypted": null, + "notesEncrypted": "", "externalId": null, - "externalSourceVersion": null, - "aggregationProvenance": null, + "externalSourceVersion": "rt-Measurement-externalSourceVersion", + "aggregationProvenance": "HEALTHKIT_STATISTICS", "glucoseContext": null, "sleepStage": null, "rhythmClassification": null, - "deviceType": null, + "deviceType": "rt-Measurement-deviceType", "syncVersion": 1, "deletedAt": null, "createdAt": "", @@ -2778,20 +2778,20 @@ "id": "", "type": "IRREGULAR_RHYTHM_NOTIFICATION", "value": 1, - "valueMin": 2.5, - "valueMax": 2.5, + "valueMin": null, + "valueMax": null, "unit": "event", "measuredAt": "2026-06-15T14:22:00.000Z", "source": "WITHINGS", "notes": null, - "notesEncrypted": "", + "notesEncrypted": null, "externalId": null, - "externalSourceVersion": "rt-Measurement-externalSourceVersion", - "aggregationProvenance": "HEALTHKIT_STATISTICS", + "externalSourceVersion": null, + "aggregationProvenance": null, "glucoseContext": null, "sleepStage": null, "rhythmClassification": "IRREGULAR", - "deviceType": "rt-Measurement-deviceType", + "deviceType": null, "syncVersion": 1, "deletedAt": null, "createdAt": "", diff --git a/tests/integration/__golden__/backup-parity/restore-portable.json b/tests/integration/__golden__/backup-parity/restore-portable.json index b182b8f13..f3554e295 100644 --- a/tests/integration/__golden__/backup-parity/restore-portable.json +++ b/tests/integration/__golden__/backup-parity/restore-portable.json @@ -533,7 +533,7 @@ "source": "WITHINGS", "measuredAt": "2026-06-15T14:22:00.000Z", "notes": null, - "notesEncrypted": "", + "notesEncrypted": null, "externalId": null, "externalSourceVersion": null, "aggregationProvenance": null, @@ -581,7 +581,7 @@ "source": "MANUAL", "measuredAt": "2026-07-01T07:00:00.000Z", "notes": null, - "notesEncrypted": null, + "notesEncrypted": "", "externalId": null, "externalSourceVersion": null, "aggregationProvenance": null, @@ -2519,7 +2519,7 @@ "unit": "kg", "measuredAt": "2026-07-01T07:00:00.000Z", "source": "MANUAL", - "notes": null, + "notes": "rt-Measurement-notesEncrypted", "deletedAt": null }, { @@ -2529,7 +2529,7 @@ "unit": "event", "measuredAt": "2026-06-15T14:22:00.000Z", "source": "WITHINGS", - "notes": "rt-Measurement-notesEncrypted", + "notes": null, "deletedAt": null } ], diff --git a/tests/integration/__golden__/backup-parity/seeded-account.json b/tests/integration/__golden__/backup-parity/seeded-account.json index 5400005db..32712c200 100644 --- a/tests/integration/__golden__/backup-parity/seeded-account.json +++ b/tests/integration/__golden__/backup-parity/seeded-account.json @@ -33,20 +33,20 @@ "userId": "round-trip-owner", "type": "IRREGULAR_RHYTHM_NOTIFICATION", "value": 1, - "valueMin": 2.5, - "valueMax": 2.5, + "valueMin": null, + "valueMax": null, "unit": "event", "source": "WITHINGS", "measuredAt": "2026-06-15T14:22:00.000Z", - "notes": "rt-Measurement-notes", - "notesEncrypted": "", + "notes": null, + "notesEncrypted": null, "externalId": null, - "externalSourceVersion": "rt-Measurement-externalSourceVersion", - "aggregationProvenance": "HEALTHKIT_STATISTICS", + "externalSourceVersion": null, + "aggregationProvenance": null, "glucoseContext": null, "sleepStage": null, "rhythmClassification": "IRREGULAR", - "deviceType": "rt-Measurement-deviceType", + "deviceType": null, "syncVersion": 1, "deletedAt": null, "createdAt": "", @@ -105,20 +105,20 @@ "userId": "round-trip-owner", "type": "WEIGHT", "value": 74.2, - "valueMin": null, - "valueMax": null, + "valueMin": 2.5, + "valueMax": 2.5, "unit": "kg", "source": "MANUAL", "measuredAt": "2026-07-01T07:00:00.000Z", - "notes": null, - "notesEncrypted": null, + "notes": "rt-Measurement-notes", + "notesEncrypted": "", "externalId": null, - "externalSourceVersion": null, - "aggregationProvenance": null, + "externalSourceVersion": "rt-Measurement-externalSourceVersion", + "aggregationProvenance": "HEALTHKIT_STATISTICS", "glucoseContext": null, "sleepStage": null, "rhythmClassification": null, - "deviceType": null, + "deviceType": "rt-Measurement-deviceType", "syncVersion": 1, "deletedAt": null, "createdAt": "", diff --git a/tests/integration/backup-fixture.ts b/tests/integration/backup-fixture.ts index 8d3b1f396..1ce127aee 100644 --- a/tests/integration/backup-fixture.ts +++ b/tests/integration/backup-fixture.ts @@ -1484,6 +1484,7 @@ export async function seedColumnShapes(prisma: PrismaClient): Promise { export type Delegate = { findMany(args: { where: Record; + orderBy?: Record[]; }): Promise[]>; update(args: { where: Record; diff --git a/tests/integration/backup-parity.test.ts b/tests/integration/backup-parity.test.ts index db96867bd..0d6903442 100644 --- a/tests/integration/backup-parity.test.ts +++ b/tests/integration/backup-parity.test.ts @@ -397,8 +397,12 @@ async function fillDefaultedColumns(prisma: PrismaClient): Promise { const enumValues = enumValuesFromSchema(); const unfillable: string[] = []; for (const model of models) { + // In key order, so the row that takes the fill is the same on every run: + // a table read without an order comes back in whatever order its last + // updates left on disk. const rows = await delegateOf(prisma, model).findMany({ where: ownerWhere(byName, prisma, model.name, OWNER_ID), + orderBy: model.primaryKey.map((key) => ({ [key]: "asc" as const })), }); if (rows.length === 0) continue; for (const column of model.columns) { @@ -624,7 +628,6 @@ async function runRestore( effects.unitRefresh.length = 0; effects.foldRepair.length = 0; effects.annotations.length = 0; - const auditFrom = new Date(); const progress: Array> = []; const raw = await restoreBackup({ backup, @@ -645,7 +648,12 @@ async function runRestore( }, }); const audit = await prisma.auditLog.findMany({ - where: { createdAt: { gte: auditFrom }, action: { startsWith: "admin." } }, + // By the copy's id rather than by time: the database clock and this + // process's clock do not agree to the millisecond. + where: { + action: { startsWith: "admin." }, + details: { contains: backup.id }, + }, orderBy: [{ createdAt: "asc" }, { id: "asc" }], select: { action: true, userId: true, details: true }, });