import { describe, it, expect, vi, beforeEach } from "vitest"; // ─── Mocks ─────────────────────────────────────────────────────────────────── const { fromMock, selectMock, eqMock, maybeSingleMock, insertMock } = vi.hoisted(() => ({ fromMock: vi.fn(), selectMock: vi.fn(), eqMock: vi.fn(), maybeSingleMock: vi.fn(), insertMock: vi.fn(), })); vi.mock("../supabase", () => ({ supabase: { from: fromMock }, })); beforeEach(() => { fromMock.mockReset(); selectMock.mockReset(); eqMock.mockReset(); maybeSingleMock.mockReset(); insertMock.mockReset(); fromMock.mockImplementation((table: string) => { if (table !== "stripe_webhook_events") return {}; return { select: selectMock, insert: insertMock, }; }); selectMock.mockReturnValue({ eq: eqMock }); eqMock.mockReturnValue({ maybeSingle: maybeSingleMock }); }); // ─── Tests ─────────────────────────────────────────────────────────────────── describe("isEventProcessed", () => { it("retourne true quand l'event est déjà journalisé", async () => { maybeSingleMock.mockResolvedValue({ data: { id: "evt_123" }, error: null }); const { isEventProcessed } = await import("../stripeWebhookEvents"); const result = await isEventProcessed("evt_123"); expect(result).toBe(true); expect(eqMock).toHaveBeenCalledWith("id", "evt_123"); }); it("retourne false quand l'event est absent", async () => { maybeSingleMock.mockResolvedValue({ data: null, error: null }); const { isEventProcessed } = await import("../stripeWebhookEvents"); const result = await isEventProcessed("evt_456"); expect(result).toBe(false); }); it("retourne false sur erreur de lecture (privilégie disponibilité)", async () => { maybeSingleMock.mockResolvedValue({ data: null, error: { message: "DB unreachable" }, }); const { isEventProcessed } = await import("../stripeWebhookEvents"); const result = await isEventProcessed("evt_789"); expect(result).toBe(false); }); it("retourne false pour un eventId vide sans toucher Supabase", async () => { const { isEventProcessed } = await import("../stripeWebhookEvents"); const result = await isEventProcessed(""); expect(result).toBe(false); expect(fromMock).not.toHaveBeenCalled(); }); }); describe("markEventProcessed", () => { it("insère l'event quand il n'existe pas", async () => { insertMock.mockResolvedValue({ error: null }); const { markEventProcessed } = await import("../stripeWebhookEvents"); await markEventProcessed("evt_new"); expect(insertMock).toHaveBeenCalledWith({ id: "evt_new" }); }); it("avale silencieusement un conflit unique (livraison concurrente)", async () => { insertMock.mockResolvedValue({ error: { code: "23505", message: "duplicate key" }, }); const { markEventProcessed } = await import("../stripeWebhookEvents"); await expect(markEventProcessed("evt_dup")).resolves.toBeUndefined(); }); it("ne throw pas sur erreur DB inattendue (webhook doit toujours répondre 200)", async () => { insertMock.mockResolvedValue({ error: { code: "08006", message: "connection failure" }, }); const { markEventProcessed } = await import("../stripeWebhookEvents"); await expect(markEventProcessed("evt_fail")).resolves.toBeUndefined(); }); it("no-op pour un eventId vide", async () => { const { markEventProcessed } = await import("../stripeWebhookEvents"); await markEventProcessed(""); expect(insertMock).not.toHaveBeenCalled(); }); });