Spaces:
Sleeping
Sleeping
File size: 5,690 Bytes
da2e594 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | 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);
}
});
}); |