import { beforeEach, describe, expect, it, vi } from "vitest"; const prismaMock = vi.hoisted(() => ({ auditLog: { create: vi.fn() }, alertLog: { create: vi.fn() }, })); vi.mock("@/lib/prisma", () => ({ prisma: prismaMock })); import { recordSecurityEvent, writeAudit } from "@/modules/audit/service"; describe("writeAudit", () => { beforeEach(() => { vi.clearAllMocks(); prismaMock.auditLog.create.mockResolvedValue({ id: BigInt(1) }); prismaMock.alertLog.create.mockResolvedValue({ id: BigInt(1) }); }); it("写入 audit_log 成功", async () => { await writeAudit("FORBIDDEN_ACCESS", "CUST_001", "QTE_B", { owner: "CUST_B", }); expect(prismaMock.auditLog.create).toHaveBeenCalledWith({ data: expect.objectContaining({ action: "FORBIDDEN_ACCESS", actorId: "CUST_001", resource: "QTE_B", }), }); }); it("DB 写失败不抛异常", async () => { prismaMock.auditLog.create.mockRejectedValue(new Error("db down")); const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}); await expect( writeAudit("TEST", "actor", "resource"), ).resolves.toBeUndefined(); consoleSpy.mockRestore(); }); }); describe("recordSecurityEvent", () => { beforeEach(() => { vi.clearAllMocks(); prismaMock.auditLog.create.mockResolvedValue({ id: BigInt(1) }); prismaMock.alertLog.create.mockResolvedValue({ id: BigInt(1) }); }); it("同时写 audit + SECURITY 预警", async () => { await recordSecurityEvent( "SECURITY_VALIDATION_BLOCKED", "CUST_001", "POST /api/quotes", { message: "injection" }, ); expect(prismaMock.auditLog.create).toHaveBeenCalled(); expect(prismaMock.alertLog.create).toHaveBeenCalledWith({ data: expect.objectContaining({ alertType: "SECURITY" }), }); }); });