import { describe, expect, it, vi, beforeEach } from "vitest"; // Mock db module vi.mock("./db", () => ({ getEmailActionToken: vi.fn(), getRequestById: vi.fn(), updateRequest: vi.fn(), createRequestHistory: vi.fn(), markTokenAsUsed: vi.fn(), createEmailActionToken: vi.fn(), })); import * as db from "./db"; describe("Email Action Token Logic", () => { beforeEach(() => { vi.clearAllMocks(); }); it("should reject an invalid token", async () => { (db.getEmailActionToken as any).mockResolvedValue(null); const token = await db.getEmailActionToken("invalid-token"); expect(token).toBeNull(); }); it("should reject an already used token", async () => { (db.getEmailActionToken as any).mockResolvedValue({ id: 1, token: "test-token", requestId: 1, action: "validee", used: true, expiresAt: new Date(Date.now() + 86400000), usedAt: new Date(), usedBy: 1, createdAt: new Date(), }); const token = await db.getEmailActionToken("test-token"); expect(token).not.toBeNull(); expect(token!.used).toBe(true); }); it("should reject an expired token", async () => { const expiredDate = new Date(Date.now() - 86400000); // 1 day ago (db.getEmailActionToken as any).mockResolvedValue({ id: 1, token: "expired-token", requestId: 1, action: "validee", used: false, expiresAt: expiredDate, usedAt: null, usedBy: null, createdAt: new Date(), }); const token = await db.getEmailActionToken("expired-token"); expect(token).not.toBeNull(); expect(new Date() > token!.expiresAt).toBe(true); }); it("should accept a valid token for validation", async () => { const futureDate = new Date(Date.now() + 7 * 86400000); // 7 days from now (db.getEmailActionToken as any).mockResolvedValue({ id: 1, token: "valid-token", requestId: 5, action: "validee", used: false, expiresAt: futureDate, usedAt: null, usedBy: null, createdAt: new Date(), }); (db.getRequestById as any).mockResolvedValue({ id: 5, titre: "Subvention test", type: "subvention_projet", status: "soumise", description: "Test description", montantDemande: 100000, associationId: 1, }); const token = await db.getEmailActionToken("valid-token"); expect(token).not.toBeNull(); expect(token!.used).toBe(false); expect(new Date() < token!.expiresAt).toBe(true); expect(token!.action).toBe("validee"); const request = await db.getRequestById(token!.requestId); expect(request).not.toBeNull(); expect(request!.status).not.toBe("validee"); expect(request!.status).not.toBe("refusee"); }); it("should accept a valid token for refusal", async () => { const futureDate = new Date(Date.now() + 7 * 86400000); (db.getEmailActionToken as any).mockResolvedValue({ id: 2, token: "refuse-token", requestId: 5, action: "refusee", used: false, expiresAt: futureDate, usedAt: null, usedBy: null, createdAt: new Date(), }); const token = await db.getEmailActionToken("refuse-token"); expect(token).not.toBeNull(); expect(token!.action).toBe("refusee"); }); it("should reject processing an already validated request", async () => { (db.getRequestById as any).mockResolvedValue({ id: 5, titre: "Subvention test", type: "subvention_projet", status: "validee", associationId: 1, }); const request = await db.getRequestById(5); expect(request!.status === "validee" || request!.status === "refusee").toBe(true); }); it("should create email action tokens with correct expiry", async () => { (db.createEmailActionToken as any).mockResolvedValue("new-token"); const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); await db.createEmailActionToken({ token: "validate-token-123", requestId: 10, action: "validee", expiresAt, }); expect(db.createEmailActionToken).toHaveBeenCalledWith({ token: "validate-token-123", requestId: 10, action: "validee", expiresAt, }); }); it("should mark token as used after processing", async () => { (db.markTokenAsUsed as any).mockResolvedValue(undefined); await db.markTokenAsUsed("used-token", 1); expect(db.markTokenAsUsed).toHaveBeenCalledWith("used-token", 1); }); });