import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { NextFunction, Response } from 'express'; import type { AuthRequest } from '../../src/api/middlewares/auth'; const { mockVerifyApiKey } = vi.hoisted(() => ({ mockVerifyApiKey: vi.fn<(apiKey: string) => Promise>(), })); vi.mock('@/services/secrets/secret.service.js', () => ({ SecretService: { getInstance: () => ({ verifyApiKey: mockVerifyApiKey, }), }, })); async function loadAuthMiddleware() { vi.stubEnv('JWT_SECRET', 'test-secret-long-enough-for-signing-32chars'); return import('../../src/api/middlewares/auth'); } describe('verifyApiKey', () => { beforeEach(() => { vi.resetModules(); mockVerifyApiKey.mockReset(); }); afterEach(() => { vi.unstubAllEnvs(); }); it('sets hasApiKey only after the API key is verified', async () => { mockVerifyApiKey.mockResolvedValue(true); const { verifyApiKey } = await loadAuthMiddleware(); const req = { headers: { authorization: 'Bearer ik_valid', }, } as AuthRequest; const next = vi.fn() as NextFunction; await verifyApiKey(req, {} as Response, next); expect(mockVerifyApiKey).toHaveBeenCalledWith('ik_valid'); expect(req.authenticated).toBe(true); expect(req.hasApiKey).toBe(true); expect(Object.prototype.hasOwnProperty.call(req, 'apiKey')).toBe(false); expect(next).toHaveBeenCalledWith(); }); it('does not set hasApiKey when API key verification fails', async () => { mockVerifyApiKey.mockResolvedValue(false); const { verifyApiKey } = await loadAuthMiddleware(); const req = { headers: { 'x-api-key': 'ik_invalid', }, } as unknown as AuthRequest; const next = vi.fn() as NextFunction; await verifyApiKey(req, {} as Response, next); expect(mockVerifyApiKey).toHaveBeenCalledWith('ik_invalid'); expect(req.authenticated).toBeUndefined(); expect(req.hasApiKey).toBeUndefined(); expect(next).toHaveBeenCalledTimes(1); expect(next).toHaveBeenCalledWith(expect.objectContaining({ statusCode: 401 })); }); });