backend/tests/unit/middlewares/error.test.js
2020-11-28 19:30:45 +01:00

152 lines
5.4 KiB
JavaScript

const httpStatus = require('http-status');
const httpMocks = require('node-mocks-http');
const { errorConverter, errorHandler } = require('../../../src/middlewares/error');
const ApiError = require('../../../src/utils/ApiError');
const config = require('../../../src/config/config');
const logger = require('../../../src/config/logger');
describe('Error middlewares', () => {
describe('Error converter', () => {
test('should return the same ApiError object it was called with', () => {
const error = new ApiError(httpStatus.BAD_REQUEST, 'Any error');
const next = jest.fn();
errorConverter(error, httpMocks.createRequest(), httpMocks.createResponse, next);
expect(next).toHaveBeenCalledWith(error);
});
test('should convert an Error to ApiError and preserve its status and message', () => {
const error = new Error('Any error');
error.statusCode = httpStatus.BAD_REQUEST;
const next = jest.fn();
errorConverter(error, httpMocks.createRequest(), httpMocks.createResponse, next);
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
expect(next).toHaveBeenCalledWith(
expect.objectContaining({
statusCode: error.statusCode,
message: error.message,
isOperational: false,
})
);
});
test('should convert an Error without status to ApiError with status 500', () => {
const error = new Error('Any error');
const next = jest.fn();
errorConverter(error, httpMocks.createRequest(), httpMocks.createResponse, next);
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
expect(next).toHaveBeenCalledWith(
expect.objectContaining({
statusCode: httpStatus.INTERNAL_SERVER_ERROR,
message: error.message,
isOperational: false,
})
);
});
test('should convert an Error without message to ApiError with default message of that http status', () => {
const error = new Error();
error.statusCode = httpStatus.BAD_REQUEST;
const next = jest.fn();
errorConverter(error, httpMocks.createRequest(), httpMocks.createResponse, next);
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
expect(next).toHaveBeenCalledWith(
expect.objectContaining({
statusCode: error.statusCode,
message: httpStatus[error.statusCode],
isOperational: false,
})
);
});
test('should convert any other object to ApiError with status 500 and its message', () => {
const error = {};
const next = jest.fn();
errorConverter(error, httpMocks.createRequest(), httpMocks.createResponse, next);
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
expect(next).toHaveBeenCalledWith(
expect.objectContaining({
statusCode: httpStatus.INTERNAL_SERVER_ERROR,
message: httpStatus[httpStatus.INTERNAL_SERVER_ERROR],
isOperational: false,
})
);
});
});
describe('Error handler', () => {
beforeEach(() => {
jest.spyOn(logger, 'error').mockImplementation(() => {});
});
test('should send proper error response and put the error message in res.locals', () => {
const error = new ApiError(httpStatus.BAD_REQUEST, 'Any error');
const res = httpMocks.createResponse();
const sendSpy = jest.spyOn(res, 'send');
errorHandler(error, httpMocks.createRequest(), res);
expect(sendSpy).toHaveBeenCalledWith(expect.objectContaining({ code: error.statusCode, message: error.message }));
expect(res.locals.errorMessage).toBe(error.message);
});
test('should put the error stack in the response if in development mode', () => {
config.env = 'development';
const error = new ApiError(httpStatus.BAD_REQUEST, 'Any error');
const res = httpMocks.createResponse();
const sendSpy = jest.spyOn(res, 'send');
errorHandler(error, httpMocks.createRequest(), res);
expect(sendSpy).toHaveBeenCalledWith(
expect.objectContaining({ code: error.statusCode, message: error.message, stack: error.stack })
);
config.env = process.env.NODE_ENV;
});
test('should send internal server error status and message if in production mode and error is not operational', () => {
config.env = 'production';
const error = new ApiError(httpStatus.BAD_REQUEST, 'Any error', false);
const res = httpMocks.createResponse();
const sendSpy = jest.spyOn(res, 'send');
errorHandler(error, httpMocks.createRequest(), res);
expect(sendSpy).toHaveBeenCalledWith(
expect.objectContaining({
code: httpStatus.INTERNAL_SERVER_ERROR,
message: httpStatus[httpStatus.INTERNAL_SERVER_ERROR],
})
);
expect(res.locals.errorMessage).toBe(error.message);
config.env = process.env.NODE_ENV;
});
test('should preserve original error status and message if in production mode and error is operational', () => {
config.env = 'production';
const error = new ApiError(httpStatus.BAD_REQUEST, 'Any error');
const res = httpMocks.createResponse();
const sendSpy = jest.spyOn(res, 'send');
errorHandler(error, httpMocks.createRequest(), res);
expect(sendSpy).toHaveBeenCalledWith(
expect.objectContaining({
code: error.statusCode,
message: error.message,
})
);
config.env = process.env.NODE_ENV;
});
});
});