deepsource-mcp-server
Version:
Model Context Protocol server for DeepSource
317 lines (258 loc) • 10.6 kB
text/typescript
/**
* @jest-environment node
*/
import { ErrorCategory } from '../../../utils/errors/categories.js';
import {
createClassifiedError,
createAuthError,
createNetworkError,
createServerError,
createClientError,
createTimeoutError,
createRateLimitError,
createSchemaError,
createNotFoundError,
createFormatError,
} from '../../../utils/errors/factory.js';
import { ClassifiedError } from '../../../utils/errors/types.js';
describe('Error Factory', () => {
// Test helper function to verify common properties of classified errors
const verifyErrorProperties = (
error: ClassifiedError,
expectedMessage: string,
expectedCategory: ErrorCategory,
expectedOriginalError?: unknown,
expectedMetadata?: Record<string, unknown>
) => {
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe(expectedMessage);
expect(error.category).toBe(expectedCategory);
if (expectedOriginalError) {
expect(error.originalError).toBe(expectedOriginalError);
} else {
expect(error.originalError).toBeUndefined();
}
if (expectedMetadata) {
expect(error.metadata).toEqual(expectedMetadata);
} else {
expect(error.metadata).toBeUndefined();
}
// Verify stack trace exists
expect(error.stack).toBeDefined();
};
describe('createClassifiedError', () => {
it('should create a basic classified error with all properties', () => {
const message = 'Test error message';
const category = ErrorCategory.NETWORK;
const originalError = new Error('Original error');
const metadata = { key: 'value', details: { nested: true } };
const error = createClassifiedError(message, category, originalError, metadata);
verifyErrorProperties(error, message, category, originalError, metadata);
});
it('should create a classified error without optional properties', () => {
const message = 'Simple error';
const category = ErrorCategory.OTHER;
const error = createClassifiedError(message, category);
verifyErrorProperties(error, message, category);
});
it('should preserve original error instance type', () => {
class CustomError extends Error {
code: string;
constructor(message: string, code: string) {
super(message);
this.code = code;
}
}
const originalError = new CustomError('Custom error', 'E001');
const error = createClassifiedError('Wrapped error', ErrorCategory.CLIENT, originalError);
expect(error.originalError).toBeInstanceOf(CustomError);
expect((error.originalError as CustomError).code).toBe('E001');
});
});
describe('Specialized error factories', () => {
const originalError = new Error('Original error');
const metadata = { requestId: '123', endpoint: '/api/data' };
describe('createAuthError', () => {
it('should create an error with AUTH category and custom message', () => {
const message = 'Invalid API key';
const error = createAuthError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.AUTH, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createAuthError('', originalError, metadata);
verifyErrorProperties(
error,
'Authentication failed',
ErrorCategory.AUTH,
originalError,
metadata
);
});
});
describe('createNetworkError', () => {
it('should create an error with NETWORK category and custom message', () => {
const message = 'Connection refused';
const error = createNetworkError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.NETWORK, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createNetworkError('', originalError, metadata);
verifyErrorProperties(
error,
'Network error occurred',
ErrorCategory.NETWORK,
originalError,
metadata
);
});
});
describe('createServerError', () => {
it('should create an error with SERVER category and custom message', () => {
const message = 'Internal server error (500)';
const error = createServerError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.SERVER, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createServerError('', originalError, metadata);
verifyErrorProperties(
error,
'Server error occurred',
ErrorCategory.SERVER,
originalError,
metadata
);
});
});
describe('createClientError', () => {
it('should create an error with CLIENT category and custom message', () => {
const message = 'Invalid request parameters';
const error = createClientError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.CLIENT, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createClientError('', originalError, metadata);
verifyErrorProperties(
error,
'Client error occurred',
ErrorCategory.CLIENT,
originalError,
metadata
);
});
});
describe('createTimeoutError', () => {
it('should create an error with TIMEOUT category and custom message', () => {
const message = 'Operation timed out after 30s';
const error = createTimeoutError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.TIMEOUT, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createTimeoutError('', originalError, metadata);
verifyErrorProperties(
error,
'Request timed out',
ErrorCategory.TIMEOUT,
originalError,
metadata
);
});
});
describe('createRateLimitError', () => {
it('should create an error with RATE_LIMIT category and custom message', () => {
const message = 'Too many requests (429)';
const error = createRateLimitError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.RATE_LIMIT, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createRateLimitError('', originalError, metadata);
verifyErrorProperties(
error,
'Rate limit exceeded',
ErrorCategory.RATE_LIMIT,
originalError,
metadata
);
});
});
describe('createSchemaError', () => {
it('should create an error with SCHEMA category and custom message', () => {
const message = 'Invalid field in schema';
const error = createSchemaError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.SCHEMA, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createSchemaError('', originalError, metadata);
verifyErrorProperties(
error,
'GraphQL schema error',
ErrorCategory.SCHEMA,
originalError,
metadata
);
});
});
describe('createNotFoundError', () => {
it('should create an error with NOT_FOUND category and custom message', () => {
const message = 'Project with ID 123 not found';
const error = createNotFoundError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.NOT_FOUND, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createNotFoundError('', originalError, metadata);
verifyErrorProperties(
error,
'Resource not found',
ErrorCategory.NOT_FOUND,
originalError,
metadata
);
});
});
describe('createFormatError', () => {
it('should create an error with FORMAT category and custom message', () => {
const message = 'Invalid JSON format';
const error = createFormatError(message, originalError, metadata);
verifyErrorProperties(error, message, ErrorCategory.FORMAT, originalError, metadata);
});
it('should use default message when no message is provided', () => {
const error = createFormatError('', originalError, metadata);
verifyErrorProperties(
error,
'Data format error',
ErrorCategory.FORMAT,
originalError,
metadata
);
});
});
});
describe('Error integration tests', () => {
it('should correctly chain original errors for debugging purposes', () => {
const level1Error = new Error('Low-level error');
const level2Error = createNetworkError('Mid-level error', level1Error);
const level3Error = createClientError('High-level error', level2Error, {
operation: 'getUserData',
});
expect(level3Error.message).toBe('High-level error');
expect(level3Error.category).toBe(ErrorCategory.CLIENT);
expect(level3Error.metadata).toEqual({ operation: 'getUserData' });
const originalError = level3Error.originalError as ClassifiedError;
expect(originalError.message).toBe('Mid-level error');
expect(originalError.category).toBe(ErrorCategory.NETWORK);
const deepestError = originalError.originalError as Error;
expect(deepestError.message).toBe('Low-level error');
});
it('should handle various metadata types', () => {
const numericMeta = { statusCode: 404, retryCount: 3 };
const complexMeta = {
request: { url: '/api/projects', method: 'GET' },
response: { status: 404, body: null },
timing: { started: '2023-01-01T00:00:00Z', duration: 1500 },
};
const error1 = createNotFoundError('Resource not found', undefined, numericMeta);
const error2 = createServerError('Server error', undefined, complexMeta);
expect(error1.metadata).toEqual(numericMeta);
expect(error2.metadata).toEqual(complexMeta);
});
});
});