// Unit tests for the audit helper. Outside a request context (vitest), the // `headers()` call throws and we should silently fall back to nulls — which // is exactly what the helper does. import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest"; import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaClient } from "@/generated/prisma/client"; import { audit } from "@/lib/audit"; import { seed, type SeedResult } from "@/lib/seed"; const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }); const db = new PrismaClient({ adapter }); let fx: SeedResult; beforeAll(async () => { fx = await seed(db); }); afterAll(async () => { await db.$disconnect(); }); beforeEach(async () => { await db.auditLog.deleteMany(); }); describe("audit", () => { test("creates an AuditLog row with the supplied fields", async () => { await audit(db, { actorId: fx.admin.id, action: "test.event", entityType: "Test", entityId: "abc", meta: { foo: "bar" }, }); const rows = await db.auditLog.findMany(); expect(rows).toHaveLength(1); expect(rows[0].actorId).toBe(fx.admin.id); expect(rows[0].action).toBe("test.event"); expect(rows[0].entityType).toBe("Test"); expect(rows[0].entityId).toBe("abc"); expect(rows[0].meta).toEqual({ foo: "bar" }); }); test("nulls IP/UA when called outside a request context", async () => { await audit(db, { actorId: fx.admin.id, action: "test.event", entityType: "Test", entityId: "abc", }); const row = await db.auditLog.findFirst(); expect(row?.ip).toBeNull(); expect(row?.ua).toBeNull(); }); test("accepts a null actorId for system events", async () => { await audit(db, { actorId: null, action: "system.heartbeat", entityType: "System", entityId: "1", }); const row = await db.auditLog.findFirst(); expect(row?.actorId).toBeNull(); }); test("does not throw if write fails (best-effort semantics)", async () => { // Simulate by sending an entityId that violates a column type or similar. // Easiest: inject a JSON value that's structured to roundtrip cleanly, // and verify no throw. (Real failure modes are network/disk, not validation.) await expect( audit(db, { actorId: fx.admin.id, action: "test.event", entityType: "Test", entityId: "ok", meta: { nested: { a: 1, b: [1, 2, 3] } }, }), ).resolves.toBeUndefined(); }); });