@vfarcic/dot-ai 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/{src/cli.ts → dist/cli.js} +19 -26
  4. package/dist/core/claude.d.ts +42 -0
  5. package/dist/core/claude.d.ts.map +1 -0
  6. package/dist/core/claude.js +229 -0
  7. package/dist/core/deploy-operation.d.ts +38 -0
  8. package/dist/core/deploy-operation.d.ts.map +1 -0
  9. package/dist/core/deploy-operation.js +101 -0
  10. package/dist/core/discovery.d.ts +162 -0
  11. package/dist/core/discovery.d.ts.map +1 -0
  12. package/dist/core/discovery.js +758 -0
  13. package/dist/core/error-handling.d.ts +167 -0
  14. package/dist/core/error-handling.d.ts.map +1 -0
  15. package/dist/core/error-handling.js +399 -0
  16. package/dist/core/index.d.ts +42 -0
  17. package/dist/core/index.d.ts.map +1 -0
  18. package/dist/core/index.js +123 -0
  19. package/dist/core/kubernetes-utils.d.ts +38 -0
  20. package/dist/core/kubernetes-utils.d.ts.map +1 -0
  21. package/dist/core/kubernetes-utils.js +177 -0
  22. package/dist/core/memory.d.ts +45 -0
  23. package/dist/core/memory.d.ts.map +1 -0
  24. package/dist/core/memory.js +113 -0
  25. package/dist/core/schema.d.ts +187 -0
  26. package/dist/core/schema.d.ts.map +1 -0
  27. package/dist/core/schema.js +655 -0
  28. package/dist/core/session-utils.d.ts +29 -0
  29. package/dist/core/session-utils.d.ts.map +1 -0
  30. package/dist/core/session-utils.js +121 -0
  31. package/dist/core/workflow.d.ts +70 -0
  32. package/dist/core/workflow.d.ts.map +1 -0
  33. package/dist/core/workflow.js +161 -0
  34. package/dist/index.d.ts +15 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +32 -0
  37. package/dist/interfaces/cli.d.ts +74 -0
  38. package/dist/interfaces/cli.d.ts.map +1 -0
  39. package/dist/interfaces/cli.js +769 -0
  40. package/dist/interfaces/mcp.d.ts +30 -0
  41. package/dist/interfaces/mcp.d.ts.map +1 -0
  42. package/dist/interfaces/mcp.js +105 -0
  43. package/dist/mcp/server.d.ts +9 -0
  44. package/dist/mcp/server.d.ts.map +1 -0
  45. package/dist/mcp/server.js +151 -0
  46. package/dist/tools/answer-question.d.ts +27 -0
  47. package/dist/tools/answer-question.d.ts.map +1 -0
  48. package/dist/tools/answer-question.js +696 -0
  49. package/dist/tools/choose-solution.d.ts +23 -0
  50. package/dist/tools/choose-solution.d.ts.map +1 -0
  51. package/dist/tools/choose-solution.js +171 -0
  52. package/dist/tools/deploy-manifests.d.ts +25 -0
  53. package/dist/tools/deploy-manifests.d.ts.map +1 -0
  54. package/dist/tools/deploy-manifests.js +74 -0
  55. package/dist/tools/generate-manifests.d.ts +23 -0
  56. package/dist/tools/generate-manifests.d.ts.map +1 -0
  57. package/dist/tools/generate-manifests.js +424 -0
  58. package/dist/tools/index.d.ts +11 -0
  59. package/dist/tools/index.d.ts.map +1 -0
  60. package/dist/tools/index.js +34 -0
  61. package/dist/tools/recommend.d.ts +23 -0
  62. package/dist/tools/recommend.d.ts.map +1 -0
  63. package/dist/tools/recommend.js +332 -0
  64. package/package.json +124 -2
  65. package/.claude/commands/context-load.md +0 -11
  66. package/.claude/commands/context-save.md +0 -16
  67. package/.claude/commands/prd-done.md +0 -115
  68. package/.claude/commands/prd-get.md +0 -25
  69. package/.claude/commands/prd-start.md +0 -87
  70. package/.claude/commands/task-done.md +0 -77
  71. package/.claude/commands/tests-reminder.md +0 -32
  72. package/.claude/settings.local.json +0 -20
  73. package/.eslintrc.json +0 -25
  74. package/.github/workflows/ci.yml +0 -170
  75. package/.prettierrc.json +0 -10
  76. package/.teller.yml +0 -8
  77. package/CLAUDE.md +0 -162
  78. package/assets/images/logo.png +0 -0
  79. package/bin/dot-ai.ts +0 -47
  80. package/bin.js +0 -19
  81. package/destroy.sh +0 -45
  82. package/devbox.json +0 -13
  83. package/devbox.lock +0 -225
  84. package/docs/API.md +0 -449
  85. package/docs/CONTEXT.md +0 -49
  86. package/docs/DEVELOPMENT.md +0 -203
  87. package/docs/NEXT_STEPS.md +0 -97
  88. package/docs/STAGE_BASED_API.md +0 -97
  89. package/docs/cli-guide.md +0 -798
  90. package/docs/design.md +0 -750
  91. package/docs/discovery-engine.md +0 -515
  92. package/docs/error-handling.md +0 -429
  93. package/docs/function-registration.md +0 -157
  94. package/docs/mcp-guide.md +0 -416
  95. package/renovate.json +0 -51
  96. package/setup.sh +0 -111
  97. package/src/core/claude.ts +0 -280
  98. package/src/core/deploy-operation.ts +0 -127
  99. package/src/core/discovery.ts +0 -900
  100. package/src/core/error-handling.ts +0 -562
  101. package/src/core/index.ts +0 -143
  102. package/src/core/kubernetes-utils.ts +0 -218
  103. package/src/core/memory.ts +0 -148
  104. package/src/core/schema.ts +0 -830
  105. package/src/core/session-utils.ts +0 -97
  106. package/src/core/workflow.ts +0 -234
  107. package/src/index.ts +0 -18
  108. package/src/interfaces/cli.ts +0 -872
  109. package/src/interfaces/mcp.ts +0 -183
  110. package/src/mcp/server.ts +0 -131
  111. package/src/tools/answer-question.ts +0 -807
  112. package/src/tools/choose-solution.ts +0 -169
  113. package/src/tools/deploy-manifests.ts +0 -94
  114. package/src/tools/generate-manifests.ts +0 -502
  115. package/src/tools/index.ts +0 -41
  116. package/src/tools/recommend.ts +0 -370
  117. package/tests/__mocks__/@kubernetes/client-node.ts +0 -106
  118. package/tests/build-system.test.ts +0 -345
  119. package/tests/configuration.test.ts +0 -226
  120. package/tests/core/deploy-operation.test.ts +0 -38
  121. package/tests/core/discovery.test.ts +0 -1648
  122. package/tests/core/error-handling.test.ts +0 -632
  123. package/tests/core/schema.test.ts +0 -1658
  124. package/tests/core/session-utils.test.ts +0 -245
  125. package/tests/core.test.ts +0 -439
  126. package/tests/fixtures/configmap-no-labels.yaml +0 -8
  127. package/tests/fixtures/crossplane-app-configuration.yaml +0 -6
  128. package/tests/fixtures/crossplane-providers.yaml +0 -45
  129. package/tests/fixtures/crossplane-rbac.yaml +0 -48
  130. package/tests/fixtures/invalid-configmap.yaml +0 -8
  131. package/tests/fixtures/invalid-deployment.yaml +0 -17
  132. package/tests/fixtures/test-deployment.yaml +0 -28
  133. package/tests/fixtures/valid-configmap.yaml +0 -15
  134. package/tests/infrastructure.test.ts +0 -426
  135. package/tests/interfaces/cli.test.ts +0 -1036
  136. package/tests/interfaces/mcp.test.ts +0 -139
  137. package/tests/kubernetes-utils.test.ts +0 -200
  138. package/tests/mcp/server.test.ts +0 -126
  139. package/tests/setup.ts +0 -31
  140. package/tests/tools/answer-question.test.ts +0 -367
  141. package/tests/tools/choose-solution.test.ts +0 -481
  142. package/tests/tools/deploy-manifests.test.ts +0 -185
  143. package/tests/tools/generate-manifests.test.ts +0 -441
  144. package/tests/tools/index.test.ts +0 -111
  145. package/tests/tools/recommend.test.ts +0 -180
  146. package/tsconfig.json +0 -34
