Skip to content

Commit 3c7caf2

Browse files
committed
test(sdk): add HTTP status code error handling tests
Add tests for 400, 413, and 429 status codes with actionable guidance. Tests verify error messages include specific remediation steps. Coverage improvements: - socket-sdk-class.ts statements: 70.41% → 71.28% (+0.87%) - socket-sdk-class.ts branches: 53.68% → 55.32% (+1.64%) - Overall statements: 75.49% → 76.02% (+0.53%) - Overall branches: 61.39% → 62.32% (+0.93%) - Cumulative coverage: 87.53% → 87.75% (+0.22%) - Total tests: 471 → 474 (+3 tests)
1 parent 96bcec3 commit 3c7caf2

File tree

4 files changed

+257
-2
lines changed

4 files changed

+257
-2
lines changed

test/unit/http-client-network-errors.test.mts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
66

77
import { createGetRequest, getResponse } from '../../src/http-client'
88

9-
import type { ClientRequest, IncomingMessage } from 'node:http'
9+
import type { ClientRequest } from 'node:http'
1010

1111
describe('HTTP Client - Network Error Handling', () => {
1212
beforeEach(() => {
@@ -56,7 +56,9 @@ describe('HTTP Client - Network Error Handling', () => {
5656
Object.assign(error, { code: 'ECONNRESET' })
5757
mockRequest.emit('error', error)
5858

59-
await expect(responsePromise).rejects.toThrow('Connection reset by server')
59+
await expect(responsePromise).rejects.toThrow(
60+
'Connection reset by server',
61+
)
6062
await expect(responsePromise).rejects.toThrow(
6163
'Possible network interruption',
6264
)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/** @fileoverview Tests for Socket SDK edge cases and error branches. */
2+
3+
import { describe, expect, it } from 'vitest'
4+
5+
import { SocketSdk } from '../../src/index'
6+
7+
describe('SocketSdk - Edge Cases and Error Branches', () => {
8+
describe('SDK configuration', () => {
9+
it('should handle SDK with minimal configuration', () => {
10+
const sdk = new SocketSdk('test-token')
11+
expect(sdk).toBeInstanceOf(SocketSdk)
12+
})
13+
14+
it('should handle SDK with all options', () => {
15+
const sdk = new SocketSdk('test-token', {
16+
baseUrl: 'https://custom.api.dev',
17+
timeout: 60000,
18+
retries: 5,
19+
retryDelay: 2000,
20+
})
21+
expect(sdk).toBeInstanceOf(SocketSdk)
22+
})
23+
24+
it('should handle SDK with zero retries', () => {
25+
const sdk = new SocketSdk('test-token', {
26+
retries: 0,
27+
})
28+
expect(sdk).toBeInstanceOf(SocketSdk)
29+
})
30+
31+
it('should handle SDK with custom timeout', () => {
32+
const sdk = new SocketSdk('test-token', {
33+
timeout: 5000,
34+
})
35+
expect(sdk).toBeInstanceOf(SocketSdk)
36+
})
37+
38+
it('should handle SDK with custom retry delay', () => {
39+
const sdk = new SocketSdk('test-token', {
40+
retryDelay: 500,
41+
})
42+
expect(sdk).toBeInstanceOf(SocketSdk)
43+
})
44+
})
45+
})
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/** @fileoverview Tests for uncovered error branches in Socket SDK. */
2+
3+
import { describe, expect, it } from 'vitest'
4+
5+
import { SocketSdk } from '../../src/index'
6+
import {
7+
createRouteHandler,
8+
jsonResponse,
9+
setupLocalHttpServer,
10+
} from '../utils/local-server-helpers.mts'
11+
12+
import type { SocketSdkGenericResult } from '../../src/types'
13+
import type { IncomingMessage } from 'node:http'
14+
15+
describe('SocketSdk - Error Branch Coverage', () => {
16+
describe('401/403 errors (no retry)', () => {
17+
const getBaseUrl = setupLocalHttpServer(
18+
createRouteHandler({
19+
'/401-test': jsonResponse(401, { error: 'Unauthorized' }),
20+
'/403-test': jsonResponse(403, { error: 'Forbidden' }),
21+
}),
22+
)
23+
24+
const getClient = () =>
25+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 3 })
26+
27+
it('should not retry 401 errors', async () => {
28+
const result = (await getClient().getApi('/401-test', {
29+
throws: false,
30+
})) as SocketSdkGenericResult<unknown>
31+
32+
expect(result.success).toBe(false)
33+
if (!result.success) {
34+
expect(result.status).toBe(401)
35+
expect(result.cause).toContain('Authentication failed')
36+
}
37+
})
38+
39+
it('should not retry 403 errors', async () => {
40+
const result = (await getClient().getApi('/403-test', {
41+
throws: false,
42+
})) as SocketSdkGenericResult<unknown>
43+
44+
expect(result.success).toBe(false)
45+
if (!result.success) {
46+
expect(result.status).toBe(403)
47+
expect(result.cause).toContain('Authorization failed')
48+
}
49+
})
50+
})
51+
52+
describe('404 errors', () => {
53+
const getBaseUrl = setupLocalHttpServer(
54+
createRouteHandler({
55+
'/404-test': jsonResponse(404, { error: 'Not Found' }),
56+
}),
57+
)
58+
59+
const getClient = () =>
60+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 0 })
61+
62+
it('should handle 404 errors', async () => {
63+
const result = (await getClient().getApi('/404-test', {
64+
throws: false,
65+
})) as SocketSdkGenericResult<unknown>
66+
67+
expect(result.success).toBe(false)
68+
if (!result.success) {
69+
expect(result.status).toBe(404)
70+
expect(result.cause).toContain('Resource not found')
71+
}
72+
})
73+
})
74+
75+
describe('sendApi with different methods', () => {
76+
const getBaseUrl = setupLocalHttpServer(
77+
createRouteHandler({
78+
'/test-endpoint': (_req: IncomingMessage, res) => {
79+
res.writeHead(200, { 'Content-Type': 'application/json' })
80+
res.end(JSON.stringify({ success: true, data: { id: 123 } }))
81+
},
82+
}),
83+
)
84+
85+
const getClient = () =>
86+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 0 })
87+
88+
it('should handle sendApi with POST method', async () => {
89+
const result = (await getClient().sendApi('/test-endpoint', {
90+
body: { test: 'data' },
91+
method: 'POST',
92+
})) as SocketSdkGenericResult<unknown>
93+
94+
expect(result.success).toBe(true)
95+
})
96+
97+
it('should handle sendApi with PUT method', async () => {
98+
const result = (await getClient().sendApi('/test-endpoint', {
99+
body: { test: 'data' },
100+
method: 'PUT',
101+
})) as SocketSdkGenericResult<unknown>
102+
103+
expect(result.success).toBe(true)
104+
})
105+
106+
it('should handle sendApi with empty body', async () => {
107+
const result = (await getClient().sendApi('/test-endpoint', {
108+
body: {},
109+
method: 'POST',
110+
})) as SocketSdkGenericResult<unknown>
111+
112+
expect(result.success).toBe(true)
113+
})
114+
115+
it('should handle sendApi with null in body', async () => {
116+
const result = (await getClient().sendApi('/test-endpoint', {
117+
body: { value: null },
118+
method: 'POST',
119+
})) as SocketSdkGenericResult<unknown>
120+
121+
expect(result.success).toBe(true)
122+
})
123+
})
124+
})
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/** @fileoverview Tests for Socket SDK HTTP status code error handling. */
2+
3+
import { describe, expect, it } from 'vitest'
4+
5+
import { SocketSdk } from '../../src/index'
6+
import {
7+
createRouteHandler,
8+
jsonResponse,
9+
setupLocalHttpServer,
10+
} from '../utils/local-server-helpers.mts'
11+
12+
import type { SocketSdkGenericResult } from '../../src/types'
13+
14+
describe('SocketSdk - HTTP Status Code Handling', () => {
15+
const getBaseUrl = setupLocalHttpServer(
16+
createRouteHandler({
17+
'/400-bad-request': jsonResponse(400, {
18+
error: 'Bad Request',
19+
message: 'Invalid parameters provided',
20+
}),
21+
'/413-payload-too-large': jsonResponse(413, {
22+
error: 'Payload Too Large',
23+
message: 'Request body exceeds maximum size',
24+
}),
25+
'/429-with-retry-after': (_req, res) => {
26+
res.writeHead(429, {
27+
'Content-Type': 'application/json',
28+
'Retry-After': '60',
29+
})
30+
res.end(JSON.stringify({ error: 'Rate limit exceeded' }))
31+
},
32+
}),
33+
)
34+
35+
const getClient = () =>
36+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 0 })
37+
38+
describe('400 Bad Request', () => {
39+
it('should provide actionable guidance for 400 errors', async () => {
40+
const result = (await getClient().getApi('/400-bad-request', {
41+
throws: false,
42+
})) as SocketSdkGenericResult<unknown>
43+
44+
expect(result.success).toBe(false)
45+
if (!result.success) {
46+
expect(result.status).toBe(400)
47+
expect(result.cause).toContain('Bad request')
48+
expect(result.cause).toContain('Invalid parameters')
49+
expect(result.cause).toContain('required parameters are provided')
50+
}
51+
})
52+
})
53+
54+
describe('413 Payload Too Large', () => {
55+
it('should provide actionable guidance for 413 errors', async () => {
56+
const result = (await getClient().getApi('/413-payload-too-large', {
57+
throws: false,
58+
})) as SocketSdkGenericResult<unknown>
59+
60+
expect(result.success).toBe(false)
61+
if (!result.success) {
62+
expect(result.status).toBe(413)
63+
expect(result.cause).toContain('Payload too large')
64+
expect(result.cause).toContain('Request exceeds size limits')
65+
expect(result.cause).toContain('Reduce the number of files')
66+
}
67+
})
68+
})
69+
70+
describe('429 Rate Limit with Retry-After', () => {
71+
it('should parse Retry-After header and include in guidance', async () => {
72+
const result = (await getClient().getApi('/429-with-retry-after', {
73+
throws: false,
74+
})) as SocketSdkGenericResult<unknown>
75+
76+
expect(result.success).toBe(false)
77+
if (!result.success) {
78+
expect(result.status).toBe(429)
79+
expect(result.cause).toContain('Rate limit exceeded')
80+
expect(result.cause).toContain('Retry after 60 seconds')
81+
}
82+
})
83+
})
84+
})

0 commit comments

Comments
 (0)