import { describe, it, expect, vi, beforeEach } from "vitest"; import { authMiddleware } from "../middleware/auth.js"; import { isValidKey, getKeyInfo } from "../services/keys.js"; const mockJson = vi.fn(); const mockStatus = vi.fn(() => ({ json: mockJson })); const mockNext = vi.fn(); function makeReq(headers: Record = {}): any { return { headers }; } function makeRes(): any { mockJson.mockClear(); mockStatus.mockClear(); return { status: mockStatus, json: mockJson }; } describe("authMiddleware", () => { beforeEach(() => { vi.clearAllMocks(); }); it("returns 401 when no auth header and no x-api-key", () => { const req = makeReq(); const res = makeRes(); authMiddleware(req, res, mockNext); expect(mockStatus).toHaveBeenCalledWith(401); expect(mockJson).toHaveBeenCalledWith( expect.objectContaining({ error: expect.stringContaining("Missing API key") }) ); expect(mockNext).not.toHaveBeenCalled(); }); it("returns 403 when Bearer token is invalid", () => { vi.mocked(isValidKey).mockReturnValueOnce(false); const req = makeReq({ authorization: "Bearer bad-key" }); const res = makeRes(); authMiddleware(req, res, mockNext); expect(mockStatus).toHaveBeenCalledWith(403); expect(mockJson).toHaveBeenCalledWith({ error: "Invalid API key" }); expect(mockNext).not.toHaveBeenCalled(); }); it("returns 403 when x-api-key is invalid", () => { vi.mocked(isValidKey).mockReturnValueOnce(false); const req = makeReq({ "x-api-key": "bad-key" }); const res = makeRes(); authMiddleware(req, res, mockNext); expect(mockStatus).toHaveBeenCalledWith(403); expect(mockNext).not.toHaveBeenCalled(); }); it("calls next() and attaches apiKeyInfo when Bearer token is valid", () => { const info = { key: "test-key", tier: "pro", email: "t@t.com", createdAt: "2025-01-01" }; vi.mocked(isValidKey).mockReturnValueOnce(true); vi.mocked(getKeyInfo).mockReturnValueOnce(info as any); const req = makeReq({ authorization: "Bearer test-key" }); const res = makeRes(); authMiddleware(req, res, mockNext); expect(mockNext).toHaveBeenCalled(); expect((req as any).apiKeyInfo).toEqual(info); }); it("calls next() and attaches apiKeyInfo when x-api-key is valid", () => { const info = { key: "xkey", tier: "free", email: "x@t.com", createdAt: "2025-01-01" }; vi.mocked(isValidKey).mockReturnValueOnce(true); vi.mocked(getKeyInfo).mockReturnValueOnce(info as any); const req = makeReq({ "x-api-key": "xkey" }); const res = makeRes(); authMiddleware(req, res, mockNext); expect(mockNext).toHaveBeenCalled(); expect((req as any).apiKeyInfo).toEqual(info); }); it("prefers Authorization header over x-api-key when both present", () => { vi.mocked(isValidKey).mockReturnValueOnce(true); vi.mocked(getKeyInfo).mockReturnValueOnce({ key: "bearer-key" } as any); const req = makeReq({ authorization: "Bearer bearer-key", "x-api-key": "header-key" }); const res = makeRes(); authMiddleware(req, res, mockNext); expect(isValidKey).toHaveBeenCalledWith("bearer-key"); expect((req as any).apiKeyInfo).toEqual({ key: "bearer-key" }); }); });