fix: resolve test failures in CI pipeline

- Add jest.setup.js with JWT_SECRET for test environment
- Update jest.config.js with setupFiles and increased timeout
- Fix auth middleware to return 401 (not 403) for invalid JWT tokens
- Fix errorHandler to return 'message' instead of 'error' in response
- Fix validate middleware to properly detect Zod errors in ESM
- Remove unused 'pool' import in middleware tests (lint fix)
- Update middleware tests to check next() calls with AppError

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
soufiane 2025-11-27 12:05:00 +01:00
parent 614abeb196
commit 74a7f387c5
6 changed files with 46 additions and 15 deletions

View File

@ -2,6 +2,9 @@ export default {
// Use Node's experimental ESM support
testEnvironment: 'node',
// Setup files to run before tests
setupFiles: ['./jest.setup.js'],
// Transform ES modules
transform: {},
@ -31,4 +34,7 @@ export default {
// Verbose output
verbose: true,
// Test timeout (increase for database operations)
testTimeout: 10000,
};

13
jest.setup.js Normal file
View File

@ -0,0 +1,13 @@
/**
* Jest setup file - configure test environment
*/
// Set test environment variables
process.env.NODE_ENV = 'test';
process.env.JWT_SECRET = 'test-jwt-secret-key-for-testing-purposes-only';
process.env.JWT_EXPIRES_IN = '1h';
process.env.PORT = '3001';
// Disable console logs during tests (optional)
// console.log = jest.fn();
// console.error = jest.fn();

View File

@ -51,10 +51,10 @@ export const authenticateToken = async (req, res, next) => {
next();
} catch (error) {
if (error.name === 'JsonWebTokenError') {
return next(new AppError('Token invalide', 403));
return next(new AppError('Token invalide', 401));
}
if (error.name === 'TokenExpiredError') {
return next(new AppError('Token expiré', 403));
return next(new AppError('Token expiré', 401));
}
return next(error);
}

View File

@ -52,7 +52,7 @@ export const errorHandler = (err, req, res, _next) => {
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Erreur serveur',
message: error.message || 'Erreur serveur',
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
});
};

View File

@ -11,16 +11,17 @@ export const validate = (schema) => async (req, res, next) => {
});
next();
} catch (error) {
if (error.errors) {
// Check for Zod validation errors (works with ESM)
if (error.issues && Array.isArray(error.issues)) {
// Erreurs de validation Zod
const errorMessages = error.errors.map((err) => ({
const errorMessages = error.issues.map((err) => ({
field: err.path.join('.'),
message: err.message,
}));
return res.status(400).json({
success: false,
error: 'Erreur de validation',
message: 'Erreur de validation',
details: errorMessages,
});
}

View File

@ -10,7 +10,6 @@ jest.unstable_mockModule('../../db.js', () => ({
},
}));
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');
@ -37,13 +36,12 @@ describe('Auth Middleware', () => {
it('should return 401 if no token provided', async () => {
await authenticateToken(mockReq, mockRes, mockNext);
expect(mockRes.status).toHaveBeenCalledWith(401);
expect(mockRes.json).toHaveBeenCalledWith(
expect(mockNext).toHaveBeenCalledWith(
expect.objectContaining({
success: false,
statusCode: 401,
isOperational: true,
})
);
expect(mockNext).not.toHaveBeenCalled();
});
it('should return 401 for invalid token', async () => {
@ -51,8 +49,12 @@ describe('Auth Middleware', () => {
await authenticateToken(mockReq, mockRes, mockNext);
expect(mockRes.status).toHaveBeenCalledWith(401);
expect(mockNext).not.toHaveBeenCalled();
expect(mockNext).toHaveBeenCalledWith(
expect.objectContaining({
statusCode: 401,
isOperational: true,
})
);
});
});
@ -72,8 +74,12 @@ describe('Auth Middleware', () => {
middleware(mockReq, mockRes, mockNext);
expect(mockRes.status).toHaveBeenCalledWith(403);
expect(mockNext).not.toHaveBeenCalled();
expect(mockNext).toHaveBeenCalledWith(
expect.objectContaining({
statusCode: 403,
isOperational: true,
})
);
});
});
@ -132,6 +138,11 @@ describe('Error Handler Middleware', () => {
errorHandler(error, mockReq, mockRes, mockNext);
expect(mockRes.status).toHaveBeenCalledWith(500);
expect(mockRes.json).toHaveBeenCalledWith(
expect.objectContaining({
success: false,
})
);
});
});