1
- import { describe , expect , it , mock } from 'bun:test'
1
+ import { afterAll , beforeAll , describe , expect , it , mock , spyOn } from 'bun:test'
2
2
import { italic } from '@stacksjs/cli'
3
+ import * as path from '@stacksjs/path'
3
4
import { ExitCode } from '@stacksjs/types'
5
+ import fs from 'fs-extra'
4
6
import { ErrorHandler , handleError } from '../src/handler'
5
7
import { rescue } from '../src/utils'
6
8
7
- // Tests for ErrorHandler class
8
9
describe ( '@stacksjs/error-handling' , ( ) => {
10
+ let originalConsoleError : typeof console . error
11
+ let originalProcessExit : typeof process . exit
12
+
13
+ beforeAll ( ( ) => {
14
+ originalConsoleError = console . error
15
+ console . error = ( ) => { } // No-op function
16
+
17
+ originalProcessExit = process . exit
18
+ process . exit = ( ) => {
19
+ throw new Error ( 'process.exit() was called' )
20
+ }
21
+ } )
22
+
23
+ afterAll ( ( ) => {
24
+ console . error = originalConsoleError
25
+ process . exit = originalProcessExit
26
+ } )
27
+
9
28
describe ( 'ErrorHandler' , ( ) => {
10
29
it ( 'should handle string errors' , ( ) => {
11
30
const error = 'Test error'
@@ -20,66 +39,151 @@ describe('@stacksjs/error-handling', () => {
20
39
expect ( handledError ) . toBe ( error )
21
40
} )
22
41
42
+ it ( 'should handle non-Error objects' , ( ) => {
43
+ const nonError = { message : 'Not an Error' }
44
+ const handledError = ErrorHandler . handle ( nonError )
45
+ expect ( handledError ) . toBeInstanceOf ( Error )
46
+ expect ( handledError . message ) . toBe ( JSON . stringify ( nonError ) )
47
+ } )
48
+
23
49
it ( 'should write error to console when not silent' , ( ) => {
24
- const consoleErrorMock = mock ( console , 'error ' )
50
+ const writeErrorToConsoleSpy = spyOn ( ErrorHandler , 'writeErrorToConsole ' )
25
51
const error = new Error ( 'Test error' )
26
52
ErrorHandler . handle ( error , { silent : false } )
27
- expect ( consoleErrorMock ) . toHaveBeenCalledWith ( error )
28
- consoleErrorMock . mockRestore ( redisTest )
53
+ expect ( writeErrorToConsoleSpy ) . toHaveBeenCalledWith ( error )
54
+ writeErrorToConsoleSpy . mockRestore ( )
55
+ } )
56
+
57
+ it ( 'should not write error to console when silent' , ( ) => {
58
+ const writeErrorToConsoleSpy = spyOn ( ErrorHandler , 'writeErrorToConsole' )
59
+ const error = new Error ( 'Test error' )
60
+ ErrorHandler . handle ( error , { silent : true } )
61
+ expect ( writeErrorToConsoleSpy ) . not . toHaveBeenCalled ( )
62
+ writeErrorToConsoleSpy . mockRestore ( )
29
63
} )
30
64
31
65
it ( 'should write error to file' , async ( ) => {
66
+ const writeErrorToFileSpy = spyOn ( ErrorHandler , 'writeErrorToFile' )
32
67
const error = new Error ( 'Test error' )
33
- await ErrorHandler . writeErrorToFile ( error )
34
- // You can add more checks here to verify the file writing logic
68
+ await ErrorHandler . handle ( error )
69
+ expect ( writeErrorToFileSpy ) . toHaveBeenCalledWith ( error )
70
+ writeErrorToFileSpy . mockRestore ( )
35
71
} )
36
72
37
73
it ( 'should handle specific command errors' , ( ) => {
38
- const consoleErrorMock = mock ( console , 'error' )
39
- const processExitMock = mock ( process , 'exit' )
74
+ const consoleErrorSpy = spyOn ( console , 'error' )
75
+ const processExitSpy = spyOn ( process , 'exit' )
40
76
41
77
const error = `Failed to execute command: ${ italic ( 'bunx --bun biome check --fix' ) } `
42
- ErrorHandler . writeErrorToConsole ( error )
43
- expect ( consoleErrorMock ) . toHaveBeenCalledWith ( error )
44
- expect ( processExitMock ) . toHaveBeenCalledWith ( ExitCode . FatalError )
78
+ expect ( ( ) => ErrorHandler . writeErrorToConsole ( error ) ) . toThrow ( 'process.exit() was called' )
79
+ expect ( consoleErrorSpy ) . toHaveBeenCalledWith ( error )
80
+ expect ( processExitSpy ) . toHaveBeenCalledWith ( ExitCode . FatalError )
45
81
46
- consoleErrorMock . mockRestore ( )
47
- processExitMock . mockRestore ( )
82
+ consoleErrorSpy . mockRestore ( )
83
+ processExitSpy . mockRestore ( )
48
84
} )
49
- } )
50
85
51
- // Tests for utility functions
52
- describe ( 'Utils' , ( ) => {
53
- it ( 'should return the result of the function if no error occurs' , ( ) => {
54
- const result = rescue ( ( ) => 42 , 0 )
55
- expect ( result ) . toBe ( 42 )
86
+ it ( 'should format error message correctly when writing to file' , async ( ) => {
87
+ const appendFileSpy = spyOn ( fs , 'appendFile' )
88
+ const error = new Error ( 'Test error' )
89
+ await ErrorHandler . writeErrorToFile ( error )
90
+ expect ( appendFileSpy ) . toHaveBeenCalledWith (
91
+ expect . any ( String ) ,
92
+ expect . stringMatching ( / ^ \[ \d { 4 } - \d { 2 } - \d { 2 } T \d { 2 } : \d { 2 } : \d { 2 } .\d { 3 } Z \] E r r o r : T e s t e r r o r \n $ / ) ,
93
+ )
94
+ appendFileSpy . mockRestore ( )
56
95
} )
57
96
58
- it ( 'should return the fallback value if an error occurs' , ( ) => {
59
- const result = rescue ( ( ) => {
60
- throw new Error ( 'Test error' )
61
- } , 0 )
62
- expect ( result ) . toBe ( 0 )
97
+ it ( 'should handle CDK destroy command errors' , ( ) => {
98
+ const consoleErrorSpy = spyOn ( console , 'error' )
99
+ const consoleLogSpy = spyOn ( console , 'log' )
100
+ const processExitSpy = spyOn ( process , 'exit' )
101
+
102
+ const error = 'Failed to execute command: bunx --bun cdk destroy'
103
+ expect ( ( ) => ErrorHandler . writeErrorToConsole ( error ) ) . toThrow ( 'process.exit() was called' )
104
+ expect ( consoleErrorSpy ) . toHaveBeenCalledWith ( error )
105
+ expect ( consoleLogSpy ) . toHaveBeenCalledTimes ( 2 )
106
+ expect ( processExitSpy ) . toHaveBeenCalledWith ( ExitCode . FatalError )
107
+
108
+ consoleErrorSpy . mockRestore ( )
109
+ consoleLogSpy . mockRestore ( )
110
+ processExitSpy . mockRestore ( )
111
+ } )
112
+
113
+ it ( 'should use correct log file path' , async ( ) => {
114
+ const mkdirSpy = spyOn ( fs , 'mkdir' )
115
+ const appendFileSpy = spyOn ( fs , 'appendFile' )
116
+ const error = new Error ( 'Test error' )
117
+ await ErrorHandler . writeErrorToFile ( error )
118
+ expect ( mkdirSpy ) . toHaveBeenCalledWith ( expect . any ( String ) , expect . anything ( ) )
119
+ expect ( appendFileSpy . mock . calls [ 0 ] [ 0 ] ) . toMatch ( / s t a c k s \. l o g $ / )
120
+ mkdirSpy . mockRestore ( )
121
+ appendFileSpy . mockRestore ( )
63
122
} )
64
123
} )
65
124
66
- // Tests for handleError function
67
- describe ( 'handleError' , ( ) => {
68
- it ( 'should handle string errors' , ( ) => {
69
- const error = 'Test error'
70
- const handledError = handleError ( error )
71
- expect ( handledError ) . toBeInstanceOf ( Error )
72
- expect ( handledError . message ) . toBe ( error )
125
+ describe ( 'Utils' , ( ) => {
126
+ describe ( 'handleError function' , ( ) => {
127
+ it ( 'should handle string errors' , ( ) => {
128
+ const error = 'Test error'
129
+ const handledError = handleError ( error )
130
+ expect ( handledError ) . toBeInstanceOf ( Error )
131
+ expect ( handledError . message ) . toBe ( error )
132
+ } )
133
+
134
+ it ( 'should handle Error objects' , ( ) => {
135
+ const error = new Error ( 'Test error' )
136
+ const handledError = handleError ( error )
137
+ expect ( handledError ) . toBe ( error )
138
+ } )
139
+
140
+ it ( 'should handle unknown objects' , ( ) => {
141
+ const unknown = { foo : 'bar' }
142
+ const handledError = handleError ( unknown )
143
+ expect ( handledError ) . toBeInstanceOf ( Error )
144
+ expect ( handledError . message ) . toBe ( JSON . stringify ( unknown ) )
145
+ } )
73
146
} )
74
147
75
- it ( 'should handle Error objects' , ( ) => {
76
- const error = new Error ( 'Test error' )
77
- const handledError = handleError ( error )
78
- expect ( handledError ) . toBe ( error )
148
+ describe ( 'rescue function' , ( ) => {
149
+ it ( 'should return the result of the function if no error occurs' , ( ) => {
150
+ const result = rescue ( ( ) => 42 , 0 )
151
+ expect ( result ) . toBe ( 42 )
152
+ } )
153
+
154
+ it ( 'should return the fallback value if an error occurs' , ( ) => {
155
+ const result = rescue ( ( ) => {
156
+ throw new Error ( 'Test error' )
157
+ } , 0 )
158
+ expect ( result ) . toBe ( 0 )
159
+ } )
160
+
161
+ it ( 'should handle async functions' , async ( ) => {
162
+ const result = await rescue ( async ( ) => {
163
+ throw new Error ( 'Async error' )
164
+ } , 'fallback' )
165
+ expect ( result ) . toBe ( 'fallback' )
166
+ } )
167
+
168
+ it ( 'should pass error to onError callback if provided' , ( ) => {
169
+ const onErrorMock = mock ( ( ) => { } )
170
+ rescue (
171
+ ( ) => {
172
+ throw new Error ( 'Test error' )
173
+ } ,
174
+ 0 ,
175
+ onErrorMock ,
176
+ )
177
+ expect ( onErrorMock ) . toHaveBeenCalledWith ( expect . any ( Error ) )
178
+ } )
179
+
180
+ it ( 'should handle successful async operations' , async ( ) => {
181
+ const result = await rescue ( async ( ) => 'async result' , 'fallback' )
182
+ expect ( result ) . toBe ( 'async result' )
183
+ } )
79
184
} )
80
185
} )
81
186
82
- // Tests for index.ts exports
83
187
describe ( 'ErrorHandling Index' , ( ) => {
84
188
it ( 'should export ErrorHandler' , ( ) => {
85
189
expect ( ErrorHandler ) . toBeDefined ( )
0 commit comments