/** * Tests unitaires pour src/middleware/ */ import { jest } from '@jest/globals'; // Mock du pool de base de données jest.unstable_mockModule('../../db.js', () => ({ pool: { query: jest.fn(), }, })); const { pool } = await import('../../db.js'); const { authenticateToken, authorizeRoles, optionalAuth } = await import('../../src/middleware/auth.js'); const { AppError, errorHandler, asyncHandler } = await import('../../src/middleware/errorHandler.js'); const { validate } = await import('../../src/middleware/validate.js'); describe('Auth Middleware', () => { let mockReq; let mockRes; let mockNext; beforeEach(() => { mockReq = { headers: {}, cookies: {}, }; mockRes = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis(), }; mockNext = jest.fn(); jest.clearAllMocks(); }); describe('authenticateToken', () => { it('should return 401 if no token provided', async () => { await authenticateToken(mockReq, mockRes, mockNext); expect(mockRes.status).toHaveBeenCalledWith(401); expect(mockRes.json).toHaveBeenCalledWith( expect.objectContaining({ success: false, }) ); expect(mockNext).not.toHaveBeenCalled(); }); it('should return 401 for invalid token', async () => { mockReq.headers.authorization = 'Bearer invalid-token'; await authenticateToken(mockReq, mockRes, mockNext); expect(mockRes.status).toHaveBeenCalledWith(401); expect(mockNext).not.toHaveBeenCalled(); }); }); describe('authorizeRoles', () => { it('should call next if user has required role', () => { mockReq.user = { role: 'ADMIN' }; const middleware = authorizeRoles('ADMIN', 'EMPLOYEE'); middleware(mockReq, mockRes, mockNext); expect(mockNext).toHaveBeenCalled(); }); it('should return 403 if user lacks required role', () => { mockReq.user = { role: 'CLIENT' }; const middleware = authorizeRoles('ADMIN'); middleware(mockReq, mockRes, mockNext); expect(mockRes.status).toHaveBeenCalledWith(403); expect(mockNext).not.toHaveBeenCalled(); }); }); describe('optionalAuth', () => { it('should call next without user if no token', async () => { await optionalAuth(mockReq, mockRes, mockNext); expect(mockNext).toHaveBeenCalled(); expect(mockReq.user).toBeUndefined(); }); }); }); describe('Error Handler Middleware', () => { let mockReq; let mockRes; let mockNext; beforeEach(() => { mockReq = {}; mockRes = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis(), }; mockNext = jest.fn(); }); describe('AppError', () => { it('should create an operational error', () => { const error = new AppError('Test error', 400); expect(error.message).toBe('Test error'); expect(error.statusCode).toBe(400); expect(error.isOperational).toBe(true); }); }); describe('errorHandler', () => { it('should handle AppError with correct status', () => { const error = new AppError('Not found', 404); errorHandler(error, mockReq, mockRes, mockNext); expect(mockRes.status).toHaveBeenCalledWith(404); expect(mockRes.json).toHaveBeenCalledWith( expect.objectContaining({ success: false, message: 'Not found', }) ); }); it('should handle generic errors with 500', () => { const error = new Error('Something went wrong'); errorHandler(error, mockReq, mockRes, mockNext); expect(mockRes.status).toHaveBeenCalledWith(500); }); }); describe('asyncHandler', () => { it('should pass errors to next middleware', async () => { const error = new Error('Async error'); const asyncFn = async () => { throw error; }; const wrappedFn = asyncHandler(asyncFn); await wrappedFn(mockReq, mockRes, mockNext); expect(mockNext).toHaveBeenCalledWith(error); }); it('should not call next on success', async () => { const asyncFn = async (req, res) => { res.json({ success: true }); }; const wrappedFn = asyncHandler(asyncFn); await wrappedFn(mockReq, mockRes, mockNext); expect(mockRes.json).toHaveBeenCalledWith({ success: true }); expect(mockNext).not.toHaveBeenCalled(); }); }); }); describe('Validation Middleware', () => { let mockReq; let mockRes; let mockNext; beforeEach(() => { mockReq = { body: {}, query: {}, params: {}, }; mockRes = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis(), }; mockNext = jest.fn(); }); describe('validate', () => { it('should call next for valid data', async () => { const { z } = await import('zod'); const schema = z.object({ body: z.object({ email: z.string().email(), }), }); mockReq.body = { email: 'test@example.com' }; const middleware = validate(schema); await middleware(mockReq, mockRes, mockNext); expect(mockNext).toHaveBeenCalled(); }); it('should return 400 for invalid data', async () => { const { z } = await import('zod'); const schema = z.object({ body: z.object({ email: z.string().email(), }), }); mockReq.body = { email: 'invalid-email' }; const middleware = validate(schema); await middleware(mockReq, mockRes, mockNext); expect(mockRes.status).toHaveBeenCalledWith(400); expect(mockNext).not.toHaveBeenCalled(); }); }); });