@devp0nt/error0 1.0.0-next.1

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.
@@ -0,0 +1,541 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { type AxiosError, isAxiosError } from 'axios'
3
+ import z, { ZodError } from 'zod'
4
+ import { Error0, e0s } from './index.js'
5
+
6
+ // TODO: test expected
7
+
8
+ const fixStack = (stack: string | undefined) => {
9
+ if (!stack) {
10
+ return stack
11
+ }
12
+ // at <anonymous> (/Users/iserdmi/cc/projects/svagatron/modules/lib/error0.test.ts:103:25)
13
+ // >>
14
+ // at <anonymous> (...)
15
+ const lines = stack.split('\n')
16
+ const fixedLines = lines.map((line) => {
17
+ const withoutPath = line.replace(/\(.*\)$/, '(...)')
18
+ return withoutPath
19
+ })
20
+ return fixedLines.join('\n')
21
+ }
22
+
23
+ const toJSON = (error: Error0) => {
24
+ const result = error.toJSON()
25
+ result.stack = fixStack(error.stack)
26
+ return result
27
+ }
28
+
29
+ describe('error0', () => {
30
+ it('simple', () => {
31
+ const error0 = new Error0('test')
32
+ expect(error0).toBeInstanceOf(Error0)
33
+ expect(error0).toMatchInlineSnapshot(`[Error0: test]`)
34
+ expect(toJSON(error0)).toMatchInlineSnapshot(`
35
+ {
36
+ "__I_AM_ERROR_0": true,
37
+ "anyMessage": undefined,
38
+ "cause": undefined,
39
+ "clientMessage": undefined,
40
+ "code": undefined,
41
+ "expected": false,
42
+ "httpStatus": undefined,
43
+ "message": "test",
44
+ "meta": {},
45
+ "stack":
46
+ "Error0: test
47
+ at <anonymous> (...)"
48
+ ,
49
+ "tag": undefined,
50
+ }
51
+ `)
52
+ })
53
+
54
+ it('full', () => {
55
+ const input = {
56
+ message: 'my message',
57
+ tag: 'tag1',
58
+ code: 'code1',
59
+ httpStatus: 400,
60
+ expected: true,
61
+ clientMessage: 'human message 1',
62
+ cause: new Error('original message'),
63
+ meta: {
64
+ reqDurationMs: 1,
65
+ userId: 'user1',
66
+ },
67
+ }
68
+ const error1 = new Error0(input)
69
+ const error2 = new Error0(input.message, input)
70
+ expect(toJSON(error1)).toMatchObject(toJSON(error2))
71
+ expect(toJSON(error1)).toMatchInlineSnapshot(`
72
+ {
73
+ "__I_AM_ERROR_0": true,
74
+ "anyMessage": undefined,
75
+ "cause": [Error: original message],
76
+ "clientMessage": "human message 1",
77
+ "code": "code1",
78
+ "expected": true,
79
+ "httpStatus": 400,
80
+ "message": "my message",
81
+ "meta": {
82
+ "reqDurationMs": 1,
83
+ "userId": "user1",
84
+ },
85
+ "stack":
86
+ "Error0: my message
87
+ at <anonymous> (...)
88
+
89
+ Error: original message
90
+ at <anonymous> (...)"
91
+ ,
92
+ "tag": "tag1",
93
+ }
94
+ `)
95
+ })
96
+
97
+ it('cause error default', () => {
98
+ const errorDefault = new Error('original message')
99
+ const error0 = new Error0('my message', { cause: errorDefault })
100
+ expect(error0).toBeInstanceOf(Error0)
101
+ expect(error0).toMatchInlineSnapshot(`[Error0: my message]`)
102
+ expect(toJSON(error0)).toMatchInlineSnapshot(`
103
+ {
104
+ "__I_AM_ERROR_0": true,
105
+ "anyMessage": undefined,
106
+ "cause": [Error: original message],
107
+ "clientMessage": undefined,
108
+ "code": undefined,
109
+ "expected": false,
110
+ "httpStatus": undefined,
111
+ "message": "my message",
112
+ "meta": {},
113
+ "stack":
114
+ "Error0: my message
115
+ at <anonymous> (...)
116
+
117
+ Error: original message
118
+ at <anonymous> (...)"
119
+ ,
120
+ "tag": undefined,
121
+ }
122
+ `)
123
+ })
124
+
125
+ it('cause strange thing', () => {
126
+ const error0 = new Error0('my message', { cause: 'strange thing' })
127
+ expect(error0).toMatchInlineSnapshot(`[Error0: my message]`)
128
+ expect(toJSON(error0)).toMatchInlineSnapshot(`
129
+ {
130
+ "__I_AM_ERROR_0": true,
131
+ "anyMessage": undefined,
132
+ "cause": "strange thing",
133
+ "clientMessage": undefined,
134
+ "code": undefined,
135
+ "expected": false,
136
+ "httpStatus": undefined,
137
+ "message": "my message",
138
+ "meta": {},
139
+ "stack":
140
+ "Error0: my message
141
+ at <anonymous> (...)"
142
+ ,
143
+ "tag": undefined,
144
+ }
145
+ `)
146
+ })
147
+
148
+ it('floats and overrides', () => {
149
+ const error01 = new Error0('first', {
150
+ tag: 'tag1',
151
+ clientMessage: 'human message 1',
152
+ meta: {
153
+ reqDurationMs: 1,
154
+ userId: 'user1',
155
+ },
156
+ })
157
+ const error02 = new Error0('second', {
158
+ tag: 'tag2',
159
+ code: 'code2',
160
+ cause: error01,
161
+ meta: {
162
+ reqDurationMs: 1,
163
+ ideaId: 'idea1',
164
+ other: {
165
+ x: 1,
166
+ },
167
+ },
168
+ })
169
+ expect(error01).toBeInstanceOf(Error0)
170
+ expect(toJSON(error02)).toMatchInlineSnapshot(`
171
+ {
172
+ "__I_AM_ERROR_0": true,
173
+ "anyMessage": undefined,
174
+ "cause": [Error0: first],
175
+ "clientMessage": "human message 1",
176
+ "code": "code2",
177
+ "expected": false,
178
+ "httpStatus": undefined,
179
+ "message": "second",
180
+ "meta": {
181
+ "ideaId": "idea1",
182
+ "other": {
183
+ "x": 1,
184
+ },
185
+ "reqDurationMs": 1,
186
+ "userId": "user1",
187
+ },
188
+ "stack":
189
+ "Error0: second
190
+ at <anonymous> (...)
191
+
192
+ Error0: first
193
+ at <anonymous> (...)"
194
+ ,
195
+ "tag": "tag2",
196
+ }
197
+ `)
198
+ })
199
+
200
+ it('unknown error', () => {
201
+ const error0 = new Error0({})
202
+ expect(toJSON(error0)).toMatchInlineSnapshot(`
203
+ {
204
+ "__I_AM_ERROR_0": true,
205
+ "anyMessage": undefined,
206
+ "cause": undefined,
207
+ "clientMessage": undefined,
208
+ "code": undefined,
209
+ "expected": false,
210
+ "httpStatus": undefined,
211
+ "message": "Unknown error",
212
+ "meta": {},
213
+ "stack":
214
+ "Error0: Unknown error
215
+ at <anonymous> (...)"
216
+ ,
217
+ "tag": undefined,
218
+ }
219
+ `)
220
+ const error1 = new Error0('test')
221
+ expect(error1.message).toBe('test')
222
+ const error2 = new Error0({ cause: error1 })
223
+ expect(toJSON(error2)).toMatchInlineSnapshot(`
224
+ {
225
+ "__I_AM_ERROR_0": true,
226
+ "anyMessage": undefined,
227
+ "cause": [Error0: test],
228
+ "clientMessage": undefined,
229
+ "code": undefined,
230
+ "expected": false,
231
+ "httpStatus": undefined,
232
+ "message": "Unknown error",
233
+ "meta": {},
234
+ "stack":
235
+ "Error0: Unknown error
236
+ at <anonymous> (...)
237
+
238
+ Error0: test
239
+ at <anonymous> (...)"
240
+ ,
241
+ "tag": undefined,
242
+ }
243
+ `)
244
+ expect(fixStack(error2.stack)).toMatchInlineSnapshot(`
245
+ "Error0: Unknown error
246
+ at <anonymous> (...)
247
+
248
+ Error0: test
249
+ at <anonymous> (...)"
250
+ `)
251
+ })
252
+
253
+ it('input error default', () => {
254
+ const errorDefault = new Error('default error')
255
+ const error0 = new Error0(errorDefault)
256
+ expect(toJSON(error0)).toMatchInlineSnapshot(`
257
+ {
258
+ "__I_AM_ERROR_0": true,
259
+ "anyMessage": undefined,
260
+ "cause": [Error: default error],
261
+ "clientMessage": undefined,
262
+ "code": undefined,
263
+ "expected": false,
264
+ "httpStatus": undefined,
265
+ "message": "Unknown error",
266
+ "meta": {},
267
+ "stack":
268
+ "Error0: Unknown error
269
+ at <anonymous> (...)
270
+
271
+ Error: default error
272
+ at <anonymous> (...)"
273
+ ,
274
+ "tag": undefined,
275
+ }
276
+ `)
277
+ expect(fixStack(error0.stack)).toMatchInlineSnapshot(`
278
+ "Error0: Unknown error
279
+ at <anonymous> (...)
280
+
281
+ Error: default error
282
+ at <anonymous> (...)"
283
+ `)
284
+ })
285
+
286
+ it('input error0 itself', () => {
287
+ const error = new Error0('error0 error')
288
+ const error0 = new Error0(error)
289
+ expect(toJSON(error0)).toMatchInlineSnapshot(`
290
+ {
291
+ "__I_AM_ERROR_0": true,
292
+ "anyMessage": undefined,
293
+ "cause": [Error0: error0 error],
294
+ "clientMessage": undefined,
295
+ "code": undefined,
296
+ "expected": false,
297
+ "httpStatus": undefined,
298
+ "message": "Unknown error",
299
+ "meta": {},
300
+ "stack":
301
+ "Error0: Unknown error
302
+ at <anonymous> (...)
303
+
304
+ Error0: error0 error
305
+ at <anonymous> (...)"
306
+ ,
307
+ "tag": undefined,
308
+ }
309
+ `)
310
+ expect(fixStack(error0.stack)).toMatchInlineSnapshot(`
311
+ "Error0: Unknown error
312
+ at <anonymous> (...)
313
+
314
+ Error0: error0 error
315
+ at <anonymous> (...)"
316
+ `)
317
+ })
318
+
319
+ it('keep stack trace', () => {
320
+ const errorDefault = new Error('default error')
321
+ const error01 = new Error0('first', {
322
+ tag: 'tag1',
323
+ clientMessage: 'human message 1',
324
+ cause: errorDefault,
325
+ })
326
+ const error02 = new Error0('second', {
327
+ tag: 'tag2',
328
+ code: 'code2',
329
+ cause: error01,
330
+ })
331
+ expect(fixStack(errorDefault.stack)).toMatchInlineSnapshot(`
332
+ "Error: default error
333
+ at <anonymous> (...)"
334
+ `)
335
+ expect(fixStack(error01.stack)).toMatchInlineSnapshot(`
336
+ "Error0: first
337
+ at <anonymous> (...)
338
+
339
+ Error: default error
340
+ at <anonymous> (...)"
341
+ `)
342
+ expect(fixStack(error02.stack)).toMatchInlineSnapshot(`
343
+ "Error0: second
344
+ at <anonymous> (...)
345
+
346
+ Error0: first
347
+ at <anonymous> (...)
348
+
349
+ Error: default error
350
+ at <anonymous> (...)"
351
+ `)
352
+ })
353
+
354
+ it('expected', () => {
355
+ const error0 = new Error0({
356
+ expected: true,
357
+ })
358
+ expect(error0.expected).toBe(true)
359
+
360
+ const error1 = new Error0({
361
+ expected: false,
362
+ })
363
+ expect(error1.expected).toBe(false)
364
+
365
+ const error3 = new Error0({
366
+ expected: true,
367
+ cause: error0,
368
+ })
369
+ expect(error3.expected).toBe(true)
370
+
371
+ const error4 = new Error0({
372
+ expected: false,
373
+ cause: error0,
374
+ })
375
+ expect(error4.expected).toBe(false)
376
+
377
+ const error5 = new Error0({
378
+ expected: true,
379
+ cause: error1,
380
+ })
381
+ expect(error5.expected).toBe(false)
382
+
383
+ const error6 = new Error0({
384
+ expected: false,
385
+ cause: error1,
386
+ })
387
+ expect(error6.expected).toBe(false)
388
+ })
389
+
390
+ it('extends self', () => {
391
+ const error7 = new e0s.Expected('expected error')
392
+ expect(e0s.Expected.defaultExpected).toBe(true)
393
+ expect(error7.expected).toBe(true)
394
+ expect(error7).toBeInstanceOf(e0s.Expected)
395
+ expect(error7).toBeInstanceOf(Error0)
396
+ expect(toJSON(error7)).toMatchInlineSnapshot(`
397
+ {
398
+ "__I_AM_ERROR_0": true,
399
+ "anyMessage": undefined,
400
+ "cause": undefined,
401
+ "clientMessage": undefined,
402
+ "code": undefined,
403
+ "expected": true,
404
+ "httpStatus": undefined,
405
+ "message": "expected error",
406
+ "meta": {},
407
+ "stack":
408
+ "Error0: expected error
409
+ at <anonymous> (...)"
410
+ ,
411
+ "tag": undefined,
412
+ }
413
+ `)
414
+ })
415
+
416
+ it('extend collection', () => {
417
+ const e0s1 = Error0.extendCollection(e0s, {
418
+ defaultMessage: 'nested error',
419
+ defaultMeta: {
420
+ tagPrefix: 'nested',
421
+ },
422
+ })
423
+ const error0 = new e0s1.Default('nested error')
424
+ expect(error0).toBeInstanceOf(e0s1.Default)
425
+ expect(error0).toBeInstanceOf(e0s.Default)
426
+ expect(error0).toBeInstanceOf(Error0)
427
+ expect(toJSON(error0)).toMatchInlineSnapshot(`
428
+ {
429
+ "__I_AM_ERROR_0": true,
430
+ "anyMessage": undefined,
431
+ "cause": undefined,
432
+ "clientMessage": undefined,
433
+ "code": undefined,
434
+ "expected": false,
435
+ "httpStatus": undefined,
436
+ "message": "nested error",
437
+ "meta": {
438
+ "tagPrefix": "nested",
439
+ },
440
+ "stack":
441
+ "Error0: nested error
442
+ at <anonymous> (...)"
443
+ ,
444
+ "tag": "nested:nested",
445
+ }
446
+ `)
447
+
448
+ const error02 = new e0s1.Expected('nested error 1')
449
+ expect(error02).toBeInstanceOf(e0s1.Expected)
450
+ expect(error02).toBeInstanceOf(e0s.Expected)
451
+ expect(error02).toBeInstanceOf(Error0)
452
+ expect(error02.expected).toBe(true)
453
+ expect(toJSON(error02)).toMatchInlineSnapshot(`
454
+ {
455
+ "__I_AM_ERROR_0": true,
456
+ "anyMessage": undefined,
457
+ "cause": undefined,
458
+ "clientMessage": undefined,
459
+ "code": undefined,
460
+ "expected": true,
461
+ "httpStatus": undefined,
462
+ "message": "nested error 1",
463
+ "meta": {
464
+ "tagPrefix": "nested",
465
+ },
466
+ "stack":
467
+ "Error0: nested error 1
468
+ at <anonymous> (...)"
469
+ ,
470
+ "tag": "nested:nested",
471
+ }
472
+ `)
473
+ })
474
+
475
+ it('cause zod error', () => {
476
+ const zodError = z.object({ x: z.number() }).safeParse('test').error
477
+ if (!zodError) {
478
+ throw new Error('zodError is undefined')
479
+ }
480
+ expect(zodError).toBeInstanceOf(ZodError)
481
+ const error0 = new Error0(zodError)
482
+ expect(error0.zodError).toBe(zodError)
483
+ expect(error0.message).toBe('Unknown error')
484
+ })
485
+
486
+ it('from zod error', () => {
487
+ const zodError = z.object({ x: z.number() }).safeParse('test').error
488
+ if (!zodError) {
489
+ throw new Error('zodError is undefined')
490
+ }
491
+ expect(zodError).toBeInstanceOf(ZodError)
492
+ const error0 = Error0.from(zodError)
493
+ expect(error0.zodError).toBe(zodError)
494
+ expect(error0.message).toMatchInlineSnapshot(`
495
+ "Zod Validation Error: [
496
+ {
497
+ "expected": "object",
498
+ "code": "invalid_type",
499
+ "path": [],
500
+ "message": "Invalid input: expected object, received string"
501
+ }
502
+ ]"
503
+ `)
504
+ })
505
+
506
+ it('from axios error', async () => {
507
+ function makeFakeAxiosError(): AxiosError {
508
+ return {
509
+ isAxiosError: true,
510
+ name: 'AxiosError',
511
+ message: 'Request failed with status code 400',
512
+ config: {}, // can be empty for test
513
+ toJSON: () => ({}),
514
+ response: {
515
+ status: 400,
516
+ statusText: 'Bad Request',
517
+ headers: {},
518
+ config: {},
519
+ data: {
520
+ error: 'Invalid input',
521
+ details: ['Field X is required'],
522
+ },
523
+ },
524
+ } as AxiosError
525
+ }
526
+ const axiosError = makeFakeAxiosError()
527
+ if (!axiosError) {
528
+ throw new Error('axiosError is undefined')
529
+ }
530
+ expect(isAxiosError(axiosError)).toBe(true)
531
+ const error0 = Error0.from(axiosError)
532
+ expect(error0.axiosError).toBe(axiosError)
533
+ expect(error0.message).toBe('Axios Error')
534
+ expect(error0.meta).toMatchInlineSnapshot(`
535
+ {
536
+ "axiosData": "{"error":"Invalid input","details":["Field X is required"]}",
537
+ "axiosStatus": 400,
538
+ }
539
+ `)
540
+ })
541
+ })