codex/backend-architecture-refactor #48
@ -0,0 +1,67 @@
|
|||||||
|
import { ArgumentsHost, BadRequestException, HttpStatus } from '@nestjs/common';
|
||||||
|
import { HttpExceptionFilter } from './http-exception.filter';
|
||||||
|
|
||||||
|
describe('HttpExceptionFilter', () => {
|
||||||
|
const createHost = () => {
|
||||||
|
const json = vi.fn();
|
||||||
|
const status = vi.fn(() => ({ json }));
|
||||||
|
const host = {
|
||||||
|
switchToHttp: () => ({
|
||||||
|
getResponse: () => ({ status }),
|
||||||
|
getRequest: () => ({ url: '/api/v1/test' }),
|
||||||
|
}),
|
||||||
|
} as unknown as ArgumentsHost;
|
||||||
|
|
||||||
|
return { host, status, json };
|
||||||
|
};
|
||||||
|
|
||||||
|
it('does not expose internal Error.message for unhandled exceptions', () => {
|
||||||
|
const filter = new HttpExceptionFilter();
|
||||||
|
const { host, status, json } = createHost();
|
||||||
|
|
||||||
|
filter.catch(new Error('Prisma failed at file:///secret/path'), host);
|
||||||
|
|
||||||
|
expect(status).toHaveBeenCalledWith(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
expect(json).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
message: 'Internal server error',
|
||||||
|
error: 'Internal Server Error',
|
||||||
|
path: '/api/v1/test',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(json.mock.calls[0][0].message).not.toContain('Prisma failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns safe defaults for non-Error thrown values', () => {
|
||||||
|
const filter = new HttpExceptionFilter();
|
||||||
|
const { host, status, json } = createHost();
|
||||||
|
|
||||||
|
filter.catch('some string error', host);
|
||||||
|
|
||||||
|
expect(status).toHaveBeenCalledWith(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
expect(json).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
message: 'Internal server error',
|
||||||
|
error: 'Internal Server Error',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps HttpException response messages intact', () => {
|
||||||
|
const filter = new HttpExceptionFilter();
|
||||||
|
const { host, status, json } = createHost();
|
||||||
|
|
||||||
|
filter.catch(new BadRequestException('Invalid request'), host);
|
||||||
|
|
||||||
|
expect(status).toHaveBeenCalledWith(HttpStatus.BAD_REQUEST);
|
||||||
|
expect(json).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
statusCode: HttpStatus.BAD_REQUEST,
|
||||||
|
message: 'Invalid request',
|
||||||
|
error: 'Bad Request',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -26,8 +26,9 @@ export class HttpExceptionFilter implements ExceptionFilter {
|
|||||||
error = (r.error as string) || exception.name;
|
error = (r.error as string) || exception.name;
|
||||||
}
|
}
|
||||||
} else if (exception instanceof Error) {
|
} else if (exception instanceof Error) {
|
||||||
message = exception.message;
|
|
||||||
this.logger.error(`Unhandled exception: ${exception.message}`, exception.stack);
|
this.logger.error(`Unhandled exception: ${exception.message}`, exception.stack);
|
||||||
|
} else {
|
||||||
|
this.logger.error(`Unhandled non-error exception: ${String(exception)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.status(status).json({
|
response.status(status).json({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user