import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import path from "node:path"; import { describe, it } from "vitest"; import { formatAuditCascadeGuardResult, migrationPath, schemaPath, verifyAuditCascadeGuard, } from "./verify-audit-cascade-guard.mjs"; const hardenedSchema = readFileSync(schemaPath, "utf8"); const hardenedMigration = readFileSync(migrationPath, "utf8"); const packageJson = JSON.parse(readFileSync(path.resolve("package.json"), "utf8")); function verifyWithOverrides({ schemaText = hardenedSchema, migrationSql = hardenedMigration } = {}) { return verifyAuditCascadeGuard({ schemaText, migrationSql }); } describe("verify-audit-cascade-guard", () => { it("passes against the hardened Prisma schema and migration", () => { const result = verifyWithOverrides(); assert.equal(result.ok, true); assert.deepEqual(result.failures, []); assert.match(formatAuditCascadeGuardResult(result), /^PASS impersonation audit cascade guard/); }); it("fails when an audit relation reintroduces onDelete Cascade", () => { const result = verifyWithOverrides({ schemaText: hardenedSchema.replace("onDelete: SetNull)", "onDelete: Cascade)"), }); assert.equal(result.ok, false); assert.deepEqual( result.failures.map((failure) => failure.id), ["schema.actor.optional-set-null", "schema.relation.no-cascade"], ); assert.match(formatAuditCascadeGuardResult(result), /onDelete: Cascade/); }); it("fails when actor or target SQL columns are made NOT NULL", () => { const result = verifyWithOverrides({ migrationSql: hardenedMigration .replace('"actorUserId" TEXT,', '"actorUserId" TEXT NOT NULL,') .replace('"targetUserId" TEXT,', '"targetUserId" TEXT NOT NULL,'), }); assert.equal(result.ok, false); assert.deepEqual( result.failures.map((failure) => failure.id), ["migration.actorUserId.nullable", "migration.targetUserId.nullable"], ); }); it("fails when actor, target, or tenant foreign keys are missing ON DELETE SET NULL", () => { const result = verifyWithOverrides({ migrationSql: hardenedMigration.replaceAll("ON DELETE SET NULL", "ON DELETE CASCADE"), }); assert.equal(result.ok, false); assert.deepEqual( result.failures.map((failure) => failure.id), ["migration.actorUserId.set-null", "migration.targetUserId.set-null", "migration.tenantId.set-null"], ); assert.match(formatAuditCascadeGuardResult(result), /migration\.tenantId\.set-null/); }); it("registers the package script for the guard command", () => { assert.equal(packageJson.scripts["verify:audit-cascade-guard"], "node scripts/verify-audit-cascade-guard.mjs"); }); });