import { describe, expect, it, vi, beforeEach } from "vitest"; import type { H3Event } from "h3"; // Mock the auth utility const mocks = vi.hoisted(() => ({ authHandler: vi.fn(), defineEventHandler: vi.fn((handler) => handler), toWebRequest: vi.fn((event: H3Event) => { // Create a mock Request object const url = event.node.req.url || "/"; const method = event.node.req.method || "GET"; return new Request(`http://localhost${url}`, { method, headers: event.node.req.headers as HeadersInit, }); }), })); vi.mock("~~/shared/utils/auth", () => ({ auth: { handler: mocks.authHandler, }, })); // Mock H3 utilities vi.mock("h3", async () => { const actual = await vi.importActual("h3"); return { ...actual, defineEventHandler: mocks.defineEventHandler, toWebRequest: mocks.toWebRequest, }; }); describe("Auth API Handler", () => { let handler: any; beforeEach(async () => { vi.clearAllMocks(); // Set up global functions for Nuxt auto-imports (globalThis as any).defineEventHandler = mocks.defineEventHandler; (globalThis as any).toWebRequest = mocks.toWebRequest; // Dynamically import the handler after mocks are set up const module = await import("../../../server/api/[...auth]"); handler = module.default; }); it("should be defined", () => { expect(handler).toBeDefined(); expect(typeof handler).toBe("function"); }); it("should call auth.handler with converted web request", async () => { // Mock H3Event const mockEvent = { node: { req: { method: "POST", url: "/api/auth/sign-in", headers: { "content-type": "application/json", }, }, res: {}, }, context: {}, } as unknown as H3Event; // Mock the response from auth.handler const mockResponse = new Response(JSON.stringify({ success: true }), { status: 200, headers: { "content-type": "application/json" }, }); mocks.authHandler.mockResolvedValue(mockResponse); // Call the handler const result = await handler(mockEvent); // Verify auth.handler was called expect(mocks.authHandler).toHaveBeenCalledTimes(1); // Verify the result expect(result).toBe(mockResponse); }); it("should handle GET requests", async () => { const mockEvent = { node: { req: { method: "GET", url: "/api/auth/session", headers: {}, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response(JSON.stringify({ user: null }), { status: 200, headers: { "content-type": "application/json" }, }); mocks.authHandler.mockResolvedValue(mockResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockResponse); }); it("should handle POST requests for sign-in", async () => { const mockEvent = { node: { req: { method: "POST", url: "/api/auth/sign-in/email", headers: { "content-type": "application/json", }, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response( JSON.stringify({ user: { id: "123", email: "test@example.com", name: "Test User", }, session: { token: "abc123" }, }), { status: 200, headers: { "content-type": "application/json" }, } ); mocks.authHandler.mockResolvedValue(mockResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockResponse); }); it("should handle POST requests for sign-up", async () => { const mockEvent = { node: { req: { method: "POST", url: "/api/auth/sign-up/email", headers: { "content-type": "application/json", }, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response( JSON.stringify({ user: { id: "456", email: "newuser@example.com", name: "New User", }, session: { token: "xyz789" }, }), { status: 201, headers: { "content-type": "application/json" }, } ); mocks.authHandler.mockResolvedValue(mockResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockResponse); }); it("should handle POST requests for sign-out", async () => { const mockEvent = { node: { req: { method: "POST", url: "/api/auth/sign-out", headers: { "content-type": "application/json", }, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response(JSON.stringify({ success: true }), { status: 200, headers: { "content-type": "application/json" }, }); mocks.authHandler.mockResolvedValue(mockResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockResponse); }); it("should handle error responses from auth.handler", async () => { const mockEvent = { node: { req: { method: "POST", url: "/api/auth/sign-in/email", headers: { "content-type": "application/json", }, }, res: {}, }, context: {}, } as unknown as H3Event; const mockErrorResponse = new Response( JSON.stringify({ error: "Invalid credentials", }), { status: 401, headers: { "content-type": "application/json" }, } ); mocks.authHandler.mockResolvedValue(mockErrorResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockErrorResponse); }); it("should handle different HTTP methods", async () => { const methods = ["GET", "POST", "PUT", "DELETE", "PATCH"]; for (const method of methods) { vi.clearAllMocks(); const mockEvent = { node: { req: { method, url: "/api/auth/test", headers: {}, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response(JSON.stringify({ method }), { status: 200, headers: { "content-type": "application/json" }, }); mocks.authHandler.mockResolvedValue(mockResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockResponse); } }); it("should convert H3Event to Web Request correctly", async () => { const mockEvent = { node: { req: { method: "POST", url: "/api/auth/test", headers: { "content-type": "application/json", authorization: "Bearer token123", }, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response(JSON.stringify({ success: true }), { status: 200, headers: { "content-type": "application/json" }, }); mocks.authHandler.mockResolvedValue(mockResponse); await handler(mockEvent); // Verify that auth.handler was called with a Request object expect(mocks.authHandler).toHaveBeenCalledTimes(1); const callArg = mocks.authHandler.mock.calls[0][0]; // The argument should be a Request object (from toWebRequest conversion) expect(callArg).toBeDefined(); }); it("should handle requests with query parameters", async () => { const mockEvent = { node: { req: { method: "GET", url: "/api/auth/session?redirect=/dashboard", headers: {}, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response(JSON.stringify({ user: null }), { status: 200, headers: { "content-type": "application/json" }, }); mocks.authHandler.mockResolvedValue(mockResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockResponse); }); it("should handle requests with different content types", async () => { const contentTypes = ["application/json", "application/x-www-form-urlencoded", "multipart/form-data"]; for (const contentType of contentTypes) { vi.clearAllMocks(); const mockEvent = { node: { req: { method: "POST", url: "/api/auth/sign-in", headers: { "content-type": contentType, }, }, res: {}, }, context: {}, } as unknown as H3Event; const mockResponse = new Response(JSON.stringify({ success: true }), { status: 200, headers: { "content-type": "application/json" }, }); mocks.authHandler.mockResolvedValue(mockResponse); const result = await handler(mockEvent); expect(mocks.authHandler).toHaveBeenCalledTimes(1); expect(result).toBe(mockResponse); } }); });