@@ -1,632 +0,0 @@
1
- import {
2
- ErrorHandler,
3
- ErrorCategory,
4
- ErrorSeverity,
5
- ConsoleLogger,
6
- LogLevel,
7
- AppError,
8
- ErrorContext
9
- } from '../../src/core/error-handling';
10
- import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
11
-
12
- describe('Error Handling System', () => {
13
- describe('ErrorHandler', () => {
14
- beforeEach(() => {
15
- // Reset logger to default for each test
16
- ErrorHandler.setLogger(new ConsoleLogger('Test', LogLevel.DEBUG));
17
- });
18
-
19
- describe('Error Creation', () => {
20
- test('should create comprehensive AppError', () => {
21
- const context: Partial<ErrorContext> = {
22
- operation: 'test_operation',
23
- component: 'TestComponent',
24
- userId: 'user123',
25
- sessionId: 'session456',
26
- input: { test: 'data' }
27
- };
28
-
29
- const error = ErrorHandler.createError(
30
- ErrorCategory.VALIDATION,
31
- ErrorSeverity.MEDIUM,
32
- 'Test error message',
33
- context
34
- );
35
-
36
- expect(error).toMatchObject({
37
- category: ErrorCategory.VALIDATION,
38
- severity: ErrorSeverity.MEDIUM,
39
- message: 'Test error message',
40
- isRetryable: false
41
- });
42
-
43
- expect(error.id).toMatch(/^err_\d+_[a-z0-9]+$/);
44
- expect(error.code).toMatch(/^VALIDATION_M_\d+_[a-z0-9]+$/);
45
- expect(error.timestamp).toBeInstanceOf(Date);
46
- expect(error.context.operation).toBe('test_operation');
47
- expect(error.context.component).toBe('TestComponent');
48
- expect(error.context.userId).toBe('user123');
49
- expect(error.suggestedActions).toBeInstanceOf(Array);
50
- expect(error.suggestedActions.length).toBeGreaterThan(0);
51
- });
52
-
53
- test('should wrap original error with context', () => {
54
- const originalError = new Error('Original error message');
55
- const context: Partial<ErrorContext> = {
56
- operation: 'test_wrap',
57
- component: 'TestWrapper'
58
- };
59
-
60
- const appError = ErrorHandler.createError(
61
- ErrorCategory.INTERNAL,
62
- ErrorSeverity.HIGH,
63
- 'Wrapped error',
64
- context,
65
- originalError
66
- );
67
-
68
- expect(appError.context.originalError).toBe(originalError);
69
- expect(appError.context.stackTrace).toBe(originalError.stack);
70
- expect(appError.technicalDetails).toBe('Original error message');
71
- // The cause field is removed to prevent circular references
72
- expect(appError.cause).toBeUndefined();
73
- });
74
-
75
- test('should generate unique error IDs and codes', () => {
76
- const error1 = ErrorHandler.createError(
77
- ErrorCategory.VALIDATION,
78
- ErrorSeverity.LOW,
79
- 'Error 1',
80
- { operation: 'test1', component: 'Test' }
81
- );
82
-
83
- const error2 = ErrorHandler.createError(
84
- ErrorCategory.VALIDATION,
85
- ErrorSeverity.LOW,
86
- 'Error 2',
87
- { operation: 'test2', component: 'Test' }
88
- );
89
-
90
- expect(error1.id).not.toBe(error2.id);
91
- expect(error1.code).not.toBe(error2.code);
92
- });
93
- });
94
-
95
- describe('MCP Error Conversion', () => {
96
- test('should convert AppError to McpError with correct error codes', () => {
97
- const validationError = ErrorHandler.createError(
98
- ErrorCategory.VALIDATION,
99
- ErrorSeverity.MEDIUM,
100
- 'Validation failed',
101
- { operation: 'test', component: 'Test' }
102
- );
103
-
104
- const mcpError = ErrorHandler.toMcpError(validationError);
105
-
106
- expect(mcpError).toBeInstanceOf(McpError);
107
- expect(mcpError.code).toBe(ErrorCode.InvalidParams);
108
- expect(mcpError.message).toContain('Validation failed');
109
- });
110
-
111
- test('should map different error categories to appropriate MCP codes', () => {
112
- const testCases = [
113
- { category: ErrorCategory.VALIDATION, expected: ErrorCode.InvalidParams },
114
- { category: ErrorCategory.AUTHENTICATION, expected: ErrorCode.InvalidParams },
115
- { category: ErrorCategory.MCP_PROTOCOL, expected: ErrorCode.MethodNotFound },
116
- { category: ErrorCategory.OPERATION, expected: ErrorCode.InvalidRequest },
117
- { category: ErrorCategory.INTERNAL, expected: ErrorCode.InternalError },
118
- { category: ErrorCategory.UNKNOWN, expected: ErrorCode.InternalError }
119
- ];
120
-
121
- testCases.forEach(({ category, expected }) => {
122
- const appError = ErrorHandler.createError(
123
- category,
124
- ErrorSeverity.MEDIUM,
125
- 'Test error',
126
- { operation: 'test', component: 'Test' }
127
- );
128
-
129
- const mcpError = ErrorHandler.toMcpError(appError);
130
- expect(mcpError.code).toBe(expected);
131
- });
132
- });
133
- });
134
-
135
- describe('Error Handling with Context', () => {
136
- test('should handle native Error and enhance with context', () => {
137
- const nativeError = new Error('Native error message');
138
- const context: Partial<ErrorContext> = {
139
- operation: 'test_operation',
140
- component: 'TestComponent',
141
- requestId: 'req123'
142
- };
143
-
144
- const result = ErrorHandler.handleError(nativeError, context, {
145
- rethrow: false
146
- }) as AppError;
147
-
148
- expect(result.message).toBe('Native error message');
149
- expect(result.context.operation).toBe('test_operation');
150
- expect(result.context.component).toBe('TestComponent');
151
- expect(result.context.requestId).toBe('req123');
152
- expect(result.context.originalError).toBe(nativeError);
153
- });
154
-
155
- test('should pass through AppError unchanged', () => {
156
- const appError = ErrorHandler.createError(
157
- ErrorCategory.VALIDATION,
158
- ErrorSeverity.MEDIUM,
159
- 'Original app error',
160
- { operation: 'original', component: 'Original' }
161
- );
162
-
163
- const context: Partial<ErrorContext> = {
164
- operation: 'handler_operation',
165
- component: 'HandlerComponent'
166
- };
167
-
168
- const result = ErrorHandler.handleError(appError, context, {
169
- rethrow: false
170
- }) as AppError;
171
-
172
- expect(result).toBe(appError);
173
- });
174
-
175
- test('should convert to MCP error when requested', () => {
176
- const nativeError = new Error('Test error');
177
- const context: Partial<ErrorContext> = {
178
- operation: 'test',
179
- component: 'Test'
180
- };
181
-
182
- const result = ErrorHandler.handleError(nativeError, context, {
183
- convertToMcp: true,
184
- rethrow: false
185
- });
186
-
187
- expect(result).toBeInstanceOf(McpError);
188
- });
189
-
190
- test('should rethrow error when requested', () => {
191
- const nativeError = new Error('Test error');
192
- const context: Partial<ErrorContext> = {
193
- operation: 'test',
194
- component: 'Test'
195
- };
196
-
197
- expect(() => {
198
- ErrorHandler.handleError(nativeError, context, {
199
- rethrow: true
200
- });
201
- }).toThrow();
202
- });
203
- });
204
-
205
- describe('Error Wrapping with Retry Logic', () => {
206
- test('should execute operation successfully', async () => {
207
- const mockOperation = jest.fn().mockResolvedValue('success');
208
- const context: Partial<ErrorContext> = {
209
- operation: 'test_success',
210
- component: 'Test'
211
- };
212
-
213
- const result = await ErrorHandler.withErrorHandling(
214
- mockOperation,
215
- context
216
- );
217
-
218
- expect(result).toBe('success');
219
- expect(mockOperation).toHaveBeenCalledTimes(1);
220
- });
221
-
222
- test('should retry operation on failure', async () => {
223
- const mockOperation = jest.fn()
224
- .mockRejectedValueOnce(new Error('First failure'))
225
- .mockResolvedValueOnce('success');
226
-
227
- const context: Partial<ErrorContext> = {
228
- operation: 'test_retry',
229
- component: 'Test'
230
- };
231
-
232
- const result = await ErrorHandler.withErrorHandling(
233
- mockOperation,
234
- context,
235
- { retryCount: 1 }
236
- );
237
-
238
- expect(result).toBe('success');
239
- expect(mockOperation).toHaveBeenCalledTimes(2);
240
- });
241
-
242
- test('should fail after max retries', async () => {
243
- const mockOperation = jest.fn()
244
- .mockRejectedValue(new Error('Persistent failure'));
245
-
246
- const context: Partial<ErrorContext> = {
247
- operation: 'test_failure',
248
- component: 'Test'
249
- };
250
-
251
- try {
252
- await ErrorHandler.withErrorHandling(
253
- mockOperation,
254
- context,
255
- { retryCount: 2 }
256
- );
257
- // If we get here, the function didn't throw
258
- fail('Expected withErrorHandling to throw but it resolved');
259
- } catch (error) {
260
- // This is expected behavior
261
- expect(error).toBeDefined();
262
- expect(mockOperation).toHaveBeenCalledTimes(3); // Original + 2 retries
263
- }
264
- });
265
-
266
- test('should convert to MCP error on final failure', async () => {
267
- const mockOperation = jest.fn()
268
- .mockRejectedValue(new Error('Test failure'));
269
-
270
- const context: Partial<ErrorContext> = {
271
- operation: 'test_mcp_failure',
272
- component: 'Test'
273
- };
274
-
275
- await expect(ErrorHandler.withErrorHandling(
276
- mockOperation,
277
- context,
278
- { convertToMcp: true }
279
- )).rejects.toBeInstanceOf(McpError);
280
- });
281
- });
282
-
283
- describe('Error Categorization', () => {
284
- test('should correctly categorize Kubernetes errors', () => {
285
- const kubeError = new Error('kubeconfig file not found');
286
- const context: Partial<ErrorContext> = {
287
- operation: 'test',
288
- component: 'Test'
289
- };
290
-
291
- const appError = ErrorHandler.handleError(kubeError, context, {
292
- rethrow: false
293
- }) as AppError;
294
-
295
- expect(appError.category).toBe(ErrorCategory.KUBERNETES);
296
- });
297
-
298
- test('should correctly categorize network errors', () => {
299
- const networkError = new Error('connection refused');
300
- const context: Partial<ErrorContext> = {
301
- operation: 'test',
302
- component: 'Test'
303
- };
304
-
305
- const appError = ErrorHandler.handleError(networkError, context, {
306
- rethrow: false
307
- }) as AppError;
308
-
309
- expect(appError.category).toBe(ErrorCategory.NETWORK);
310
- });
311
-
312
- test('should correctly categorize authentication errors', () => {
313
- const authError = new Error('unauthorized access');
314
- const context: Partial<ErrorContext> = {
315
- operation: 'test',
316
- component: 'Test'
317
- };
318
-
319
- const appError = ErrorHandler.handleError(authError, context, {
320
- rethrow: false
321
- }) as AppError;
322
-
323
- expect(appError.category).toBe(ErrorCategory.AUTHENTICATION);
324
- });
325
-
326
- test('should correctly categorize validation errors', () => {
327
- const validationError = new Error('invalid parameter format');
328
- const context: Partial<ErrorContext> = {
329
- operation: 'test',
330
- component: 'Test'
331
- };
332
-
333
- const appError = ErrorHandler.handleError(validationError, context, {
334
- rethrow: false
335
- }) as AppError;
336
-
337
- expect(appError.category).toBe(ErrorCategory.VALIDATION);
338
- });
339
-
340
- test('should correctly categorize AI service errors', () => {
341
- const aiError = new Error('anthropic api key invalid');
342
- const context: Partial<ErrorContext> = {
343
- operation: 'test',
344
- component: 'Test'
345
- };
346
-
347
- const appError = ErrorHandler.handleError(aiError, context, {
348
- rethrow: false
349
- }) as AppError;
350
-
351
- expect(appError.category).toBe(ErrorCategory.AI_SERVICE);
352
- });
353
- });
354
-
355
- describe('Error Severity Assessment', () => {
356
- test('should assign critical severity for critical errors', () => {
357
- const criticalError = new Error('critical system failure');
358
- const context: Partial<ErrorContext> = {
359
- operation: 'test',
360
- component: 'Test'
361
- };
362
-
363
- const appError = ErrorHandler.handleError(criticalError, context, {
364
- rethrow: false
365
- }) as AppError;
366
-
367
- expect(appError.severity).toBe(ErrorSeverity.CRITICAL);
368
- });
369
-
370
- test('should assign high severity for authentication errors', () => {
371
- const authError = new Error('authentication failed');
372
- const context: Partial<ErrorContext> = {
373
- operation: 'test',
374
- component: 'Test'
375
- };
376
-
377
- const appError = ErrorHandler.handleError(authError, context, {
378
- rethrow: false
379
- }) as AppError;
380
-
381
- expect(appError.severity).toBe(ErrorSeverity.HIGH);
382
- });
383
-
384
- test('should assign medium severity for validation errors', () => {
385
- const validationError = new Error('validation error');
386
- const context: Partial<ErrorContext> = {
387
- operation: 'test',
388
- component: 'Test'
389
- };
390
-
391
- const appError = ErrorHandler.handleError(validationError, context, {
392
- rethrow: false
393
- }) as AppError;
394
-
395
- expect(appError.severity).toBe(ErrorSeverity.MEDIUM);
396
- });
397
- });
398
-
399
- describe('Suggested Actions', () => {
400
- test('should provide Kubernetes-specific suggestions', () => {
401
- const appError = ErrorHandler.createError(
402
- ErrorCategory.KUBERNETES,
403
- ErrorSeverity.HIGH,
404
- 'Kubernetes error',
405
- { operation: 'test', component: 'Test' }
406
- );
407
-
408
- expect(appError.suggestedActions).toContain('Verify kubeconfig file exists and is valid');
409
- expect(appError.suggestedActions).toContain('Check cluster connectivity with kubectl cluster-info');
410
- });
411
-
412
- test('should provide validation-specific suggestions', () => {
413
- const appError = ErrorHandler.createError(
414
- ErrorCategory.VALIDATION,
415
- ErrorSeverity.MEDIUM,
416
- 'Validation error',
417
- { operation: 'test', component: 'Test' }
418
- );
419
-
420
- expect(appError.suggestedActions).toContain('Review input parameters for correct format');
421
- expect(appError.suggestedActions).toContain('Check required fields are provided');
422
- });
423
-
424
- test('should provide AI service-specific suggestions', () => {
425
- const appError = ErrorHandler.createError(
426
- ErrorCategory.AI_SERVICE,
427
- ErrorSeverity.HIGH,
428
- 'AI service error',
429
- { operation: 'test', component: 'Test' }
430
- );
431
-
432
- expect(appError.suggestedActions).toContain('Check ANTHROPIC_API_KEY environment variable');
433
- expect(appError.suggestedActions).toContain('Verify API key is valid and has sufficient credits');
434
- });
435
-
436
- test('should use custom suggested actions when provided', () => {
437
- const customActions = ['Custom action 1', 'Custom action 2'];
438
- const context: Partial<ErrorContext> = {
439
- operation: 'test',
440
- component: 'Test',
441
- suggestedActions: customActions
442
- };
443
-
444
- const appError = ErrorHandler.createError(
445
- ErrorCategory.INTERNAL,
446
- ErrorSeverity.LOW,
447
- 'Test error',
448
- context
449
- );
450
-
451
- expect(appError.suggestedActions).toEqual(customActions);
452
- });
453
- });
454
-
455
- describe('Request ID Generation', () => {
456
- test('should generate unique request IDs', () => {
457
- const id1 = ErrorHandler.generateRequestId();
458
- const id2 = ErrorHandler.generateRequestId();
459
-
460
- expect(id1).not.toBe(id2);
461
- expect(id1).toMatch(/^req_\d+_\d+$/);
462
- expect(id2).toMatch(/^req_\d+_\d+$/);
463
- });
464
- });
465
- });
466
-
467
- describe('ConsoleLogger', () => {
468
- let consoleSpy: {
469
- debug: jest.SpyInstance;
470
- info: jest.SpyInstance;
471
- warn: jest.SpyInstance;
472
- error: jest.SpyInstance;
473
- };
474
-
475
- beforeEach(() => {
476
- consoleSpy = {
477
- debug: jest.spyOn(console, 'debug').mockImplementation(),
478
- info: jest.spyOn(console, 'info').mockImplementation(),
479
- warn: jest.spyOn(console, 'warn').mockImplementation(),
480
- error: jest.spyOn(console, 'error').mockImplementation()
481
- };
482
- });
483
-
484
- afterEach(() => {
485
- Object.values(consoleSpy).forEach(spy => spy.mockRestore());
486
- });
487
-
488
- test('should log at appropriate levels', () => {
489
- const logger = new ConsoleLogger('TestComponent', LogLevel.DEBUG);
490
-
491
- logger.debug('Debug message');
492
- logger.info('Info message');
493
- logger.warn('Warn message');
494
- logger.error('Error message');
495
-
496
- expect(consoleSpy.debug).toHaveBeenCalledWith(
497
- expect.stringContaining('[TestComponent] Debug message')
498
- );
499
- expect(consoleSpy.info).toHaveBeenCalledWith(
500
- expect.stringContaining('[TestComponent] Info message')
501
- );
502
- expect(consoleSpy.warn).toHaveBeenCalledWith(
503
- expect.stringContaining('[TestComponent] Warn message')
504
- );
505
- expect(consoleSpy.error).toHaveBeenCalledWith(
506
- expect.stringContaining('[TestComponent] Error message')
507
- );
508
- });
509
-
510
- test('should respect minimum log level', () => {
511
- const logger = new ConsoleLogger('TestComponent', LogLevel.WARN);
512
-
513
- logger.debug('Debug message');
514
- logger.info('Info message');
515
- logger.warn('Warn message');
516
- logger.error('Error message');
517
-
518
- expect(consoleSpy.debug).not.toHaveBeenCalled();
519
- expect(consoleSpy.info).not.toHaveBeenCalled();
520
- expect(consoleSpy.warn).toHaveBeenCalled();
521
- expect(consoleSpy.error).toHaveBeenCalled();
522
- });
523
-
524
- test('should format messages with timestamps and component', () => {
525
- const logger = new ConsoleLogger('TestComponent', LogLevel.INFO);
526
-
527
- logger.info('Test message');
528
-
529
- expect(consoleSpy.info).toHaveBeenCalledWith(
530
- expect.stringMatching(/^\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\] INFO \[TestComponent\] Test message$/)
531
- );
532
- });
533
-
534
- test('should include data in log messages', () => {
535
- const logger = new ConsoleLogger('TestComponent', LogLevel.INFO);
536
- const testData = { key: 'value', number: 42 };
537
-
538
- logger.info('Test message', testData);
539
-
540
- expect(consoleSpy.info).toHaveBeenCalledWith(
541
- expect.stringContaining(JSON.stringify(testData, null, 2))
542
- );
543
- });
544
-
545
- test('should serialize errors properly', () => {
546
- const logger = new ConsoleLogger('TestComponent', LogLevel.ERROR);
547
- const testError = new Error('Test error');
548
- const appError = ErrorHandler.createError(
549
- ErrorCategory.VALIDATION,
550
- ErrorSeverity.MEDIUM,
551
- 'App error',
552
- { operation: 'test', component: 'Test' }
553
- );
554
-
555
- logger.error('Native error', testError);
556
- logger.error('App error', appError);
557
-
558
- expect(consoleSpy.error).toHaveBeenCalledWith(
559
- expect.stringContaining('"name": "Error"')
560
- );
561
- expect(consoleSpy.error).toHaveBeenCalledWith(
562
- expect.stringContaining('"category": "validation"')
563
- );
564
- });
565
- });
566
-
567
- describe('Error Context Tracking', () => {
568
- test('should preserve context through error chain', () => {
569
- const originalContext: Partial<ErrorContext> = {
570
- operation: 'original_operation',
571
- component: 'OriginalComponent',
572
- userId: 'user123',
573
- sessionId: 'session456',
574
- requestId: 'req789'
575
- };
576
-
577
- const originalError = ErrorHandler.createError(
578
- ErrorCategory.VALIDATION,
579
- ErrorSeverity.MEDIUM,
580
- 'Original error',
581
- originalContext
582
- );
583
-
584
- const wrappedContext: Partial<ErrorContext> = {
585
- operation: 'wrapped_operation',
586
- component: 'WrapperComponent'
587
- };
588
-
589
- const wrappedError = ErrorHandler.handleError(originalError, wrappedContext, {
590
- rethrow: false
591
- }) as AppError;
592
-
593
- // Original AppError should be passed through unchanged
594
- expect(wrappedError).toBe(originalError);
595
- expect(wrappedError.context.operation).toBe('original_operation');
596
- expect(wrappedError.context.userId).toBe('user123');
597
- });
598
-
599
- test('should include version and timestamp in context', () => {
600
- const appError = ErrorHandler.createError(
601
- ErrorCategory.INTERNAL,
602
- ErrorSeverity.LOW,
603
- 'Test error',
604
- { operation: 'test', component: 'Test' }
605
- );
606
-
607
- expect(appError.context.version).toBeDefined();
608
- expect(appError.context.timestamp).toBeInstanceOf(Date);
609
- expect(appError.timestamp).toBeInstanceOf(Date);
610
- });
611
-
612
- test('should handle retry count in context', () => {
613
- const context: Partial<ErrorContext> = {
614
- operation: 'test_retry',
615
- component: 'Test',
616
- retryCount: 2,
617
- isRetryable: true
618
- };
619
-
620
- const appError = ErrorHandler.createError(
621
- ErrorCategory.NETWORK,
622
- ErrorSeverity.MEDIUM,
623
- 'Network error',
624
- context
625
- );
626
-
627
- expect(appError.context.retryCount).toBe(2);
628
- expect(appError.context.isRetryable).toBe(true);
629
- expect(appError.isRetryable).toBe(true);
630
- });
631
- });
632
- });