import { beforeEach, describe, expect, it, vi } from "vitest"; const { mockFindFirst, mockUpsert, mockFindUnique, mockUpdate, mockDelete, mockSendMail, mockRedirect, } = vi.hoisted(() => ({ mockFindFirst: vi.fn(), mockUpsert: vi.fn(), mockFindUnique: vi.fn(), mockUpdate: vi.fn(), mockDelete: vi.fn(), mockSendMail: vi.fn(), mockRedirect: vi.fn(), })); vi.mock("next/navigation", () => ({ redirect: (...args: unknown[]) => { mockRedirect(...args); throw new Error("redirect"); }, })); vi.mock("@/lib/prisma", () => ({ prisma: { user: { findFirst: mockFindFirst, update: mockUpdate }, passwordReset: { upsert: mockUpsert, findUnique: mockFindUnique, delete: mockDelete, }, }, })); vi.mock("@/lib/services/email", () => ({ sendMail: mockSendMail, })); vi.mock("@/lib/services/captcha", () => ({ captchaConfig: vi.fn().mockResolvedValue({ provider: "none", siteKey: "", field: "", }), verifyCaptcha: vi.fn().mockResolvedValue(true), })); vi.mock("@/lib/rate-limit", () => ({ rateLimit: vi.fn().mockResolvedValue({ ok: true, retryAfter: 0 }), clientIp: vi.fn().mockResolvedValue("127.0.0.1"), })); vi.mock("@/env", () => ({ env: { APP_URL: "http://localhost:3000", HOTEL_NAME: "TestHotel" }, })); import { requestReset } from "./password-reset"; beforeEach(() => { vi.clearAllMocks(); }); describe("requestReset", () => { it("sends a reset email when the user exists", async () => { mockFindFirst.mockResolvedValue({ id: 1 }); mockUpsert.mockResolvedValue({}); const fd = new FormData(); fd.set("email", "user@example.com"); await expect(requestReset(fd)).rejects.toThrow("redirect"); expect(mockFindFirst).toHaveBeenCalledWith( expect.objectContaining({ where: { mail: "user@example.com" } }), ); expect(mockUpsert).toHaveBeenCalled(); expect(mockSendMail).toHaveBeenCalledWith( "user@example.com", expect.stringContaining("password reset"), expect.stringContaining("http://localhost:3000/reset"), ); }); it("does not send email when user is not found", async () => { mockFindFirst.mockResolvedValue(null); const fd = new FormData(); fd.set("email", "unknown@example.com"); await expect(requestReset(fd)).rejects.toThrow("redirect"); expect(mockSendMail).not.toHaveBeenCalled(); }); it("rate limits and does not throw on email without @", async () => { const fd = new FormData(); fd.set("email", "not-an-email"); await expect(requestReset(fd)).rejects.toThrow("redirect"); expect(mockFindFirst).not.toHaveBeenCalled(); }); });