error-handler.test.tsβ’5.69 kB
import { describe, it, expect, vi } from 'vitest';
import {
MCPError,
N8NConnectionError,
AuthenticationError,
ValidationError,
ToolNotFoundError,
ResourceNotFoundError,
handleError,
withErrorHandling,
} from '../src/utils/error-handler';
import { logger } from '../src/utils/logger';
// Mock the logger
vi.mock('../src/utils/logger', () => ({
logger: {
error: vi.fn(),
},
}));
describe('Error Classes', () => {
describe('MCPError', () => {
it('should create error with all properties', () => {
const error = new MCPError('Test error', 'TEST_CODE', 400, { field: 'value' });
expect(error.message).toBe('Test error');
expect(error.code).toBe('TEST_CODE');
expect(error.statusCode).toBe(400);
expect(error.data).toEqual({ field: 'value' });
expect(error.name).toBe('MCPError');
});
});
describe('N8NConnectionError', () => {
it('should create connection error with correct code', () => {
const error = new N8NConnectionError('Connection failed');
expect(error.message).toBe('Connection failed');
expect(error.code).toBe('N8N_CONNECTION_ERROR');
expect(error.statusCode).toBe(503);
expect(error.name).toBe('N8NConnectionError');
});
});
describe('AuthenticationError', () => {
it('should create auth error with default message', () => {
const error = new AuthenticationError();
expect(error.message).toBe('Authentication failed');
expect(error.code).toBe('AUTH_ERROR');
expect(error.statusCode).toBe(401);
});
it('should accept custom message', () => {
const error = new AuthenticationError('Invalid token');
expect(error.message).toBe('Invalid token');
});
});
describe('ValidationError', () => {
it('should create validation error', () => {
const error = new ValidationError('Invalid input', { field: 'email' });
expect(error.message).toBe('Invalid input');
expect(error.code).toBe('VALIDATION_ERROR');
expect(error.statusCode).toBe(400);
expect(error.data).toEqual({ field: 'email' });
});
});
describe('ToolNotFoundError', () => {
it('should create tool not found error', () => {
const error = new ToolNotFoundError('myTool');
expect(error.message).toBe("Tool 'myTool' not found");
expect(error.code).toBe('TOOL_NOT_FOUND');
expect(error.statusCode).toBe(404);
});
});
describe('ResourceNotFoundError', () => {
it('should create resource not found error', () => {
const error = new ResourceNotFoundError('workflow://123');
expect(error.message).toBe("Resource 'workflow://123' not found");
expect(error.code).toBe('RESOURCE_NOT_FOUND');
expect(error.statusCode).toBe(404);
});
});
});
describe('handleError', () => {
it('should return MCPError instances as-is', () => {
const mcpError = new ValidationError('Test');
const result = handleError(mcpError);
expect(result).toBe(mcpError);
});
it('should handle HTTP 401 errors', () => {
const httpError = {
response: { status: 401, data: { message: 'Unauthorized' } },
};
const result = handleError(httpError);
expect(result).toBeInstanceOf(AuthenticationError);
expect(result.message).toBe('Unauthorized');
});
it('should handle HTTP 404 errors', () => {
const httpError = {
response: { status: 404, data: { message: 'Not found' } },
};
const result = handleError(httpError);
expect(result.code).toBe('NOT_FOUND');
expect(result.statusCode).toBe(404);
});
it('should handle HTTP 5xx errors', () => {
const httpError = {
response: { status: 503, data: { message: 'Service unavailable' } },
};
const result = handleError(httpError);
expect(result).toBeInstanceOf(N8NConnectionError);
});
it('should handle connection refused errors', () => {
const connError = { code: 'ECONNREFUSED' };
const result = handleError(connError);
expect(result).toBeInstanceOf(N8NConnectionError);
expect(result.message).toBe('Cannot connect to n8n API');
});
it('should handle generic errors', () => {
const error = new Error('Something went wrong');
const result = handleError(error);
expect(result.message).toBe('Something went wrong');
expect(result.code).toBe('UNKNOWN_ERROR');
expect(result.statusCode).toBe(500);
});
it('should handle errors without message', () => {
const error = {};
const result = handleError(error);
expect(result.message).toBe('An unexpected error occurred');
});
});
describe('withErrorHandling', () => {
it('should execute operation successfully', async () => {
const operation = vi.fn().mockResolvedValue('success');
const result = await withErrorHandling(operation, 'test operation');
expect(result).toBe('success');
expect(logger.error).not.toHaveBeenCalled();
});
it('should handle and log errors', async () => {
const error = new Error('Operation failed');
const operation = vi.fn().mockRejectedValue(error);
await expect(withErrorHandling(operation, 'test operation')).rejects.toThrow();
expect(logger.error).toHaveBeenCalledWith('Error in test operation:', error);
});
it('should transform errors using handleError', async () => {
const error = { code: 'ECONNREFUSED' };
const operation = vi.fn().mockRejectedValue(error);
try {
await withErrorHandling(operation, 'test operation');
} catch (err) {
expect(err).toBeInstanceOf(N8NConnectionError);
}
});
});