|
2 | 2 | * @vitest-environment node |
3 | 3 | */ |
4 | 4 | import { describe, expect, it } from 'vitest' |
5 | | -import { getPostgresErrorCode, toError } from './errors.js' |
| 5 | +import { describeError, getPostgresErrorCode, toError } from './errors.js' |
6 | 6 |
|
7 | 7 | describe('toError', () => { |
8 | 8 | it('returns the same Error when given an Error', () => { |
@@ -76,3 +76,54 @@ describe('getPostgresErrorCode', () => { |
76 | 76 | expect(getPostgresErrorCode(err1)).toBeUndefined() |
77 | 77 | }) |
78 | 78 | }) |
| 79 | + |
| 80 | +describe('describeError', () => { |
| 81 | + it('reports name and message for a plain error, omitting causeChain', () => { |
| 82 | + const described = describeError(new Error('boom')) |
| 83 | + expect(described).toEqual({ name: 'Error', message: 'boom' }) |
| 84 | + expect(described.causeChain).toBeUndefined() |
| 85 | + }) |
| 86 | + |
| 87 | + it('surfaces the deepest cause for a wrapped driver error', () => { |
| 88 | + const driver = Object.assign(new Error('read ECONNRESET'), { |
| 89 | + code: 'ECONNRESET', |
| 90 | + errno: 'ECONNRESET', |
| 91 | + syscall: 'read', |
| 92 | + }) |
| 93 | + const wrapped = new Error('Failed query: select ...', { cause: driver }) |
| 94 | + const described = describeError(wrapped) |
| 95 | + expect(described.message).toBe('read ECONNRESET') |
| 96 | + expect(described.code).toBe('ECONNRESET') |
| 97 | + expect(described.errno).toBe('ECONNRESET') |
| 98 | + expect(described.syscall).toBe('read') |
| 99 | + expect(described.causeChain).toEqual([ |
| 100 | + 'Error: Failed query: select ...', |
| 101 | + 'Error: read ECONNRESET', |
| 102 | + ]) |
| 103 | + }) |
| 104 | + |
| 105 | + it('always returns the cause for unclassified errors (AbortError)', () => { |
| 106 | + const aborted = Object.assign(new Error('The operation was aborted'), { name: 'AbortError' }) |
| 107 | + expect(describeError(aborted)).toEqual({ |
| 108 | + name: 'AbortError', |
| 109 | + message: 'The operation was aborted', |
| 110 | + }) |
| 111 | + }) |
| 112 | + |
| 113 | + it('falls back to a populated description for non-Error input without throwing', () => { |
| 114 | + expect(describeError('just a string')).toEqual({ name: 'Error', message: 'just a string' }) |
| 115 | + expect(() => describeError({ weird: true })).not.toThrow() |
| 116 | + }) |
| 117 | + |
| 118 | + it('stops at depth 10 and does not loop on a cyclic cause', () => { |
| 119 | + const a = new Error('a') |
| 120 | + const b = new Error('b') |
| 121 | + ;(a as { cause?: unknown }).cause = b |
| 122 | + ;(b as { cause?: unknown }).cause = a |
| 123 | + let described: ReturnType<typeof describeError> | undefined |
| 124 | + expect(() => { |
| 125 | + described = describeError(a) |
| 126 | + }).not.toThrow() |
| 127 | + expect(described?.causeChain?.length).toBeLessThanOrEqual(10) |
| 128 | + }) |
| 129 | +}) |
0 commit comments