diff --git a/src/utils/errorHandling.test.ts b/src/utils/errorHandling.test.ts new file mode 100644 index 0000000..e79e349 --- /dev/null +++ b/src/utils/errorHandling.test.ts @@ -0,0 +1,165 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { AppError, errorCodes, handleError, createAPIError } from './errorHandling'; +import * as analytics from './analytics'; + +// Mock the analytics module +vi.mock('./analytics', () => ({ + trackEvent: vi.fn() +})); + +describe('errorHandling', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('AppError', () => { + it('should create an AppError with all properties', () => { + const error = new AppError('Test error', 'TEST_CODE', 'error', { key: 'value' }); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(AppError); + expect(error.message).toBe('Test error'); + expect(error.code).toBe('TEST_CODE'); + expect(error.severity).toBe('error'); + expect(error.metadata).toEqual({ key: 'value' }); + expect(error.name).toBe('AppError'); + }); + + it('should create an AppError with default severity', () => { + const error = new AppError('Test error', 'TEST_CODE'); + + expect(error.severity).toBe('error'); + expect(error.metadata).toBeUndefined(); + }); + + it('should create an AppError with warning severity', () => { + const error = new AppError('Test warning', 'TEST_CODE', 'warning'); + + expect(error.severity).toBe('warning'); + }); + + it('should create an AppError with info severity', () => { + const error = new AppError('Test info', 'TEST_CODE', 'info'); + + expect(error.severity).toBe('info'); + }); + }); + + describe('errorCodes', () => { + it('should have all error codes defined', () => { + expect(errorCodes.NETWORK_ERROR).toBe('ERR_NETWORK'); + expect(errorCodes.API_ERROR).toBe('ERR_API'); + expect(errorCodes.AUTH_ERROR).toBe('ERR_AUTH'); + expect(errorCodes.VALIDATION_ERROR).toBe('ERR_VALIDATION'); + expect(errorCodes.UNKNOWN_ERROR).toBe('ERR_UNKNOWN'); + }); + }); + + describe('handleError', () => { + it('should handle AppError and return it unchanged', () => { + const originalError = new AppError('Test error', 'TEST_CODE', 'error', { foo: 'bar' }); + const result = handleError(originalError, 'test-context'); + + expect(result).toBe(originalError); + expect(result.message).toBe('Test error'); + expect(result.code).toBe('TEST_CODE'); + expect(result.severity).toBe('error'); + expect(result.metadata).toEqual({ foo: 'bar' }); + expect(analytics.trackEvent).toHaveBeenCalledWith( + 'Error', + 'TEST_CODE', + 'test-context: Test error' + ); + }); + + it('should convert regular Error to AppError', () => { + const originalError = new Error('Regular error'); + const result = handleError(originalError, 'test-context'); + + expect(result).toBeInstanceOf(AppError); + expect(result.message).toBe('Regular error'); + expect(result.code).toBe(errorCodes.UNKNOWN_ERROR); + expect(result.severity).toBe('error'); + expect(result.metadata).toEqual({ originalError }); + expect(analytics.trackEvent).toHaveBeenCalledWith( + 'Error', + errorCodes.UNKNOWN_ERROR, + 'test-context: Regular error' + ); + }); + + it('should handle unknown error types', () => { + const result = handleError('string error', 'test-context'); + + expect(result).toBeInstanceOf(AppError); + expect(result.message).toBe('An unexpected error occurred'); + expect(result.code).toBe(errorCodes.UNKNOWN_ERROR); + expect(result.severity).toBe('error'); + expect(result.metadata).toEqual({ originalError: 'string error' }); + expect(analytics.trackEvent).toHaveBeenCalledWith( + 'Error', + errorCodes.UNKNOWN_ERROR, + 'test-context: An unexpected error occurred' + ); + }); + + it('should handle null error', () => { + const result = handleError(null, 'test-context'); + + expect(result).toBeInstanceOf(AppError); + expect(result.message).toBe('An unexpected error occurred'); + expect(result.code).toBe(errorCodes.UNKNOWN_ERROR); + expect(result.metadata).toEqual({ originalError: null }); + }); + + it('should handle undefined error', () => { + const result = handleError(undefined, 'test-context'); + + expect(result).toBeInstanceOf(AppError); + expect(result.message).toBe('An unexpected error occurred'); + expect(result.code).toBe(errorCodes.UNKNOWN_ERROR); + expect(result.metadata).toEqual({ originalError: undefined }); + }); + + it('should track error in analytics', () => { + const error = new AppError('Analytics test', 'ANALYTICS_CODE'); + handleError(error, 'analytics-context'); + + expect(analytics.trackEvent).toHaveBeenCalledTimes(1); + expect(analytics.trackEvent).toHaveBeenCalledWith( + 'Error', + 'ANALYTICS_CODE', + 'analytics-context: Analytics test' + ); + }); + }); + + describe('createAPIError', () => { + it('should create an API error with status code', () => { + const error = createAPIError(404, 'Not found'); + + expect(error).toBeInstanceOf(AppError); + expect(error.message).toBe('Not found'); + expect(error.code).toBe(errorCodes.API_ERROR); + expect(error.severity).toBe('error'); + expect(error.metadata).toEqual({ status: 404 }); + }); + + it('should create an API error for 500 status', () => { + const error = createAPIError(500, 'Internal server error'); + + expect(error).toBeInstanceOf(AppError); + expect(error.message).toBe('Internal server error'); + expect(error.code).toBe(errorCodes.API_ERROR); + expect(error.metadata).toEqual({ status: 500 }); + }); + + it('should create an API error for 401 status', () => { + const error = createAPIError(401, 'Unauthorized'); + + expect(error).toBeInstanceOf(AppError); + expect(error.message).toBe('Unauthorized'); + expect(error.metadata).toEqual({ status: 401 }); + }); + }); +});