import { describe, it, expect, vi, beforeEach } from "vitest"; vi.unmock("../services/email.js"); const { mockSendMail } = vi.hoisted(() => ({ mockSendMail: vi.fn(), })); vi.mock("nodemailer", () => ({ default: { createTransport: vi.fn(() => ({ sendMail: mockSendMail, })), }, })); vi.mock("../services/logger.js", () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() }, })); import { sendVerificationEmail } from "../services/email.js"; describe("Email Service", () => { beforeEach(() => { vi.clearAllMocks(); }); describe("sendVerificationEmail", () => { it("constructs correct email with code", async () => { mockSendMail.mockResolvedValueOnce({ messageId: "test-123" }); const result = await sendVerificationEmail("user@example.com", "654321"); expect(result).toBe(true); expect(mockSendMail).toHaveBeenCalledOnce(); const opts = mockSendMail.mock.calls[0][0]; expect(opts.to).toBe("user@example.com"); expect(opts.subject).toContain("Verify"); expect(opts.text).toContain("654321"); expect(opts.html).toContain("654321"); }); it("returns false when SMTP fails", async () => { mockSendMail.mockRejectedValueOnce(new Error("SMTP connection refused")); const result = await sendVerificationEmail("user@example.com", "123456"); expect(result).toBe(false); }); it("includes expiry notice in email body", async () => { mockSendMail.mockResolvedValueOnce({ messageId: "test-456" }); await sendVerificationEmail("user@example.com", "111111"); const opts = mockSendMail.mock.calls[0][0]; expect(opts.text).toContain("15 minutes"); }); }); });