diff --git a/apps/server/src/core/error/filter/global-error.filter.spec.ts b/apps/server/src/core/error/filter/global-error.filter.spec.ts index ca40620515f..c45c13e4bff 100644 --- a/apps/server/src/core/error/filter/global-error.filter.spec.ts +++ b/apps/server/src/core/error/filter/global-error.filter.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable promise/valid-params */ import { NotFound } from '@feathersjs/errors'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { ArgumentsHost, BadRequestException, HttpStatus } from '@nestjs/common'; +import { ArgumentsHost, BadRequestException, HttpStatus, InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { BusinessError } from '@shared/common'; import { ErrorLogger, ErrorLogMessage, Loggable, LogMessage, ValidationErrorLogMessage } from '@src/core/logger'; @@ -9,6 +9,7 @@ import { Response } from 'express'; import util from 'util'; import { ErrorResponse } from '../dto'; import { ErrorLoggable } from '../loggable/error.loggable'; +import { ErrorUtils } from '../utils'; import { GlobalErrorFilter } from './global-error.filter'; class SampleBusinessError extends BusinessError { @@ -42,6 +43,24 @@ class SampleLoggableException extends BadRequestException implements Loggable { } } +class SampleLoggableExceptionWithCause extends InternalServerErrorException implements Loggable { + constructor(private readonly testValue: string, error?: unknown) { + super(ErrorUtils.createHttpExceptionOptions(error)); + } + + getLogMessage(): ErrorLogMessage { + const message: ErrorLogMessage = { + type: 'WITH_CAUSE', + stack: this.stack, + data: { + testValue: this.testValue, + }, + }; + + return message; + } +} + describe('GlobalErrorFilter', () => { let module: TestingModule; let service: GlobalErrorFilter; @@ -304,24 +323,101 @@ describe('GlobalErrorFilter', () => { ).toBeCalledWith(expectedResponse); }); }); + + describe('when error has a cause error', () => { + const setup = () => { + const causeError = new Error('Cause error'); + const error = new SampleLoggableExceptionWithCause('test', causeError); + const expectedResponse = new ErrorResponse( + 'SAMPLE_WITH_CAUSE', + 'Sample With Cause', + 'Sample Loggable Exception With Cause', + HttpStatus.INTERNAL_SERVER_ERROR + ); + + const argumentsHost = setupHttpArgumentsHost(); + + return { error, argumentsHost, expectedResponse }; + }; + + it('should set response status appropriately', () => { + const { error, argumentsHost } = setup(); + + service.catch(error, argumentsHost); + + expect(argumentsHost.switchToHttp().getResponse().status).toBeCalledWith( + HttpStatus.INTERNAL_SERVER_ERROR + ); + }); + + it('should send appropriate error response', () => { + const { error, argumentsHost, expectedResponse } = setup(); + + service.catch(error, argumentsHost); + + expect( + argumentsHost.switchToHttp().getResponse().status(HttpStatus.INTERNAL_SERVER_ERROR).json + ).toBeCalledWith(expectedResponse); + }); + }); }); describe('when context is rmq', () => { + describe('when error is unknown error', () => { + const setup = () => { + const argumentsHost = createMock(); + argumentsHost.getType.mockReturnValueOnce('rmq'); + + const error = new Error(); + + return { error, argumentsHost }; + }; + + it('should return an RpcMessage with the error', () => { + const { error, argumentsHost } = setup(); + + const result = service.catch(error, argumentsHost); + + expect(result).toEqual({ message: undefined, error }); + }); + }); + + describe('when error is a LoggableError', () => { + const setup = () => { + const causeError = new Error('Cause error'); + const error = new SampleLoggableExceptionWithCause('test', causeError); + const argumentsHost = createMock(); + argumentsHost.getType.mockReturnValueOnce('rmq'); + + return { error, argumentsHost }; + }; + + it('should return appropriate error', () => { + const { error, argumentsHost } = setup(); + + const result = service.catch(error, argumentsHost); + + expect(result).toEqual({ message: undefined, error }); + }); + }); + }); + + describe('when context is other than rmq and http', () => { const setup = () => { const argumentsHost = createMock(); - argumentsHost.getType.mockReturnValueOnce('rmq'); + argumentsHost.getType.mockReturnValueOnce('other'); const error = new Error(); return { error, argumentsHost }; }; - it('should return an RpcMessage with the error', () => { + it('should return undefined', () => { const { error, argumentsHost } = setup(); const result = service.catch(error, argumentsHost); - expect(result).toEqual({ message: undefined, error }); + expect(result).toBeUndefined(); }); }); }); diff --git a/apps/server/src/core/error/filter/global-error.filter.ts b/apps/server/src/core/error/filter/global-error.filter.ts index 7e0d1dc3c3f..56760b18dd9 100644 --- a/apps/server/src/core/error/filter/global-error.filter.ts +++ b/apps/server/src/core/error/filter/global-error.filter.ts @@ -24,7 +24,9 @@ export class GlobalErrorFilter implements Exceptio if (contextType === 'http') { this.sendHttpResponse(error, host); - } else if (contextType === 'rmq') { + } + + if (contextType === 'rmq') { return { message: undefined, error }; } }