stt-error.test.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { describe, it, expect, vi } from 'vitest'
  2. import { SttError, createErrorHandler, ErrorRecovery } from '../../src/core/stt-error'
  3. describe('SttError', () => {
  4. describe('constructor', () => {
  5. it('should create error with code and message', () => {
  6. const error = new SttError('NOT_INITIALIZED', 'SDK not initialized')
  7. expect(error).toBeInstanceOf(SttError)
  8. expect(error.name).toBe('SttError')
  9. expect(error.code).toBe('NOT_INITIALIZED')
  10. expect(error.message).toBe('SDK not initialized')
  11. expect(error.timestamp).toBeGreaterThan(0)
  12. expect(error.details).toBeUndefined()
  13. })
  14. it('should create error with details', () => {
  15. const details = { userId: 'test-user', channel: 'test-channel' }
  16. const error = new SttError('INVALID_CONFIG', 'Invalid configuration', details)
  17. expect(error.details).toEqual(details)
  18. })
  19. })
  20. describe('fromError', () => {
  21. it('should return SttError instance if already SttError', () => {
  22. const originalError = new SttError('NETWORK_ERROR', 'Network failed')
  23. const result = SttError.fromError(originalError)
  24. expect(result).toBe(originalError)
  25. })
  26. it('should convert Error to SttError', () => {
  27. const originalError = new Error('Something went wrong')
  28. const result = SttError.fromError(originalError, 'UNKNOWN_ERROR')
  29. expect(result).toBeInstanceOf(SttError)
  30. expect(result.code).toBe('UNKNOWN_ERROR')
  31. expect(result.message).toBe('Something went wrong')
  32. expect(result.details?.originalError).toBe(originalError)
  33. })
  34. it('should handle non-Error objects', () => {
  35. const result = SttError.fromError('String error', 'NETWORK_ERROR')
  36. expect(result).toBeInstanceOf(SttError)
  37. expect(result.code).toBe('NETWORK_ERROR')
  38. expect(result.message).toBe('String error')
  39. })
  40. })
  41. describe('toJSON', () => {
  42. it('should serialize error to JSON', () => {
  43. const error = new SttError('NOT_INITIALIZED', 'SDK not initialized')
  44. const json = error.toJSON()
  45. expect(json).toEqual({
  46. name: 'SttError',
  47. code: 'NOT_INITIALIZED',
  48. message: 'SDK not initialized',
  49. timestamp: error.timestamp,
  50. details: undefined,
  51. stack: error.stack,
  52. })
  53. })
  54. })
  55. describe('toString', () => {
  56. it('should return formatted string', () => {
  57. const error = new SttError('NOT_INITIALIZED', 'SDK not initialized')
  58. const str = error.toString()
  59. expect(str).toBe('[NOT_INITIALIZED] SDK not initialized')
  60. })
  61. })
  62. })
  63. describe('createErrorHandler', () => {
  64. it('should create error handler without emitter', () => {
  65. const handler = createErrorHandler()
  66. const error = new Error('Test error')
  67. const result = handler(error)
  68. expect(result).toBeInstanceOf(SttError)
  69. })
  70. it('should create error handler with emitter', () => {
  71. const emitter = {
  72. emit: vi.fn(),
  73. }
  74. const handler = createErrorHandler(emitter)
  75. const error = new Error('Test error')
  76. const result = handler(error)
  77. expect(result).toBeInstanceOf(SttError)
  78. expect(emitter.emit).toHaveBeenCalledWith('error', result)
  79. })
  80. it('should include context in details', () => {
  81. const handler = createErrorHandler()
  82. const error = new Error('Test error')
  83. const result = handler(error, 'test-context')
  84. expect(result.details?.context).toBe('test-context')
  85. })
  86. })
  87. describe('ErrorRecovery', () => {
  88. describe('retry', () => {
  89. it('should succeed on first attempt', async () => {
  90. const recovery = new ErrorRecovery()
  91. const operation = vi.fn().mockResolvedValue('success')
  92. const result = await recovery.retry(operation)
  93. expect(result).toBe('success')
  94. expect(operation).toHaveBeenCalledTimes(1)
  95. })
  96. it('should retry and succeed', async () => {
  97. const recovery = new ErrorRecovery()
  98. const operation = vi
  99. .fn()
  100. .mockRejectedValueOnce(new Error('First attempt failed'))
  101. .mockResolvedValueOnce('success')
  102. const result = await recovery.retry(operation)
  103. expect(result).toBe('success')
  104. expect(operation).toHaveBeenCalledTimes(2)
  105. })
  106. it('should fail after max retries', async () => {
  107. const recovery = new ErrorRecovery(2, 10)
  108. const operation = vi.fn().mockRejectedValue(new Error('Always fails'))
  109. await expect(recovery.retry(operation)).rejects.toThrow('Always fails')
  110. expect(operation).toHaveBeenCalledTimes(2) // Initial + 1 retry (maxRetries=2 means 2 total attempts)
  111. })
  112. it('should respect shouldRetry function', async () => {
  113. const recovery = new ErrorRecovery()
  114. const operation = vi.fn().mockRejectedValue(new SttError('NETWORK_ERROR', 'Network failed'))
  115. const shouldRetry = vi.fn().mockReturnValue(false)
  116. await expect(recovery.retry(operation, shouldRetry)).rejects.toThrow(SttError)
  117. expect(operation).toHaveBeenCalledTimes(1)
  118. expect(shouldRetry).toHaveBeenCalledTimes(1)
  119. })
  120. })
  121. })