366 lines
9.3 KiB
TypeScript
366 lines
9.3 KiB
TypeScript
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<typeof import("h3")>("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);
|
|
}
|
|
});
|
|
});
|