diff --git a/tests/integration/environment-air-quality-history.test.ts b/tests/integration/environment-air-quality-history.test.ts index 249ecd696..26ef059c2 100644 --- a/tests/integration/environment-air-quality-history.test.ts +++ b/tests/integration/environment-air-quality-history.test.ts @@ -261,8 +261,10 @@ describe("air-quality history backfill", () => { source: "TRAVEL", pm25Mean: 12, tempMax: 5, - lat: null, }); + // The place lives only in the sealed copy; migration 0409 dropped the + // readable columns. + expect(byDate.get("2020-03-01")).not.toHaveProperty("lat"); expect( openLocation(byDate.get("2020-03-01")!.locationEncrypted!), ).toMatchObject({ lat: 51.5, lon: 7.0 }); diff --git a/tests/integration/free-text-encryption-backfill.test.ts b/tests/integration/free-text-encryption-backfill.test.ts index f35b9a577..1733c5130 100644 --- a/tests/integration/free-text-encryption-backfill.test.ts +++ b/tests/integration/free-text-encryption-backfill.test.ts @@ -126,6 +126,8 @@ describe("free-text encryption backfill (real Postgres)", () => { // The boot call is deferred past the startup storm. expect(first.sent.every((s) => s.startAfter === 180)).toBe(true); + // The summary carries no location counts: migration 0409 dropped the + // readable home, period and day locations, so there is nothing to seal. expect(await runFreeTextEncryptionBackfillForUser("ft-a")).toEqual({ conversationTitlesMigrated: 1, metricNotesMigrated: 0, @@ -133,9 +135,6 @@ describe("free-text encryption backfill (real Postgres)", () => { routeGeometriesMigrated: 0, appointmentAddressesCleared: 0, contactAuditRowsScrubbed: 0, - homeLocationsMigrated: 0, - travelLocationsMigrated: 0, - dayLocationsMigrated: 0, }); expect(await runFreeTextEncryptionBackfillForUser("ft-b")).toEqual({ conversationTitlesMigrated: 0, @@ -144,9 +143,6 @@ describe("free-text encryption backfill (real Postgres)", () => { routeGeometriesMigrated: 0, appointmentAddressesCleared: 0, contactAuditRowsScrubbed: 0, - homeLocationsMigrated: 0, - travelLocationsMigrated: 0, - dayLocationsMigrated: 0, }); const conversations = await prisma.coachConversation.findMany({ @@ -195,9 +191,6 @@ describe("free-text encryption backfill (real Postgres)", () => { routeGeometriesMigrated: 0, appointmentAddressesCleared: 0, contactAuditRowsScrubbed: 0, - homeLocationsMigrated: 0, - travelLocationsMigrated: 0, - dayLocationsMigrated: 0, }); }); @@ -282,9 +275,6 @@ describe("free-text encryption backfill (real Postgres)", () => { routeGeometriesMigrated: 0, appointmentAddressesCleared: 0, contactAuditRowsScrubbed: 0, - homeLocationsMigrated: 0, - travelLocationsMigrated: 0, - dayLocationsMigrated: 0, }); expect(await runFreeTextEncryptionBackfillForUser("pc-b")).toEqual({ conversationTitlesMigrated: 0, @@ -293,9 +283,6 @@ describe("free-text encryption backfill (real Postgres)", () => { routeGeometriesMigrated: 1, appointmentAddressesCleared: 0, contactAuditRowsScrubbed: 0, - homeLocationsMigrated: 0, - travelLocationsMigrated: 0, - dayLocationsMigrated: 0, }); const practitioners = await prisma.practitioner.findMany({ @@ -356,9 +343,6 @@ describe("free-text encryption backfill (real Postgres)", () => { routeGeometriesMigrated: 0, appointmentAddressesCleared: 0, contactAuditRowsScrubbed: 0, - homeLocationsMigrated: 0, - travelLocationsMigrated: 0, - dayLocationsMigrated: 0, }); }); @@ -456,9 +440,6 @@ describe("free-text encryption backfill (real Postgres)", () => { expect(summaryA).toMatchObject({ appointmentAddressesCleared: 1, contactAuditRowsScrubbed: 1, - homeLocationsMigrated: 0, - travelLocationsMigrated: 0, - dayLocationsMigrated: 0, }); await runFreeTextEncryptionBackfillForUser("ad-b"); diff --git a/tests/integration/managed-profile-surface.test.ts b/tests/integration/managed-profile-surface.test.ts index 253db33a0..1758a5a52 100644 --- a/tests/integration/managed-profile-surface.test.ts +++ b/tests/integration/managed-profile-surface.test.ts @@ -22,6 +22,7 @@ import { cookieJar, headerJar, queuedSessionIds } from "./mock-next-headers"; import { getPrismaClient, switchSessionTo, truncateAllTables } from "./setup"; import { createManagedProfile } from "@/lib/managed-profiles/create"; import { acceptGrant, inviteGrant } from "@/lib/sharing/grants"; +import { bodyWithoutRequestId } from "@/__tests__/helpers/error-envelope"; process.env.API_TOKEN_HMAC_KEY ??= "test-hmac-key-managed-profile-surface-32-bytes-min-1234509876"; @@ -294,9 +295,18 @@ describe("who looks after a managed profile (real Postgres)", () => { const stranger = await person("stranger"); signIn(stranger); - const refused = await (await listGuardians(profile.id)).json(); - const unknown = await (await listGuardians("does-not-exist")).json(); - expect(unknown).toEqual(refused); + // `meta.requestId` is a fresh id per request and says nothing about the + // profile; every other byte has to match. + const refusedRes = await listGuardians(profile.id); + const unknownRes = await listGuardians("does-not-exist"); + expect(unknownRes.status).toBe(refusedRes.status); + const refused = await refusedRes.clone().json(); + const unknown = await unknownRes.clone().json(); + expect(typeof refused.meta?.requestId).toBe("string"); + expect(typeof unknown.meta?.requestId).toBe("string"); + expect(await bodyWithoutRequestId(unknownRes)).toBe( + await bodyWithoutRequestId(refusedRes), + ); }); it.each(["READ", "WRITE", "MANAGE"] as const)( diff --git a/tests/integration/sharing-manage-handler-matrix.test.ts b/tests/integration/sharing-manage-handler-matrix.test.ts index 28686b708..8cb459941 100644 --- a/tests/integration/sharing-manage-handler-matrix.test.ts +++ b/tests/integration/sharing-manage-handler-matrix.test.ts @@ -282,7 +282,7 @@ const ROUTE_IMPORTS = import.meta.glob>( describe("the complete MANAGE handler matrix", () => { it("starts with a non-empty, exact handler inventory", () => { - expect(ADMITTED_MUTATING_HANDLERS.length).toBe(89); + expect(ADMITTED_MUTATING_HANDLERS.length).toBe(90); }); }); @@ -2953,13 +2953,13 @@ describe("the conditions the admissions were granted on", () => { describe("the complete MANAGE handler matrix", () => { it("registers one strict actor-and-effect driver for every admission", () => { const expected = ADMITTED_MUTATING_HANDLERS.map(matrixKey).sort(); - expect(STRICT_DRIVER_KEYS.size).toBe(89); + expect(STRICT_DRIVER_KEYS.size).toBe(90); expect([...STRICT_DRIVER_KEYS].sort()).toEqual(expected); }); it("executes every registered driver through its owned effect and actor audit", () => { const expected = ADMITTED_MUTATING_HANDLERS.map(matrixKey).sort(); - expect(REAL_EFFECT_KEYS.size).toBe(89); + expect(REAL_EFFECT_KEYS.size).toBe(90); expect([...REAL_EFFECT_KEYS].sort()).toEqual(expected); }); }); diff --git a/tests/integration/wipe-seeder.ts b/tests/integration/wipe-seeder.ts index c23514fc9..23274df00 100644 --- a/tests/integration/wipe-seeder.ts +++ b/tests/integration/wipe-seeder.ts @@ -133,6 +133,27 @@ export async function seedEveryTable( if (!enums.has(row.typname)) enums.set(row.typname, row.enumlabel); } + // A column fenced by a single-column `CHECK (col IN (...))` takes the first + // value the fence admits; a synthesised tag would be refused before the wipe + // ever ran. Postgres stores the IN list as `col = ANY (ARRAY['a'::text, ...])`. + const checkRows = await prisma.$queryRaw< + Array<{ table_name: string; column_name: string; def: string }> + >` + SELECT con.conrelid::regclass::text AS table_name, + att.attname AS column_name, + pg_get_constraintdef(con.oid) AS def + FROM pg_constraint con + JOIN pg_attribute att + ON att.attrelid = con.conrelid AND att.attnum = con.conkey[1] + WHERE con.contype = 'c' + AND con.connamespace = 'public'::regnamespace + AND cardinality(con.conkey) = 1`; + const admitted = new Map(); + for (const row of checkRows) { + const first = /=\s*ANY\s*\(\s*\(?ARRAY\['((?:[^']|'')*)'/.exec(row.def); + if (first) admitted.set(`${row.table_name}.${row.column_name}`, first[1]); + } + const byTable = new Map(); for (const column of columns) { if (SEED_SKIP.has(column.table_name)) continue; @@ -206,7 +227,12 @@ export async function seedEveryTable( continue; } names.push(column.column_name); - values.push(synthesise(column.udt_name, enums, `seed-${tag++}`)); + const fenced = admitted.get(`${table}.${column.column_name}`); + values.push( + fenced !== undefined + ? `'${fenced}'` + : synthesise(column.udt_name, enums, `seed-${tag++}`), + ); } const sql =