import { readFile } from "node:fs/promises"; import { describe, expect, it } from "vitest"; import { isValidPhone, normalizePhone } from "@/lib/server/account-store"; type Fixture = { version: 1; phoneCases: Array<{ input: string; normalized: string; valid: boolean }>; authorizationCases: Array<{ actorRole: string; targetRole: string; allowed: boolean }>; sessionVersionCases: Array<{ name: string; increment: boolean }>; archiveTables: string[]; }; const fixtureURL = new URL("../contracts/admin/accounts-organizations-v1.json", import.meta.url); describe("administration accounts and organizations v1 contract", () => { it("shares phone normalization and validation with the real TypeScript store", async () => { const fixture = JSON.parse(await readFile(fixtureURL, "utf8")) as Fixture; for (const testCase of fixture.phoneCases) { expect(normalizePhone(testCase.input)).toBe(testCase.normalized); expect(isValidPhone(testCase.input)).toBe(testCase.valid); } }); it("freezes route authorization and mutation policy consumed by Go", async () => { const fixture = JSON.parse(await readFile(fixtureURL, "utf8")) as Fixture; const accountsRoute = await readFile(new URL("../app/api/admin/accounts/route.ts", import.meta.url), "utf8"); const passwordRoute = await readFile(new URL("../app/api/admin/accounts/password/route.ts", import.meta.url), "utf8"); const organizationRoute = await readFile(new URL("../app/api/admin/organizations/route.ts", import.meta.url), "utf8"); const store = await readFile(new URL("../lib/server/account-store.ts", import.meta.url), "utf8"); expect(accountsRoute).toContain('target.role !== "user"'); expect(accountsRoute).toContain("actor.organizationId !== target.organizationId"); expect(passwordRoute).toContain('target.role !== "user"'); expect(organizationRoute).toContain("requireSuperAdmin(session.user)"); expect(store).toContain("sessionVersion: nextPassword || patch.role || patch.organizationId !== undefined || patch.status ? current.sessionVersion + 1 : current.sessionVersion"); expect(fixture.authorizationCases.filter((item) => item.allowed)).toHaveLength(2); expect(fixture.sessionVersionCases.filter((item) => item.increment).map((item) => item.name)).toEqual([ "role mutation", "organization mutation", "status mutation", "password mutation" ]); for (const table of fixture.archiveTables) expect(store).toContain(`"${table}"`); }); });