-
Notifications
You must be signed in to change notification settings - Fork 114
Expand file tree
/
Copy pathhttpTransport.spec.ts
More file actions
239 lines (213 loc) · 8.62 KB
/
Copy pathhttpTransport.spec.ts
File metadata and controls
239 lines (213 loc) · 8.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import * as http from 'http'
import FormData from 'form-data'
import { Client } from '../../index'
import { HttpMethod, RequestContext } from '../../codegen/crm/contacts/http/http'
import { IsomorphicFetchHttpLibrary } from '../../codegen/crm/contacts/http/isomorphic-fetch'
import { createConfiguration } from '../../codegen/files/configuration'
import { FilesApiRequestFactory } from '../../codegen/files/apis/FilesApi'
import { IsomorphicFetchHttpLibrary as FilesHttpLibrary } from '../../codegen/files/http/isomorphic-fetch'
import { ServerConfiguration } from '../../codegen/files/servers'
interface ICapturedRequest {
method: string
url: string
headers: http.IncomingHttpHeaders
body: Buffer
}
describe('HTTP transport', () => {
it('sends apiRequest JSON bodies through native fetch', async () => {
let capturedRequest: ICapturedRequest | undefined
const server = await createServer(async (request: ICapturedRequest, response: http.ServerResponse) => {
capturedRequest = request
response.statusCode = 201
response.setHeader('content-type', 'application/json')
response.end(JSON.stringify({ ok: true }))
})
try {
const client = new Client({ accessToken: 'test-token' })
const response = await client.apiRequest({
method: 'POST',
overlapUrl: `${server.baseUrl}/json?foo=bar`,
body: { hello: 'world' },
})
expect(response.status).toBe(201)
expect(await response.json()).toEqual({ ok: true })
expect(capturedRequest).toBeDefined()
const req = capturedRequest as ICapturedRequest
expect(req.method).toBe('POST')
expect(req.url).toBe('/json?foo=bar')
expect(req.headers.authorization).toBe('Bearer test-token')
expect(String(req.headers['content-type'])).toContain('application/json')
expect(req.body.toString()).toBe(JSON.stringify({ hello: 'world' }))
} finally {
await server.close()
}
})
it('preserves multipart apiRequest uploads with the form-data package', async () => {
let capturedRequest: ICapturedRequest | undefined
const server = await createServer(async (request: ICapturedRequest, response: http.ServerResponse) => {
capturedRequest = request
response.statusCode = 204
response.end()
})
try {
const formData = new FormData()
formData.append('field', 'value')
formData.append('file', Buffer.from('payload'), { filename: 'hello.txt', contentType: 'text/plain' })
const client = new Client()
const response = await client.apiRequest({
method: 'POST',
overlapUrl: `${server.baseUrl}/upload`,
body: formData,
defaultJson: false,
})
expect(response.status).toBe(204)
expect(capturedRequest).toBeDefined()
const req = capturedRequest as ICapturedRequest
expect(String(req.headers['content-type'])).toContain('multipart/form-data; boundary=')
expect(req.body.toString()).toContain('name="field"')
expect(req.body.toString()).toContain('value')
expect(req.body.toString()).toContain('filename="hello.txt"')
expect(req.body.toString()).toContain('payload')
} finally {
await server.close()
}
})
it('supports WHATWG FormData when form-data is provided by a runtime shim', async () => {
let capturedRequest: ICapturedRequest | undefined
const server = await createServer(async (request: ICapturedRequest, response: http.ServerResponse) => {
capturedRequest = request
response.statusCode = 204
response.end()
})
const nativeFormData = new globalThis.FormData()
nativeFormData.append('field', 'value')
const originalHasInstance = Object.getOwnPropertyDescriptor(FormData, Symbol.hasInstance)
Object.defineProperty(FormData, Symbol.hasInstance, {
configurable: true,
value: (body: unknown) => body instanceof globalThis.FormData,
})
try {
const client = new Client()
const response = await client.apiRequest({
method: 'POST',
overlapUrl: `${server.baseUrl}/upload`,
body: nativeFormData,
defaultJson: false,
})
expect(response.status).toBe(204)
expect(capturedRequest).toBeDefined()
const req = capturedRequest as ICapturedRequest
expect(String(req.headers['content-type'])).toContain('multipart/form-data; boundary=')
expect(req.body.toString()).toContain('name="field"')
expect(req.body.toString()).toContain('value')
} finally {
if (originalHasInstance) {
Object.defineProperty(FormData, Symbol.hasInstance, originalHasInstance)
} else {
Reflect.deleteProperty(FormData, Symbol.hasInstance)
}
await server.close()
}
})
it('returns generated ResponseContext bodies through native fetch', async () => {
let capturedRequest: ICapturedRequest | undefined
const server = await createServer(async (request: ICapturedRequest, response: http.ServerResponse) => {
capturedRequest = request
response.statusCode = 202
response.setHeader('content-type', 'application/octet-stream')
response.end(Buffer.from('generated-response'))
})
try {
const requestContext = new RequestContext(`${server.baseUrl}/generated`, HttpMethod.POST)
requestContext.setHeaderParam('X-Test', '1')
requestContext.setBody('payload')
const response = await new IsomorphicFetchHttpLibrary().send(requestContext).toPromise()
expect(response.httpStatusCode).toBe(202)
expect(capturedRequest).toBeDefined()
const req = capturedRequest as ICapturedRequest
expect(req.method).toBe('POST')
expect(req.headers['x-test']).toBe('1')
expect(req.body.toString()).toBe('payload')
expect(await response.body.text()).toBe('generated-response')
expect((await response.body.binary()).toString()).toBe('generated-response')
} finally {
await server.close()
}
})
it('supports generated multipart uploads when a custom httpAgent is configured', async () => {
let capturedRequest: ICapturedRequest | undefined
const server = await createServer(async (request: ICapturedRequest, response: http.ServerResponse) => {
capturedRequest = request
response.statusCode = 200
response.end('ok')
})
try {
const configuration = createConfiguration({
baseServer: new ServerConfiguration(server.baseUrl, {}),
})
const requestContext = await new FilesApiRequestFactory(configuration).upload(
{ data: Buffer.from('payload'), name: 'hello.txt' },
undefined,
'/folder',
'hello.txt',
)
requestContext.setAgent(new http.Agent({ keepAlive: true }))
const response = await new FilesHttpLibrary().send(requestContext).toPromise()
expect(response.httpStatusCode).toBe(200)
expect(capturedRequest).toBeDefined()
const req = capturedRequest as ICapturedRequest
expect(req.method).toBe('POST')
expect(req.url).toBe('/files/v3/files')
expect(String(req.headers['content-type'])).toContain('multipart/form-data; boundary=')
expect(req.body.toString()).toContain('filename="hello.txt"')
expect(req.body.toString()).toContain('name="folderPath"')
expect(req.body.toString()).toContain('/folder')
expect(req.body.toString()).toContain('payload')
expect(await response.body.text()).toBe('ok')
} finally {
await server.close()
}
})
})
async function createServer(
handler: (request: ICapturedRequest, response: http.ServerResponse) => Promise<void> | void,
): Promise<{ baseUrl: string; close(): Promise<void> }> {
const server = http.createServer(async (request: http.IncomingMessage, response: http.ServerResponse) => {
const bodyChunks: Buffer[] = []
request.on('data', (chunk: Buffer | string) => {
bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
})
request.on('end', async () => {
await handler(
{
body: Buffer.concat(bodyChunks),
headers: request.headers,
method: request.method ?? 'GET',
url: request.url ?? '/',
},
response,
)
})
})
await new Promise<void>((resolve) => {
server.listen(0, '127.0.0.1', () => resolve(undefined))
})
const address = server.address()
if (!address || typeof address === 'string') {
throw new Error('Failed to start test server.')
}
return {
baseUrl: `http://127.0.0.1:${address.port}`,
close: async () => {
await new Promise<void>((resolve, reject) => {
server.close((error) => {
if (error) {
reject(error)
return
}
resolve(undefined)
})
})
},
}
}