@vafast/api-client 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +282 -0
- package/build.ts +37 -0
- package/bun.lock +569 -0
- package/example/index.ts +255 -0
- package/package.json +53 -0
- package/src/core/api-client.ts +389 -0
- package/src/core/typed-client.ts +305 -0
- package/src/index.ts +73 -0
- package/src/types/index.ts +133 -0
- package/src/utils/index.ts +232 -0
- package/src/websocket/websocket-client.ts +347 -0
- package/test/api-client.test.ts +262 -0
- package/test/basic.test.ts +55 -0
- package/test/typed-client.test.ts +304 -0
- package/test/utils.test.ts +363 -0
- package/test/websocket.test.ts +434 -0
- package/tsconfig.dts.json +20 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +23 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, mock } from 'bun:test'
|
|
2
|
+
import { VafastApiClient } from '../src'
|
|
3
|
+
|
|
4
|
+
// Mock fetch
|
|
5
|
+
const mockFetch = mock(() =>
|
|
6
|
+
Promise.resolve(new Response(JSON.stringify({ success: true }), {
|
|
7
|
+
status: 200,
|
|
8
|
+
headers: { 'Content-Type': 'application/json' }
|
|
9
|
+
}))
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// Mock global fetch
|
|
13
|
+
global.fetch = mockFetch
|
|
14
|
+
|
|
15
|
+
describe('VafastApiClient', () => {
|
|
16
|
+
let client: VafastApiClient
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
client = new VafastApiClient({
|
|
20
|
+
baseURL: 'https://api.example.com',
|
|
21
|
+
timeout: 5000,
|
|
22
|
+
retries: 2
|
|
23
|
+
})
|
|
24
|
+
mockFetch.mockClear()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('Constructor', () => {
|
|
28
|
+
it('should create client with default config', () => {
|
|
29
|
+
const defaultClient = new VafastApiClient()
|
|
30
|
+
expect(defaultClient).toBeDefined()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should create client with custom config', () => {
|
|
34
|
+
expect(client).toBeDefined()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should merge config correctly', () => {
|
|
38
|
+
const customClient = new VafastApiClient({
|
|
39
|
+
baseURL: 'https://custom.api.com',
|
|
40
|
+
timeout: 10000
|
|
41
|
+
})
|
|
42
|
+
expect(customClient).toBeDefined()
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('Middleware Management', () => {
|
|
47
|
+
it('should add middleware', () => {
|
|
48
|
+
const middleware = {
|
|
49
|
+
name: 'test',
|
|
50
|
+
onRequest: async (req: Request) => req
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
client.addMiddleware(middleware)
|
|
54
|
+
expect(client).toBeDefined()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should remove middleware', () => {
|
|
58
|
+
const middleware = {
|
|
59
|
+
name: 'test',
|
|
60
|
+
onRequest: async (req: Request) => req
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
client.addMiddleware(middleware)
|
|
64
|
+
client.removeMiddleware('test')
|
|
65
|
+
expect(client).toBeDefined()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should handle non-existent middleware removal', () => {
|
|
69
|
+
client.removeMiddleware('non-existent')
|
|
70
|
+
expect(client).toBeDefined()
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe('Interceptor Management', () => {
|
|
75
|
+
it('should add interceptor', () => {
|
|
76
|
+
const interceptor = {
|
|
77
|
+
request: async (config: any) => config
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
client.addInterceptor(interceptor)
|
|
81
|
+
expect(client).toBeDefined()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('should remove interceptor', () => {
|
|
85
|
+
const interceptor = {
|
|
86
|
+
request: async (config: any) => config
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
client.addInterceptor(interceptor)
|
|
90
|
+
client.removeInterceptor(0)
|
|
91
|
+
expect(client).toBeDefined()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should handle invalid interceptor index', () => {
|
|
95
|
+
client.removeInterceptor(-1)
|
|
96
|
+
client.removeInterceptor(999)
|
|
97
|
+
expect(client).toBeDefined()
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
describe('Cache Management', () => {
|
|
102
|
+
it('should clear cache', () => {
|
|
103
|
+
client.clearCache()
|
|
104
|
+
const stats = client.getCacheStats()
|
|
105
|
+
expect(stats.size).toBe(0)
|
|
106
|
+
expect(stats.keys).toEqual([])
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should get cache stats', () => {
|
|
110
|
+
const stats = client.getCacheStats()
|
|
111
|
+
expect(stats).toHaveProperty('size')
|
|
112
|
+
expect(stats).toHaveProperty('keys')
|
|
113
|
+
expect(Array.isArray(stats.keys)).toBe(true)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe('Configuration Methods', () => {
|
|
118
|
+
it('should set cache config', () => {
|
|
119
|
+
const result = client.setCacheConfig({
|
|
120
|
+
enabled: true,
|
|
121
|
+
ttl: 300000,
|
|
122
|
+
maxSize: 100,
|
|
123
|
+
strategy: 'memory'
|
|
124
|
+
})
|
|
125
|
+
expect(result).toBe(client)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should set retry config', () => {
|
|
129
|
+
const result = client.setRetryConfig({
|
|
130
|
+
enabled: true,
|
|
131
|
+
maxRetries: 5,
|
|
132
|
+
retryDelay: 1000,
|
|
133
|
+
backoffMultiplier: 2,
|
|
134
|
+
retryableStatuses: [500, 502, 503]
|
|
135
|
+
})
|
|
136
|
+
expect(result).toBe(client)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should set log config', () => {
|
|
140
|
+
const result = client.setLogConfig({
|
|
141
|
+
enabled: true,
|
|
142
|
+
level: 'info',
|
|
143
|
+
format: 'json',
|
|
144
|
+
includeHeaders: true,
|
|
145
|
+
includeBody: false
|
|
146
|
+
})
|
|
147
|
+
expect(result).toBe(client)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe('HTTP Methods', () => {
|
|
152
|
+
it('should make GET request', async () => {
|
|
153
|
+
const response = await client.get('/users', { page: 1, limit: 10 })
|
|
154
|
+
expect(response).toBeDefined()
|
|
155
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should make POST request', async () => {
|
|
159
|
+
const response = await client.post('/users', { name: 'John', email: 'john@example.com' })
|
|
160
|
+
expect(response).toBeDefined()
|
|
161
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should make PUT request', async () => {
|
|
165
|
+
const response = await client.put('/users/123', { name: 'John Updated' })
|
|
166
|
+
expect(response).toBeDefined()
|
|
167
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('should make DELETE request', async () => {
|
|
171
|
+
const response = await client.delete('/users/123')
|
|
172
|
+
expect(response).toBeDefined()
|
|
173
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should make PATCH request', async () => {
|
|
177
|
+
const response = await client.patch('/users/123', { name: 'John Patched' })
|
|
178
|
+
expect(response).toBeDefined()
|
|
179
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should make HEAD request', async () => {
|
|
183
|
+
const response = await client.head('/users')
|
|
184
|
+
expect(response).toBeDefined()
|
|
185
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should make OPTIONS request', async () => {
|
|
189
|
+
const response = await client.options('/users')
|
|
190
|
+
expect(response).toBeDefined()
|
|
191
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
describe('Request Configuration', () => {
|
|
196
|
+
it('should handle custom headers', async () => {
|
|
197
|
+
const response = await client.get('/users', undefined, {
|
|
198
|
+
headers: { 'X-Custom-Header': 'test' }
|
|
199
|
+
})
|
|
200
|
+
expect(response).toBeDefined()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('should handle timeout configuration', async () => {
|
|
204
|
+
const response = await client.get('/users', undefined, {
|
|
205
|
+
timeout: 10000
|
|
206
|
+
})
|
|
207
|
+
expect(response).toBeDefined()
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('should handle retry configuration', async () => {
|
|
211
|
+
const response = await client.get('/users', undefined, {
|
|
212
|
+
retries: 5,
|
|
213
|
+
retryDelay: 2000
|
|
214
|
+
})
|
|
215
|
+
expect(response).toBeDefined()
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
describe('Error Handling', () => {
|
|
220
|
+
it('should handle network errors gracefully', async () => {
|
|
221
|
+
// Mock fetch to throw error
|
|
222
|
+
const errorFetch = mock(() => Promise.reject(new Error('Network error')))
|
|
223
|
+
global.fetch = errorFetch
|
|
224
|
+
|
|
225
|
+
const response = await client.get('/users')
|
|
226
|
+
expect(response.error).toBeDefined()
|
|
227
|
+
expect(response.data).toBeNull()
|
|
228
|
+
|
|
229
|
+
// Restore original mock
|
|
230
|
+
global.fetch = mockFetch
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('should handle HTTP error statuses', async () => {
|
|
234
|
+
const errorFetch = mock(() =>
|
|
235
|
+
Promise.resolve(new Response('Not Found', { status: 404 }))
|
|
236
|
+
)
|
|
237
|
+
global.fetch = errorFetch
|
|
238
|
+
|
|
239
|
+
const response = await client.get('/users')
|
|
240
|
+
expect(response.status).toBe(404)
|
|
241
|
+
|
|
242
|
+
// Restore original mock
|
|
243
|
+
global.fetch = mockFetch
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
describe('URL Building', () => {
|
|
248
|
+
it('should build URLs correctly with baseURL', async () => {
|
|
249
|
+
const customClient = new VafastApiClient({
|
|
250
|
+
baseURL: 'https://api.example.com'
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
await customClient.get('/users')
|
|
254
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('should handle absolute URLs', async () => {
|
|
258
|
+
await client.get('https://external.api.com/users')
|
|
259
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach } from 'bun:test'
|
|
2
|
+
import { VafastApiClient } from '../src'
|
|
3
|
+
|
|
4
|
+
describe('VafastApiClient', () => {
|
|
5
|
+
let client: VafastApiClient
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
client = new VafastApiClient({
|
|
9
|
+
baseURL: 'https://api.example.com',
|
|
10
|
+
timeout: 5000,
|
|
11
|
+
retries: 2
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should create client with default config', () => {
|
|
16
|
+
const defaultClient = new VafastApiClient()
|
|
17
|
+
expect(defaultClient).toBeDefined()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should create client with custom config', () => {
|
|
21
|
+
expect(client).toBeDefined()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should add and remove middleware', () => {
|
|
25
|
+
const middleware = {
|
|
26
|
+
name: 'test',
|
|
27
|
+
onRequest: async (req: Request) => req
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
client.addMiddleware(middleware)
|
|
31
|
+
expect(client.getCacheStats()).toBeDefined()
|
|
32
|
+
|
|
33
|
+
client.removeMiddleware('test')
|
|
34
|
+
expect(client.getCacheStats()).toBeDefined()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should add and remove interceptor', () => {
|
|
38
|
+
const interceptor = {
|
|
39
|
+
request: async (config: any) => config
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
client.addInterceptor(interceptor)
|
|
43
|
+
expect(client.getCacheStats()).toBeDefined()
|
|
44
|
+
|
|
45
|
+
client.removeInterceptor(0)
|
|
46
|
+
expect(client.getCacheStats()).toBeDefined()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should clear cache', () => {
|
|
50
|
+
client.clearCache()
|
|
51
|
+
const stats = client.getCacheStats()
|
|
52
|
+
expect(stats.size).toBe(0)
|
|
53
|
+
expect(stats.keys).toEqual([])
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, mock } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
createTypedClient,
|
|
4
|
+
createRouteBasedClient,
|
|
5
|
+
createSimpleClient,
|
|
6
|
+
type TypedApiClient
|
|
7
|
+
} from '../src'
|
|
8
|
+
|
|
9
|
+
// Mock VafastApiClient
|
|
10
|
+
const MockVafastApiClient = mock(() => ({
|
|
11
|
+
get: mock(() => Promise.resolve({ data: 'test', error: null, status: 200, headers: new Headers(), response: new Response() })),
|
|
12
|
+
post: mock(() => Promise.resolve({ data: 'created', error: null, status: 201, headers: new Headers(), response: new Response() })),
|
|
13
|
+
put: mock(() => Promise.resolve({ data: 'updated', error: null, status: 200, headers: new Headers(), response: new Response() })),
|
|
14
|
+
delete: mock(() => Promise.resolve({ data: 'deleted', error: null, status: 200, headers: new Headers(), response: new Response() })),
|
|
15
|
+
patch: mock(() => Promise.resolve({ data: 'patched', error: null, status: 200, headers: new Headers(), response: new Response() })),
|
|
16
|
+
head: mock(() => Promise.resolve({ data: null, error: null, status: 200, headers: new Headers(), response: new Response() })),
|
|
17
|
+
options: mock(() => Promise.resolve({ data: null, error: null, status: 200, headers: new Headers(), response: new Response() }))
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
// Mock server type
|
|
21
|
+
interface MockServer {
|
|
22
|
+
routes: {
|
|
23
|
+
'/users': {
|
|
24
|
+
GET: { query: { page?: number; limit?: number } }
|
|
25
|
+
POST: { body: { name: string; email: string } }
|
|
26
|
+
}
|
|
27
|
+
'/users/:id': {
|
|
28
|
+
GET: { params: { id: string } }
|
|
29
|
+
PUT: { params: { id: string }; body: Partial<{ name: string; email: string }> }
|
|
30
|
+
DELETE: { params: { id: string } }
|
|
31
|
+
}
|
|
32
|
+
'/posts': {
|
|
33
|
+
GET: { query: { author?: string; category?: string } }
|
|
34
|
+
POST: { body: { title: string; content: string; authorId: string } }
|
|
35
|
+
}
|
|
36
|
+
'/posts/:id': {
|
|
37
|
+
GET: { params: { id: string } }
|
|
38
|
+
PUT: { params: { id: string }; body: Partial<{ title: string; content: string }> }
|
|
39
|
+
DELETE: { params: { id: string } }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe('Typed Client', () => {
|
|
45
|
+
let mockServer: MockServer
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
mockServer = {
|
|
49
|
+
routes: {
|
|
50
|
+
'/users': {
|
|
51
|
+
GET: { query: { page: 1, limit: 10 } },
|
|
52
|
+
POST: { body: { name: 'John', email: 'john@example.com' } }
|
|
53
|
+
},
|
|
54
|
+
'/users/:id': {
|
|
55
|
+
GET: { params: { id: '123' } },
|
|
56
|
+
PUT: { params: { id: '123' }, body: { name: 'John Updated' } },
|
|
57
|
+
DELETE: { params: { id: '123' } }
|
|
58
|
+
},
|
|
59
|
+
'/posts': {
|
|
60
|
+
GET: { query: { author: 'user123', category: 'tech' } },
|
|
61
|
+
POST: { body: { title: 'Test Post', content: 'Content', authorId: 'user123' } }
|
|
62
|
+
},
|
|
63
|
+
'/posts/:id': {
|
|
64
|
+
GET: { params: { id: '456' } },
|
|
65
|
+
PUT: { params: { id: '456' }, body: { title: 'Updated Post' } },
|
|
66
|
+
DELETE: { params: { id: '456' } }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('createTypedClient', () => {
|
|
73
|
+
it('should create typed client from server', () => {
|
|
74
|
+
const typedClient = createTypedClient<MockServer>(mockServer as any, {
|
|
75
|
+
baseURL: 'https://api.example.com'
|
|
76
|
+
})
|
|
77
|
+
expect(typedClient).toBeDefined()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should support HTTP method calls', async () => {
|
|
81
|
+
const typedClient = createTypedClient<MockServer>(mockServer as any)
|
|
82
|
+
|
|
83
|
+
// Test GET method
|
|
84
|
+
const getResponse = await typedClient.get('/users', { page: 1, limit: 10 })
|
|
85
|
+
expect(getResponse).toBeDefined()
|
|
86
|
+
// Note: The actual response will depend on the implementation
|
|
87
|
+
expect(getResponse).toHaveProperty('data')
|
|
88
|
+
|
|
89
|
+
// Test POST method
|
|
90
|
+
const postResponse = await typedClient.post('/users', { name: 'Jane', email: 'jane@example.com' })
|
|
91
|
+
expect(postResponse).toBeDefined()
|
|
92
|
+
expect(postResponse).toHaveProperty('data')
|
|
93
|
+
|
|
94
|
+
// Test PUT method
|
|
95
|
+
const putResponse = await typedClient.put('/users/123', { name: 'Jane Updated' })
|
|
96
|
+
expect(putResponse).toBeDefined()
|
|
97
|
+
expect(putResponse).toHaveProperty('data')
|
|
98
|
+
|
|
99
|
+
// Test DELETE method
|
|
100
|
+
const deleteResponse = await typedClient.delete('/users/123')
|
|
101
|
+
expect(deleteResponse).toBeDefined()
|
|
102
|
+
expect(deleteResponse).toHaveProperty('data')
|
|
103
|
+
|
|
104
|
+
// Test PATCH method
|
|
105
|
+
const patchResponse = await typedClient.patch('/users/123', { name: 'Jane Patched' })
|
|
106
|
+
expect(patchResponse).toBeDefined()
|
|
107
|
+
expect(patchResponse).toHaveProperty('data')
|
|
108
|
+
|
|
109
|
+
// Test HEAD method
|
|
110
|
+
const headResponse = await typedClient.head('/users')
|
|
111
|
+
expect(headResponse).toBeDefined()
|
|
112
|
+
expect(headResponse).toHaveProperty('data')
|
|
113
|
+
|
|
114
|
+
// Test OPTIONS method
|
|
115
|
+
const optionsResponse = await typedClient.options('/users')
|
|
116
|
+
expect(optionsResponse).toBeDefined()
|
|
117
|
+
expect(optionsResponse).toHaveProperty('data')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should support path-based calls', async () => {
|
|
121
|
+
const typedClient = createTypedClient<MockServer>(mockServer as any)
|
|
122
|
+
|
|
123
|
+
// Test path segments
|
|
124
|
+
const usersClient = (typedClient as any).users
|
|
125
|
+
expect(usersClient).toBeDefined()
|
|
126
|
+
|
|
127
|
+
// Test nested path segments
|
|
128
|
+
const postsClient = (typedClient as any).posts
|
|
129
|
+
expect(postsClient).toBeDefined()
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should handle dynamic path parameters', async () => {
|
|
133
|
+
const typedClient = createTypedClient<MockServer>(mockServer as any)
|
|
134
|
+
|
|
135
|
+
// Test dynamic path with parameters
|
|
136
|
+
const userClient = (typedClient as any).users
|
|
137
|
+
if (typeof userClient === 'function') {
|
|
138
|
+
const response = await userClient({ id: '123' })
|
|
139
|
+
expect(response).toBeDefined()
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
describe('createRouteBasedClient', () => {
|
|
145
|
+
it('should create route-based client', () => {
|
|
146
|
+
const routeClient = createRouteBasedClient<MockServer>(mockServer as any, {
|
|
147
|
+
baseURL: 'https://api.example.com'
|
|
148
|
+
})
|
|
149
|
+
expect(routeClient).toBeDefined()
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('should support dynamic path handling', async () => {
|
|
153
|
+
const routeClient = createRouteBasedClient<MockServer>(mockServer as any)
|
|
154
|
+
|
|
155
|
+
// Test dynamic path creation
|
|
156
|
+
const dynamicPath = (routeClient as any).users
|
|
157
|
+
expect(dynamicPath).toBeDefined()
|
|
158
|
+
|
|
159
|
+
if (typeof dynamicPath === 'function') {
|
|
160
|
+
const response = await dynamicPath({ id: '123' })
|
|
161
|
+
expect(response).toBeDefined()
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
describe('createSimpleClient', () => {
|
|
167
|
+
it('should create simple client', () => {
|
|
168
|
+
const simpleClient = createSimpleClient<MockServer>(mockServer as any, {
|
|
169
|
+
baseURL: 'https://api.example.com'
|
|
170
|
+
})
|
|
171
|
+
expect(simpleClient).toBeDefined()
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should have all HTTP methods', () => {
|
|
175
|
+
const simpleClient = createSimpleClient<MockServer>(mockServer as any)
|
|
176
|
+
|
|
177
|
+
expect(typeof simpleClient.get).toBe('function')
|
|
178
|
+
expect(typeof simpleClient.post).toBe('function')
|
|
179
|
+
expect(typeof simpleClient.put).toBe('function')
|
|
180
|
+
expect(typeof simpleClient.delete).toBe('function')
|
|
181
|
+
expect(typeof simpleClient.patch).toBe('function')
|
|
182
|
+
expect(typeof simpleClient.head).toBe('function')
|
|
183
|
+
expect(typeof simpleClient.options).toBe('function')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('should make HTTP requests correctly', async () => {
|
|
187
|
+
const simpleClient = createSimpleClient<MockServer>(mockServer as any)
|
|
188
|
+
|
|
189
|
+
// Test GET request
|
|
190
|
+
const getResponse = await simpleClient.get('/users', { page: 1, limit: 10 })
|
|
191
|
+
expect(getResponse).toBeDefined()
|
|
192
|
+
expect(getResponse).toHaveProperty('data')
|
|
193
|
+
|
|
194
|
+
// Test POST request
|
|
195
|
+
const postResponse = await simpleClient.post('/users', { name: 'John', email: 'john@example.com' })
|
|
196
|
+
expect(postResponse).toBeDefined()
|
|
197
|
+
expect(postResponse).toHaveProperty('data')
|
|
198
|
+
|
|
199
|
+
// Test PUT request
|
|
200
|
+
const putResponse = await simpleClient.put('/users/123', { name: 'John Updated' })
|
|
201
|
+
expect(putResponse).toBeDefined()
|
|
202
|
+
expect(putResponse).toHaveProperty('data')
|
|
203
|
+
|
|
204
|
+
// Test DELETE request
|
|
205
|
+
const deleteResponse = await simpleClient.delete('/users/123')
|
|
206
|
+
expect(deleteResponse).toBeDefined()
|
|
207
|
+
expect(deleteResponse).toHaveProperty('data')
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('should handle query parameters', async () => {
|
|
211
|
+
const simpleClient = createSimpleClient<MockServer>(mockServer as any)
|
|
212
|
+
|
|
213
|
+
const response = await simpleClient.get('/users', { page: 2, limit: 20, search: 'john' })
|
|
214
|
+
expect(response).toBeDefined()
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
it('should handle request body', async () => {
|
|
218
|
+
const simpleClient = createSimpleClient<MockServer>(mockServer as any)
|
|
219
|
+
|
|
220
|
+
const response = await simpleClient.post('/users', {
|
|
221
|
+
name: 'Jane Doe',
|
|
222
|
+
email: 'jane@example.com',
|
|
223
|
+
age: 25
|
|
224
|
+
})
|
|
225
|
+
expect(response).toBeDefined()
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('should handle custom request config', async () => {
|
|
229
|
+
const simpleClient = createSimpleClient<MockServer>(mockServer as any)
|
|
230
|
+
|
|
231
|
+
const response = await simpleClient.get('/users', { page: 1, limit: 10 }, {
|
|
232
|
+
headers: { 'X-Custom-Header': 'test' },
|
|
233
|
+
timeout: 10000
|
|
234
|
+
})
|
|
235
|
+
expect(response).toBeDefined()
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
describe('Type Safety', () => {
|
|
240
|
+
it('should maintain type safety for server types', () => {
|
|
241
|
+
// This test ensures TypeScript compilation works correctly
|
|
242
|
+
const typedClient: TypedApiClient<MockServer> = createTypedClient<MockServer>(mockServer as any)
|
|
243
|
+
expect(typedClient).toBeDefined()
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('should support generic type constraints', () => {
|
|
247
|
+
// Test with different server types
|
|
248
|
+
interface SimpleServer {
|
|
249
|
+
routes: {
|
|
250
|
+
'/health': {
|
|
251
|
+
GET: {}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const simpleServer: SimpleServer = {
|
|
257
|
+
routes: {
|
|
258
|
+
'/health': { GET: {} }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const client = createTypedClient<SimpleServer>(simpleServer as any)
|
|
263
|
+
expect(client).toBeDefined()
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
describe('Error Handling', () => {
|
|
268
|
+
it('should handle client creation errors gracefully', () => {
|
|
269
|
+
// Test with invalid server
|
|
270
|
+
const invalidClient = createTypedClient(null as any)
|
|
271
|
+
expect(invalidClient).toBeDefined()
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should handle missing routes gracefully', () => {
|
|
275
|
+
const emptyServer = { routes: {} }
|
|
276
|
+
const client = createTypedClient(emptyServer as any)
|
|
277
|
+
expect(client).toBeDefined()
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
describe('Integration', () => {
|
|
282
|
+
it('should work with different server configurations', () => {
|
|
283
|
+
const configs = [
|
|
284
|
+
{ baseURL: 'https://api1.example.com' },
|
|
285
|
+
{ baseURL: 'https://api2.example.com', timeout: 5000 },
|
|
286
|
+
{ baseURL: 'https://api3.example.com', retries: 3 }
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
configs.forEach(config => {
|
|
290
|
+
const client = createSimpleClient<MockServer>(mockServer as any, config)
|
|
291
|
+
expect(client).toBeDefined()
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('should support method chaining patterns', () => {
|
|
296
|
+
const client = createSimpleClient<MockServer>(mockServer as any)
|
|
297
|
+
|
|
298
|
+
// Test that methods return the client for chaining
|
|
299
|
+
expect(client).toBeDefined()
|
|
300
|
+
expect(typeof client.get).toBe('function')
|
|
301
|
+
expect(typeof client.post).toBe('function')
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
})
|