import { describe, it, expect, vi } from "vitest"; import { createAuditErasureHook, createReqScopedAuditErasureHook, } from "./audit-erasure-hook"; import type { IAuditLog } from "../audit-log.interface"; function makeAuditLog(): IAuditLog { return { record: vi.fn().mockResolvedValue(undefined), eraseSubject: vi.fn().mockResolvedValue(undefined), }; } /** Minimal CollectionAfterDeleteHook args shape (only `doc` matters here). */ function hookArgs(id: unknown) { return { doc: { id }, req: {} as never, id: String(id), collection: {} as never, context: {}, }; } describe("createAuditErasureHook", () => { it("defaults to 'pseudonymize' mode", async () => { const auditLog = makeAuditLog(); const hook = createAuditErasureHook({ auditLog }); await hook(hookArgs("user_1") as never); expect(auditLog.eraseSubject).toHaveBeenCalledWith( "user_1", "pseudonymize", ); }); it("respects explicit mode='delete'", async () => { const auditLog = makeAuditLog(); const hook = createAuditErasureHook({ auditLog, mode: "delete" }); await hook(hookArgs("user_2") as never); expect(auditLog.eraseSubject).toHaveBeenCalledWith("user_2", "delete"); }); it("coerces numeric id to string", async () => { const auditLog = makeAuditLog(); const hook = createAuditErasureHook({ auditLog }); await hook(hookArgs(42) as never); expect(auditLog.eraseSubject).toHaveBeenCalledWith("42", "pseudonymize"); }); it("skips when doc.id is undefined", async () => { const auditLog = makeAuditLog(); const hook = createAuditErasureHook({ auditLog }); await hook(hookArgs(undefined) as never); expect(auditLog.eraseSubject).not.toHaveBeenCalled(); }); it("skips when doc.id is null", async () => { const auditLog = makeAuditLog(); const hook = createAuditErasureHook({ auditLog }); await hook(hookArgs(null) as never); expect(auditLog.eraseSubject).not.toHaveBeenCalled(); }); it("skips when doc.id is an object", async () => { const auditLog = makeAuditLog(); const hook = createAuditErasureHook({ auditLog }); await hook(hookArgs({ nested: true }) as never); expect(auditLog.eraseSubject).not.toHaveBeenCalled(); }); }); describe("createReqScopedAuditErasureHook (A6)", () => { function makeReqPayload(withAuditCollection: boolean) { const find = vi.fn().mockResolvedValue({ docs: [{ id: "log-1" }] }); const update = vi.fn().mockResolvedValue({}); const del = vi.fn().mockResolvedValue({}); const payload = { config: { collections: withAuditCollection ? [{ slug: "audit-logs" }] : [], }, find, update, delete: del, }; return { payload, find, update, del }; } function reqHookArgs(id: unknown, payload: unknown) { return { doc: { id }, req: { payload } as never, id: String(id), collection: {} as never, context: {}, }; } it("pseudonymizes the deleted subject's audit entries via req.payload", async () => { const { payload, find, update } = makeReqPayload(true); const hook = createReqScopedAuditErasureHook(); await hook(reqHookArgs("user_1", payload) as never); expect(find).toHaveBeenCalledWith( expect.objectContaining({ collection: "audit-logs", where: { actorId: { equals: "user_1" } }, }), ); expect(update).toHaveBeenCalledWith( expect.objectContaining({ collection: "audit-logs", id: "log-1", data: { actorId: expect.stringMatching(/^erased-/) }, }), ); }); it("respects mode='delete'", async () => { const { payload, del } = makeReqPayload(true); const hook = createReqScopedAuditErasureHook({ mode: "delete" }); await hook(reqHookArgs("user_2", payload) as never); expect(del).toHaveBeenCalledWith( expect.objectContaining({ collection: "audit-logs", where: { actorId: { equals: "user_2" } }, }), ); }); it("no-ops when the audit-logs collection is not registered", async () => { const { payload, find, update, del } = makeReqPayload(false); const hook = createReqScopedAuditErasureHook(); await hook(reqHookArgs("user_1", payload) as never); expect(find).not.toHaveBeenCalled(); expect(update).not.toHaveBeenCalled(); expect(del).not.toHaveBeenCalled(); }); it("skips invalid doc ids", async () => { const { payload, find } = makeReqPayload(true); const hook = createReqScopedAuditErasureHook(); await hook(reqHookArgs(undefined, payload) as never); expect(find).not.toHaveBeenCalled(); }); });