返回 AiToEarn
error.serializer.spec.ts
根目录 / project / aitoearn-backend / libs / common / src / loggers / error.serializer.spec.ts
1 import pino from 'pino'
2 import { describe, expect, it } from 'vitest'
3 import { serializeError } from './error.serializer'
4
5 function createAxiosErrorLike() {
6 const error = Object.assign(new Error('Request failed with status code 429'), {
7 name: 'AxiosError',
8 code: 'ERR_BAD_REQUEST',
9 isAxiosError: true,
10 config: {
11 baseURL: 'https://api.example.com',
12 url: '/v1/video/create',
13 method: 'post',
14 headers: {
15 Authorization: 'Bearer token',
16 },
17 },
18 request: {
19 socket: { remoteAddress: '127.0.0.1' },
20 agent: { keepAlive: true },
21 },
22 response: {
23 status: 429,
24 statusText: 'Too Many Requests',
25 data: {
26 error: 'rate limited',
27 },
28 headers: {
29 'x-api-request-id': 'request-1',
30 },
31 request: {
32 socket: { remoteAddress: '127.0.0.1' },
33 },
34 req: {
35 socket: { remoteAddress: '127.0.0.1' },
36 },
37 socket: {
38 remoteAddress: '127.0.0.1',
39 },
40 },
41 status: 429,
42 })
43
44 return error
45 }
46
47 describe('error serializer', () => {
48 it('keeps normal error fields', () => {
49 const serialized = serializeError(new Error('failed')) as Record<string, unknown>
50
51 expect(serialized['type']).toBe('Error')
52 expect(serialized['message']).toBe('failed')
53 expect(serialized['stack']).toContain('failed')
54 })
55
56 it('keeps error cause', () => {
57 const cause = new Error('inner')
58 const serialized = serializeError(new Error('outer', { cause })) as Record<string, unknown>
59 const serializedCause = serialized['cause'] as Record<string, unknown>
60
61 expect(serialized['message']).toBe('outer')
62 expect(serializedCause['message']).toBe('inner')
63 expect(String(serializedCause['stack'])).toContain('inner')
64 })
65
66 it('removes axios request and socket data only', () => {
67 const serialized = serializeError(createAxiosErrorLike()) as Record<string, unknown>
68 const config = serialized['config'] as Record<string, unknown>
69 const response = serialized['response'] as Record<string, unknown>
70
71 expect(serialized['message']).toBe('Request failed with status code 429')
72 expect(serialized['code']).toBe('ERR_BAD_REQUEST')
73 expect(serialized['status']).toBe(429)
74 expect(config['baseURL']).toBe('https://api.example.com')
75 expect(response['status']).toBe(429)
76 expect(response['data']).toEqual({ error: 'rate limited' })
77 expect(response['headers']).toEqual({ 'x-api-request-id': 'request-1' })
78 expect(serialized).not.toHaveProperty('request')
79 expect(serialized).not.toHaveProperty('raw')
80 expect(response).not.toHaveProperty('request')
81 expect(response).not.toHaveProperty('req')
82 expect(response).not.toHaveProperty('socket')
83 })
84
85 it('removes axios socket data from causes', () => {
86 const serialized = serializeError(new Error('wrapped', { cause: createAxiosErrorLike() })) as Record<string, unknown>
87 const cause = serialized['cause'] as Record<string, unknown>
88 const response = cause['response'] as Record<string, unknown>
89
90 expect(cause['isAxiosError']).toBe(true)
91 expect(cause['config']).toBeDefined()
92 expect(cause).not.toHaveProperty('request')
93 expect(cause).not.toHaveProperty('raw')
94 expect(response).not.toHaveProperty('request')
95 expect(response).not.toHaveProperty('req')
96 expect(response).not.toHaveProperty('socket')
97 })
98
99 it('supports pino object field named error', () => {
100 const logs: Array<Record<string, unknown>> = []
101 const logger = pino({
102 serializers: {
103 error: serializeError,
104 },
105 }, {
106 write(line) {
107 logs.push(JSON.parse(line) as Record<string, unknown>)
108 },
109 })
110
111 logger.error({ error: createAxiosErrorLike() }, 'request failed')
112
113 const error = logs[0]['error'] as Record<string, unknown>
114 const response = error['response'] as Record<string, unknown>
115
116 expect(error['code']).toBe('ERR_BAD_REQUEST')
117 expect(error).not.toHaveProperty('request')
118 expect(error).not.toHaveProperty('raw')
119 expect(response['data']).toEqual({ error: 'rate limited' })
120 expect(response).not.toHaveProperty('request')
121 expect(response).not.toHaveProperty('req')
122 expect(response).not.toHaveProperty('socket')
123 })
124
125 it('returns non-error values unchanged', () => {
126 const value = { ok: true }
127
128 expect(serializeError(value)).toBe(value)
129 })
130 })
131
131 lines TYPESCRIPT