Skip to content

Commit ed8f0ee

Browse files
committed
test(sdk): add Retry-After header parsing tests
Add 5 tests for Retry-After header parsing variations: - Numeric seconds format - HTTP-date format - Zero seconds value - Past date (should be ignored) - Invalid format These tests improve coverage of #parseRetryAfter method branches.
1 parent 50d1a7e commit ed8f0ee

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/** @fileoverview Tests for Retry-After header parsing. */
2+
3+
import { describe, expect, it } from 'vitest'
4+
5+
import { SocketSdk } from '../../src/index'
6+
import {
7+
createRouteHandler,
8+
setupLocalHttpServer,
9+
} from '../utils/local-server-helpers.mts'
10+
11+
import type { IncomingMessage } from 'node:http'
12+
import type { SocketSdkGenericResult } from '../../src/types'
13+
14+
describe('SocketSdk - Retry-After Header Parsing', () => {
15+
describe('Retry-After with seconds (delay-seconds format)', () => {
16+
const getBaseUrl = setupLocalHttpServer(
17+
createRouteHandler({
18+
'/retry-seconds': (_req: IncomingMessage, res) => {
19+
res.writeHead(429, {
20+
'Content-Type': 'application/json',
21+
'Retry-After': '60',
22+
})
23+
res.end(JSON.stringify({ error: 'Rate limited' }))
24+
},
25+
}),
26+
)
27+
28+
const getClient = () =>
29+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 1 })
30+
31+
it('should parse numeric Retry-After header', async () => {
32+
const result = (await getClient().getApi('/retry-seconds', {
33+
throws: false,
34+
})) as SocketSdkGenericResult<unknown>
35+
36+
expect(result.success).toBe(false)
37+
if (!result.success) {
38+
expect(result.status).toBe(429)
39+
expect(result.cause).toContain('Retry after 60 seconds')
40+
}
41+
})
42+
})
43+
44+
describe('Retry-After with HTTP-date format', () => {
45+
const getBaseUrl = setupLocalHttpServer(
46+
createRouteHandler({
47+
'/retry-date': (_req: IncomingMessage, res) => {
48+
// Create a date 30 seconds in the future
49+
const futureDate = new Date(Date.now() + 30000)
50+
res.writeHead(429, {
51+
'Content-Type': 'application/json',
52+
'Retry-After': futureDate.toUTCString(),
53+
})
54+
res.end(JSON.stringify({ error: 'Rate limited' }))
55+
},
56+
}),
57+
)
58+
59+
const getClient = () =>
60+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 1 })
61+
62+
it('should parse HTTP-date Retry-After header', async () => {
63+
const result = (await getClient().getApi('/retry-date', {
64+
throws: false,
65+
})) as SocketSdkGenericResult<unknown>
66+
67+
expect(result.success).toBe(false)
68+
if (!result.success) {
69+
expect(result.status).toBe(429)
70+
}
71+
})
72+
})
73+
74+
describe('Retry-After with zero seconds', () => {
75+
const getBaseUrl = setupLocalHttpServer(
76+
createRouteHandler({
77+
'/retry-zero': (_req: IncomingMessage, res) => {
78+
res.writeHead(429, {
79+
'Content-Type': 'application/json',
80+
'Retry-After': '0',
81+
})
82+
res.end(JSON.stringify({ error: 'Rate limited' }))
83+
},
84+
}),
85+
)
86+
87+
const getClient = () =>
88+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 1 })
89+
90+
it('should handle zero seconds Retry-After', async () => {
91+
const result = (await getClient().getApi('/retry-zero', {
92+
throws: false,
93+
})) as SocketSdkGenericResult<unknown>
94+
95+
expect(result.success).toBe(false)
96+
if (!result.success) {
97+
expect(result.status).toBe(429)
98+
}
99+
})
100+
})
101+
102+
describe('Retry-After with invalid date (past)', () => {
103+
const getBaseUrl = setupLocalHttpServer(
104+
createRouteHandler({
105+
'/retry-past': (_req: IncomingMessage, res) => {
106+
// Date in the past should not be used
107+
const pastDate = new Date(Date.now() - 30000)
108+
res.writeHead(429, {
109+
'Content-Type': 'application/json',
110+
'Retry-After': pastDate.toUTCString(),
111+
})
112+
res.end(JSON.stringify({ error: 'Rate limited' }))
113+
},
114+
}),
115+
)
116+
117+
const getClient = () =>
118+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 1 })
119+
120+
it('should ignore past date in Retry-After', async () => {
121+
const result = (await getClient().getApi('/retry-past', {
122+
throws: false,
123+
})) as SocketSdkGenericResult<unknown>
124+
125+
expect(result.success).toBe(false)
126+
if (!result.success) {
127+
expect(result.status).toBe(429)
128+
}
129+
})
130+
})
131+
132+
describe('Retry-After with invalid format', () => {
133+
const getBaseUrl = setupLocalHttpServer(
134+
createRouteHandler({
135+
'/retry-invalid': (_req: IncomingMessage, res) => {
136+
res.writeHead(429, {
137+
'Content-Type': 'application/json',
138+
'Retry-After': 'invalid-value',
139+
})
140+
res.end(JSON.stringify({ error: 'Rate limited' }))
141+
},
142+
}),
143+
)
144+
145+
const getClient = () =>
146+
new SocketSdk('test-token', { baseUrl: getBaseUrl(), retries: 1 })
147+
148+
it('should handle invalid Retry-After value', async () => {
149+
const result = (await getClient().getApi('/retry-invalid', {
150+
throws: false,
151+
})) as SocketSdkGenericResult<unknown>
152+
153+
expect(result.success).toBe(false)
154+
if (!result.success) {
155+
expect(result.status).toBe(429)
156+
}
157+
})
158+
})
159+
})

0 commit comments

Comments
 (0)