From 1eb0d22285b9bd939b642e5ead75a132565efaa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 6 Aug 2026 02:25:53 +0200 Subject: [PATCH] test: add record settings contract - Cover record-keyed DTO and cache behavior\n- Pin actor identity separately from the active record --- .../record-settings-contract.test.ts | 48 +++++++++++++++++++ .../record-settings-authorization.test.ts | 35 ++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/__tests__/record-settings-contract.test.ts create mode 100644 tests/integration/record-settings-authorization.test.ts diff --git a/src/__tests__/record-settings-contract.test.ts b/src/__tests__/record-settings-contract.test.ts new file mode 100644 index 000000000..98ea53cb0 --- /dev/null +++ b/src/__tests__/record-settings-contract.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from "vitest"; + +import { + assertRecordSettingsAccess, + type RecordSettingsAccess, +} from "@/lib/record-settings/access"; +import { toRecordSettingsDto } from "@/lib/record-settings/dto"; +import { recordSettingsKeys } from "@/lib/query-keys"; + +describe("record settings contract", () => { + it("names the target record in both the DTO and cache key", () => { + const dto = toRecordSettingsDto({ + id: "record-1", + name: "Managed profile", + locale: "de", + timezone: "Europe/Berlin", + recordKind: "managed", + }); + + expect(dto).toEqual({ + record: { + id: "record-1", + displayName: "Managed profile", + locale: "de", + timezone: "Europe/Berlin", + kind: "managed", + }, + }); + expect(recordSettingsKeys.detail("record-1")).toEqual([ + "record-settings", + "record-1", + "detail", + ]); + }); + + it("does not let an adult MANAGE grant use Guardian-only configuration", () => { + const access: RecordSettingsAccess = { + actorId: "adult-manager", + recordId: "managed-record", + recordKind: "managed", + relationship: "adult-manager", + }; + + expect(() => assertRecordSettingsAccess(access, "guardian")).toThrow( + "Guardian configuration is unavailable for this record", + ); + }); +}); diff --git a/tests/integration/record-settings-authorization.test.ts b/tests/integration/record-settings-authorization.test.ts new file mode 100644 index 000000000..15eaaf24c --- /dev/null +++ b/tests/integration/record-settings-authorization.test.ts @@ -0,0 +1,35 @@ +import { readFile } from "node:fs/promises"; +import { resolve } from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { resolveGuardianRecordSettingsAccess } from "@/lib/record-settings/access"; + +const fromRoot = (...segments: string[]) => resolve(process.cwd(), ...segments); + +describe("record settings authorization", () => { + it("keeps actor and record separation", async () => { + const [authMeRoute, recordSettingsRoute] = await Promise.all([ + readFile(fromRoot("src/app/api/auth/me/route.ts"), "utf8"), + readFile(fromRoot("src/app/api/record-settings/route.ts"), "utf8"), + ]); + + const access = resolveGuardianRecordSettingsAccess({ + actor: { id: "guardian-1" }, + grantId: "grant-1", + user: { id: "managed-record-1", managedProfileAt: new Date() }, + }); + + expect(access).toMatchObject({ + actorId: "guardian-1", + recordId: "managed-record-1", + recordKind: "managed", + relationship: "guardian", + }); + expect(authMeRoute).toContain("requireAuth"); + expect(authMeRoute).not.toContain("requireGuardianAuth"); + expect(authMeRoute).not.toContain("requireRecordAuth"); + expect(recordSettingsRoute).toContain("requireGuardianAuth"); + expect(recordSettingsRoute).toContain("toRecordSettingsDto"); + }); +});