Skip to content

Commit e6ed9d9

Browse files
committed
test(http-client): add comprehensive network error handling tests
Add tests for all network error code branches in getResponse function. Covers ENOTFOUND, ETIMEDOUT, ECONNRESET, EPIPE, CERT_HAS_EXPIRED, UNABLE_TO_VERIFY_LEAF_SIGNATURE, and unknown error codes. Coverage improvements: - http-client.ts statements: 73.91% → 76.39% (+2.48%) - http-client.ts branches: 61.22% → 67.34% (+6.12%) - http-client.ts lines: 75% → 77.56% (+2.56%) - Overall statements: 74.65% → 75.18% (+0.53%) - Overall branches: 59.3% → 60.69% (+1.39%) - Total tests: 456 → 466 (+10 tests)
1 parent a71f0e9 commit e6ed9d9

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/** @fileoverview Tests for HTTP client network error handling. */
2+
3+
import { EventEmitter } from 'node:events'
4+
5+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
6+
7+
import { createGetRequest, getResponse } from '../../src/http-client'
8+
9+
import type { ClientRequest, IncomingMessage } from 'node:http'
10+
11+
describe('HTTP Client - Network Error Handling', () => {
12+
beforeEach(() => {
13+
vi.clearAllMocks()
14+
})
15+
16+
afterEach(() => {
17+
vi.restoreAllMocks()
18+
})
19+
20+
describe('getResponse error codes', () => {
21+
it('should handle ENOTFOUND error', async () => {
22+
const mockRequest = new EventEmitter() as ClientRequest
23+
24+
const responsePromise = getResponse(mockRequest)
25+
26+
// Simulate DNS lookup failure
27+
const error = new Error('getaddrinfo ENOTFOUND api.socket.dev')
28+
Object.assign(error, { code: 'ENOTFOUND' })
29+
mockRequest.emit('error', error)
30+
31+
await expect(responsePromise).rejects.toThrow('DNS lookup failed')
32+
await expect(responsePromise).rejects.toThrow('Cannot resolve hostname')
33+
})
34+
35+
it('should handle ETIMEDOUT error', async () => {
36+
const mockRequest = new EventEmitter() as ClientRequest
37+
38+
const responsePromise = getResponse(mockRequest)
39+
40+
// Simulate connection timeout
41+
const error = new Error('connect ETIMEDOUT')
42+
Object.assign(error, { code: 'ETIMEDOUT' })
43+
mockRequest.emit('error', error)
44+
45+
await expect(responsePromise).rejects.toThrow('Connection timed out')
46+
await expect(responsePromise).rejects.toThrow('Network or server issue')
47+
})
48+
49+
it('should handle ECONNRESET error', async () => {
50+
const mockRequest = new EventEmitter() as ClientRequest
51+
52+
const responsePromise = getResponse(mockRequest)
53+
54+
// Simulate connection reset
55+
const error = new Error('socket hang up')
56+
Object.assign(error, { code: 'ECONNRESET' })
57+
mockRequest.emit('error', error)
58+
59+
await expect(responsePromise).rejects.toThrow('Connection reset by server')
60+
await expect(responsePromise).rejects.toThrow(
61+
'Possible network interruption',
62+
)
63+
})
64+
65+
it('should handle EPIPE error', async () => {
66+
const mockRequest = new EventEmitter() as ClientRequest
67+
68+
const responsePromise = getResponse(mockRequest)
69+
70+
// Simulate broken pipe
71+
const error = new Error('write EPIPE')
72+
Object.assign(error, { code: 'EPIPE' })
73+
mockRequest.emit('error', error)
74+
75+
await expect(responsePromise).rejects.toThrow('Broken pipe')
76+
await expect(responsePromise).rejects.toThrow(
77+
'Server closed connection unexpectedly',
78+
)
79+
})
80+
81+
it('should handle CERT_HAS_EXPIRED error', async () => {
82+
const mockRequest = new EventEmitter() as ClientRequest
83+
84+
const responsePromise = getResponse(mockRequest)
85+
86+
// Simulate certificate expiry
87+
const error = new Error('certificate has expired')
88+
Object.assign(error, { code: 'CERT_HAS_EXPIRED' })
89+
mockRequest.emit('error', error)
90+
91+
await expect(responsePromise).rejects.toThrow('SSL/TLS certificate error')
92+
await expect(responsePromise).rejects.toThrow(
93+
'System time and date are correct',
94+
)
95+
})
96+
97+
it('should handle UNABLE_TO_VERIFY_LEAF_SIGNATURE error', async () => {
98+
const mockRequest = new EventEmitter() as ClientRequest
99+
100+
const responsePromise = getResponse(mockRequest)
101+
102+
// Simulate certificate verification failure
103+
const error = new Error('unable to verify the first certificate')
104+
Object.assign(error, { code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' })
105+
mockRequest.emit('error', error)
106+
107+
await expect(responsePromise).rejects.toThrow('SSL/TLS certificate error')
108+
})
109+
110+
it('should handle unknown error codes', async () => {
111+
const mockRequest = new EventEmitter() as ClientRequest
112+
113+
const responsePromise = getResponse(mockRequest)
114+
115+
// Simulate unknown error code
116+
const error = new Error('some unknown error')
117+
Object.assign(error, { code: 'UNKNOWN_ERROR' })
118+
mockRequest.emit('error', error)
119+
120+
await expect(responsePromise).rejects.toThrow('Error code: UNKNOWN_ERROR')
121+
})
122+
123+
it('should handle errors without error codes', async () => {
124+
const mockRequest = new EventEmitter() as ClientRequest
125+
126+
const responsePromise = getResponse(mockRequest)
127+
128+
// Simulate error without code
129+
const error = new Error('generic error message')
130+
mockRequest.emit('error', error)
131+
132+
await expect(responsePromise).rejects.toThrow('request failed')
133+
})
134+
})
135+
136+
describe('createGetRequest integration with error codes', () => {
137+
it('should propagate ENOTFOUND through createGetRequest', async () => {
138+
await expect(
139+
createGetRequest('http://nonexistent.socket.dev.invalid', '/test', {
140+
timeout: 100,
141+
}),
142+
).rejects.toThrow()
143+
})
144+
145+
it('should propagate ECONNREFUSED through createGetRequest', async () => {
146+
// Use a port that's guaranteed not to have a server running
147+
await expect(
148+
createGetRequest('http://localhost:1', '/test', {
149+
timeout: 100,
150+
}),
151+
).rejects.toThrow()
152+
})
153+
})
154+
})

0 commit comments

Comments
 (0)