mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-20 09:24:10 +01:00
* chore: fix mock typing in packages/api tests * chore: improve imports, type handling and method signatures for MCPServersRegistry * chore: use enum in migration scripts * chore: ParsedServerConfig type to enhance server configuration handling * feat: Implement agent permissions migration check and logging * feat: Integrate migration checks into server initialization process * feat: Add prompt permissions migration check and logging to server initialization * chore: move prompt formatting functions to dedicated prompts dir
266 lines
9 KiB
TypeScript
266 lines
9 KiB
TypeScript
import { logger } from '@librechat/data-schemas';
|
|
import { ErrorController } from './error';
|
|
import type { Request, Response } from 'express';
|
|
import type { ValidationError, MongoServerError, CustomError } from '~/types';
|
|
|
|
// Mock the logger
|
|
jest.mock('@librechat/data-schemas', () => ({
|
|
...jest.requireActual('@librechat/data-schemas'),
|
|
logger: {
|
|
error: jest.fn(),
|
|
warn: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
describe('ErrorController', () => {
|
|
let mockReq: Request;
|
|
let mockRes: Response;
|
|
let mockNext: jest.Mock;
|
|
|
|
beforeEach(() => {
|
|
mockReq = {
|
|
originalUrl: '',
|
|
} as Request;
|
|
mockRes = {
|
|
status: jest.fn().mockReturnThis(),
|
|
send: jest.fn(),
|
|
} as unknown as Response;
|
|
(logger.error as jest.Mock).mockClear();
|
|
mockNext = jest.fn();
|
|
});
|
|
|
|
describe('ValidationError handling', () => {
|
|
it('should handle ValidationError with single error', () => {
|
|
const validationError = {
|
|
name: 'ValidationError',
|
|
message: 'Validation error',
|
|
errors: {
|
|
email: { message: 'Email is required', path: 'email' },
|
|
},
|
|
} as ValidationError;
|
|
|
|
ErrorController(validationError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(400);
|
|
expect(mockRes.send).toHaveBeenCalledWith({
|
|
messages: '["Email is required"]',
|
|
fields: '["email"]',
|
|
});
|
|
expect(logger.error).toHaveBeenCalledWith('Validation error:', validationError.errors);
|
|
});
|
|
|
|
it('should handle ValidationError with multiple errors', () => {
|
|
const validationError = {
|
|
name: 'ValidationError',
|
|
message: 'Validation error',
|
|
errors: {
|
|
email: { message: 'Email is required', path: 'email' },
|
|
password: { message: 'Password is required', path: 'password' },
|
|
},
|
|
} as ValidationError;
|
|
|
|
ErrorController(validationError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(400);
|
|
expect(mockRes.send).toHaveBeenCalledWith({
|
|
messages: '"Email is required Password is required"',
|
|
fields: '["email","password"]',
|
|
});
|
|
expect(logger.error).toHaveBeenCalledWith('Validation error:', validationError.errors);
|
|
});
|
|
|
|
it('should handle ValidationError with empty errors object', () => {
|
|
const validationError = {
|
|
name: 'ValidationError',
|
|
errors: {},
|
|
} as ValidationError;
|
|
|
|
ErrorController(validationError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(400);
|
|
expect(mockRes.send).toHaveBeenCalledWith({
|
|
messages: '[]',
|
|
fields: '[]',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Duplicate key error handling', () => {
|
|
it('should handle duplicate key error (code 11000)', () => {
|
|
const duplicateKeyError = {
|
|
name: 'MongoServerError',
|
|
message: 'Duplicate key error',
|
|
code: 11000,
|
|
keyValue: { email: 'test@example.com' },
|
|
errmsg:
|
|
'E11000 duplicate key error collection: test.users index: email_1 dup key: { email: "test@example.com" }',
|
|
} as MongoServerError;
|
|
|
|
ErrorController(duplicateKeyError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(409);
|
|
expect(mockRes.send).toHaveBeenCalledWith({
|
|
messages: 'An document with that ["email"] already exists.',
|
|
fields: '["email"]',
|
|
});
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
'Duplicate key error: E11000 duplicate key error collection: test.users index: email_1 dup key: { email: "test@example.com" }',
|
|
);
|
|
});
|
|
|
|
it('should handle duplicate key error with multiple fields', () => {
|
|
const duplicateKeyError = {
|
|
name: 'MongoServerError',
|
|
message: 'Duplicate key error',
|
|
code: 11000,
|
|
keyValue: { email: 'test@example.com', username: 'testuser' },
|
|
errmsg:
|
|
'E11000 duplicate key error collection: test.users index: email_1 dup key: { email: "test@example.com" }',
|
|
} as MongoServerError;
|
|
|
|
ErrorController(duplicateKeyError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(409);
|
|
expect(mockRes.send).toHaveBeenCalledWith({
|
|
messages: 'An document with that ["email","username"] already exists.',
|
|
fields: '["email","username"]',
|
|
});
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
'Duplicate key error: E11000 duplicate key error collection: test.users index: email_1 dup key: { email: "test@example.com" }',
|
|
);
|
|
});
|
|
|
|
it('should handle error with code 11000 as string', () => {
|
|
const duplicateKeyError = {
|
|
name: 'MongoServerError',
|
|
message: 'Duplicate key error',
|
|
code: 11000,
|
|
keyValue: { email: 'test@example.com' },
|
|
errmsg:
|
|
'E11000 duplicate key error collection: test.users index: email_1 dup key: { email: "test@example.com" }',
|
|
} as MongoServerError;
|
|
|
|
ErrorController(duplicateKeyError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(409);
|
|
expect(mockRes.send).toHaveBeenCalledWith({
|
|
messages: 'An document with that ["email"] already exists.',
|
|
fields: '["email"]',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('SyntaxError handling', () => {
|
|
it('should handle errors with statusCode and body', () => {
|
|
const syntaxError = {
|
|
statusCode: 400,
|
|
body: 'Invalid JSON syntax',
|
|
} as CustomError;
|
|
|
|
ErrorController(syntaxError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(400);
|
|
expect(mockRes.send).toHaveBeenCalledWith('Invalid JSON syntax');
|
|
});
|
|
|
|
it('should handle errors with different statusCode and body', () => {
|
|
const customError = {
|
|
statusCode: 422,
|
|
body: { error: 'Unprocessable entity' },
|
|
} as CustomError;
|
|
|
|
ErrorController(customError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(422);
|
|
expect(mockRes.send).toHaveBeenCalledWith({ error: 'Unprocessable entity' });
|
|
});
|
|
|
|
it('should handle error with statusCode but no body', () => {
|
|
const partialError = {
|
|
statusCode: 400,
|
|
} as CustomError;
|
|
|
|
ErrorController(partialError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(500);
|
|
expect(mockRes.send).toHaveBeenCalledWith('An unknown error occurred.');
|
|
});
|
|
|
|
it('should handle error with body but no statusCode', () => {
|
|
const partialError = {
|
|
body: 'Some error message',
|
|
} as CustomError;
|
|
|
|
ErrorController(partialError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(500);
|
|
expect(mockRes.send).toHaveBeenCalledWith('An unknown error occurred.');
|
|
});
|
|
});
|
|
|
|
describe('Unknown error handling', () => {
|
|
it('should handle unknown errors', () => {
|
|
const unknownError = new Error('Some unknown error');
|
|
|
|
ErrorController(unknownError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(500);
|
|
expect(mockRes.send).toHaveBeenCalledWith('An unknown error occurred.');
|
|
expect(logger.error).toHaveBeenCalledWith('ErrorController => error', unknownError);
|
|
});
|
|
|
|
it('should handle errors with code other than 11000', () => {
|
|
const mongoError = {
|
|
code: 11100,
|
|
message: 'Some MongoDB error',
|
|
} as MongoServerError;
|
|
|
|
ErrorController(mongoError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(500);
|
|
expect(mockRes.send).toHaveBeenCalledWith('An unknown error occurred.');
|
|
expect(logger.error).toHaveBeenCalledWith('ErrorController => error', mongoError);
|
|
});
|
|
|
|
it('should handle generic errors', () => {
|
|
const genericError = new Error('Test error');
|
|
|
|
ErrorController(genericError, mockReq, mockRes, mockNext);
|
|
|
|
expect(mockRes.status).toHaveBeenCalledWith(500);
|
|
expect(mockRes.send).toHaveBeenCalledWith('An unknown error occurred.');
|
|
expect(logger.error).toHaveBeenCalledWith('ErrorController => error', genericError);
|
|
});
|
|
});
|
|
|
|
describe('Catch block handling', () => {
|
|
beforeEach(() => {
|
|
// Restore logger mock to normal behavior for these tests
|
|
(logger.error as jest.Mock).mockRestore();
|
|
(logger.error as jest.Mock) = jest.fn();
|
|
});
|
|
|
|
it('should handle errors when logger.error throws', () => {
|
|
// Create fresh mocks for this test
|
|
const freshMockRes = {
|
|
status: jest.fn().mockReturnThis(),
|
|
send: jest.fn(),
|
|
} as unknown as Response;
|
|
|
|
// Mock logger to throw on the first call, succeed on the second
|
|
(logger.error as jest.Mock)
|
|
.mockImplementationOnce(() => {
|
|
throw new Error('Logger error');
|
|
})
|
|
.mockImplementation(() => {});
|
|
|
|
const testError = new Error('Test error');
|
|
|
|
ErrorController(testError, mockReq, freshMockRes, mockNext);
|
|
|
|
expect(freshMockRes.status).toHaveBeenCalledWith(500);
|
|
expect(freshMockRes.send).toHaveBeenCalledWith('Processing error in ErrorController.');
|
|
expect(logger.error).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
});
|