cloudflare/cloudflare-typescript

Public

mirrored fromhttps://github.com/cloudflare/cloudflare-typescriptAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
next

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tests/index.test.ts

543lines · modecode

1// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
3import Cloudflare from 'cloudflare';
4import { APIUserAbortError } from 'cloudflare';
5import { Headers } from 'cloudflare/core';
6import defaultFetch, { Response, type RequestInit, type RequestInfo } from 'node-fetch';
7
8describe('instantiate client', () => {
9 const env = process.env;
10
11 beforeEach(() => {
12 jest.resetModules();
13 process.env = { ...env };
14
15 console.warn = jest.fn();
16 });
17
18 afterEach(() => {
19 process.env = env;
20 });
21
22 describe('defaultHeaders', () => {
23 const client = new Cloudflare({
24 baseURL: 'http://localhost:5000/',
25 defaultHeaders: { 'X-My-Default-Header': '2' },
26 apiKey: '144c9defac04969c7bfad8efaa8ea194',
27 apiEmail: 'user@example.com',
28 });
29
30 test('they are used in the request', async () => {
31 const { req } = await client.buildRequest({ path: '/foo', method: 'post' });
32 expect((req.headers as Headers)['x-my-default-header']).toEqual('2');
33 });
34
35 test('can ignore `undefined` and leave the default', async () => {
36 const { req } = await client.buildRequest({
37 path: '/foo',
38 method: 'post',
39 headers: { 'X-My-Default-Header': undefined },
40 });
41 expect((req.headers as Headers)['x-my-default-header']).toEqual('2');
42 });
43
44 test('can be removed with `null`', async () => {
45 const { req } = await client.buildRequest({
46 path: '/foo',
47 method: 'post',
48 headers: { 'X-My-Default-Header': null },
49 });
50 expect(req.headers as Headers).not.toHaveProperty('x-my-default-header');
51 });
52 });
53
54 describe('defaultQuery', () => {
55 test('with null query params given', () => {
56 const client = new Cloudflare({
57 baseURL: 'http://localhost:5000/',
58 defaultQuery: { apiVersion: 'foo' },
59 apiKey: '144c9defac04969c7bfad8efaa8ea194',
60 apiEmail: 'user@example.com',
61 });
62 expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo');
63 });
64
65 test('multiple default query params', () => {
66 const client = new Cloudflare({
67 baseURL: 'http://localhost:5000/',
68 defaultQuery: { apiVersion: 'foo', hello: 'world' },
69 apiKey: '144c9defac04969c7bfad8efaa8ea194',
70 apiEmail: 'user@example.com',
71 });
72 expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo&hello=world');
73 });
74
75 test('overriding with `undefined`', () => {
76 const client = new Cloudflare({
77 baseURL: 'http://localhost:5000/',
78 defaultQuery: { hello: 'world' },
79 apiKey: '144c9defac04969c7bfad8efaa8ea194',
80 apiEmail: 'user@example.com',
81 });
82 expect(client.buildURL('/foo', { hello: undefined })).toEqual('http://localhost:5000/foo');
83 });
84 });
85
86 test('custom fetch', async () => {
87 const client = new Cloudflare({
88 baseURL: 'http://localhost:5000/',
89 apiKey: '144c9defac04969c7bfad8efaa8ea194',
90 apiEmail: 'user@example.com',
91 fetch: (url) => {
92 return Promise.resolve(
93 new Response(JSON.stringify({ url, custom: true }), {
94 headers: { 'Content-Type': 'application/json' },
95 }),
96 );
97 },
98 });
99
100 const response = await client.get('/foo');
101 expect(response).toEqual({ url: 'http://localhost:5000/foo', custom: true });
102 });
103
104 test('explicit global fetch', async () => {
105 // make sure the global fetch type is assignable to our Fetch type
106 const client = new Cloudflare({
107 baseURL: 'http://localhost:5000/',
108 apiKey: '144c9defac04969c7bfad8efaa8ea194',
109 apiEmail: 'user@example.com',
110 fetch: defaultFetch,
111 });
112 });
113
114 test('custom signal', async () => {
115 const client = new Cloudflare({
116 baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
117 apiKey: '144c9defac04969c7bfad8efaa8ea194',
118 apiEmail: 'user@example.com',
119 fetch: (...args) => {
120 return new Promise((resolve, reject) =>
121 setTimeout(
122 () =>
123 defaultFetch(...args)
124 .then(resolve)
125 .catch(reject),
126 300,
127 ),
128 );
129 },
130 });
131
132 const controller = new AbortController();
133 setTimeout(() => controller.abort(), 200);
134
135 const spy = jest.spyOn(client, 'request');
136
137 await expect(client.get('/foo', { signal: controller.signal })).rejects.toThrowError(APIUserAbortError);
138 expect(spy).toHaveBeenCalledTimes(1);
139 });
140
141 test('normalized method', async () => {
142 let capturedRequest: RequestInit | undefined;
143 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
144 capturedRequest = init;
145 return new Response(JSON.stringify({}), { headers: { 'Content-Type': 'application/json' } });
146 };
147
148 const client = new Cloudflare({
149 baseURL: 'http://localhost:5000/',
150 apiKey: '144c9defac04969c7bfad8efaa8ea194',
151 apiEmail: 'user@example.com',
152 fetch: testFetch,
153 });
154
155 await client.patch('/foo');
156 expect(capturedRequest?.method).toEqual('PATCH');
157 });
158
159 describe('baseUrl', () => {
160 test('trailing slash', () => {
161 const client = new Cloudflare({
162 baseURL: 'http://localhost:5000/custom/path/',
163 apiKey: '144c9defac04969c7bfad8efaa8ea194',
164 apiEmail: 'user@example.com',
165 });
166 expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo');
167 });
168
169 test('no trailing slash', () => {
170 const client = new Cloudflare({
171 baseURL: 'http://localhost:5000/custom/path',
172 apiKey: '144c9defac04969c7bfad8efaa8ea194',
173 apiEmail: 'user@example.com',
174 });
175 expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo');
176 });
177
178 afterEach(() => {
179 process.env['CLOUDFLARE_BASE_URL'] = undefined;
180 });
181
182 test('explicit option', () => {
183 const client = new Cloudflare({
184 baseURL: 'https://example.com',
185 apiKey: '144c9defac04969c7bfad8efaa8ea194',
186 apiEmail: 'user@example.com',
187 });
188 expect(client.baseURL).toEqual('https://example.com');
189 });
190
191 test('env variable', () => {
192 process.env['CLOUDFLARE_BASE_URL'] = 'https://example.com/from_env';
193 const client = new Cloudflare({
194 apiKey: '144c9defac04969c7bfad8efaa8ea194',
195 apiEmail: 'user@example.com',
196 });
197 expect(client.baseURL).toEqual('https://example.com/from_env');
198 });
199
200 test('empty env variable', () => {
201 process.env['CLOUDFLARE_BASE_URL'] = ''; // empty
202 const client = new Cloudflare({
203 apiKey: '144c9defac04969c7bfad8efaa8ea194',
204 apiEmail: 'user@example.com',
205 });
206 expect(client.baseURL).toEqual('https://api.cloudflare.com/client/v4');
207 });
208
209 test('blank env variable', () => {
210 process.env['CLOUDFLARE_BASE_URL'] = ' '; // blank
211 const client = new Cloudflare({
212 apiKey: '144c9defac04969c7bfad8efaa8ea194',
213 apiEmail: 'user@example.com',
214 });
215 expect(client.baseURL).toEqual('https://api.cloudflare.com/client/v4');
216 });
217
218 test('in request options', () => {
219 const client = new Cloudflare({
220 apiKey: '144c9defac04969c7bfad8efaa8ea194',
221 apiEmail: 'user@example.com',
222 });
223 expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual(
224 'http://localhost:5000/option/foo',
225 );
226 });
227
228 test('in request options overridden by client options', () => {
229 const client = new Cloudflare({
230 apiKey: '144c9defac04969c7bfad8efaa8ea194',
231 apiEmail: 'user@example.com',
232 baseURL: 'http://localhost:5000/client',
233 });
234 expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual(
235 'http://localhost:5000/client/foo',
236 );
237 });
238
239 test('in request options overridden by env variable', () => {
240 process.env['CLOUDFLARE_BASE_URL'] = 'http://localhost:5000/env';
241 const client = new Cloudflare({
242 apiKey: '144c9defac04969c7bfad8efaa8ea194',
243 apiEmail: 'user@example.com',
244 });
245 expect(client.buildURL('/foo', null, 'http://localhost:5000/option')).toEqual(
246 'http://localhost:5000/env/foo',
247 );
248 });
249 });
250
251 test('maxRetries option is correctly set', () => {
252 const client = new Cloudflare({
253 maxRetries: 4,
254 apiKey: '144c9defac04969c7bfad8efaa8ea194',
255 apiEmail: 'user@example.com',
256 });
257 expect(client.maxRetries).toEqual(4);
258
259 // default
260 const client2 = new Cloudflare({
261 apiKey: '144c9defac04969c7bfad8efaa8ea194',
262 apiEmail: 'user@example.com',
263 });
264 expect(client2.maxRetries).toEqual(2);
265 });
266
267 test('with environment variable arguments', () => {
268 // set options via env var
269 process.env['CLOUDFLARE_API_KEY'] = '144c9defac04969c7bfad8efaa8ea194';
270 process.env['CLOUDFLARE_EMAIL'] = 'user@example.com';
271 const client = new Cloudflare();
272 expect(client.apiKey).toBe('144c9defac04969c7bfad8efaa8ea194');
273 expect(client.apiEmail).toBe('user@example.com');
274 });
275
276 test('with overridden environment variable arguments', () => {
277 // set options via env var
278 process.env['CLOUDFLARE_API_KEY'] = 'another 144c9defac04969c7bfad8efaa8ea194';
279 process.env['CLOUDFLARE_EMAIL'] = 'another user@example.com';
280 const client = new Cloudflare({
281 apiKey: '144c9defac04969c7bfad8efaa8ea194',
282 apiEmail: 'user@example.com',
283 });
284 expect(client.apiKey).toBe('144c9defac04969c7bfad8efaa8ea194');
285 expect(client.apiEmail).toBe('user@example.com');
286 });
287});
288
289describe('request building', () => {
290 const client = new Cloudflare({ apiKey: '144c9defac04969c7bfad8efaa8ea194', apiEmail: 'user@example.com' });
291
292 describe('Content-Length', () => {
293 test('handles multi-byte characters', async () => {
294 const { req } = await client.buildRequest({ path: '/foo', method: 'post', body: { value: '—' } });
295 expect((req.headers as Record<string, string>)['content-length']).toEqual('20');
296 });
297
298 test('handles standard characters', async () => {
299 const { req } = await client.buildRequest({ path: '/foo', method: 'post', body: { value: 'hello' } });
300 expect((req.headers as Record<string, string>)['content-length']).toEqual('22');
301 });
302 });
303
304 describe('custom headers', () => {
305 test('handles undefined', async () => {
306 const { req } = await client.buildRequest({
307 path: '/foo',
308 method: 'post',
309 body: { value: 'hello' },
310 headers: { 'X-Foo': 'baz', 'x-foo': 'bar', 'x-Foo': undefined, 'x-baz': 'bam', 'X-Baz': null },
311 });
312 expect((req.headers as Record<string, string>)['x-foo']).toEqual('bar');
313 expect((req.headers as Record<string, string>)['x-Foo']).toEqual(undefined);
314 expect((req.headers as Record<string, string>)['X-Foo']).toEqual(undefined);
315 expect((req.headers as Record<string, string>)['x-baz']).toEqual(undefined);
316 });
317 });
318});
319
320describe('retries', () => {
321 test('retry on timeout', async () => {
322 let count = 0;
323 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
324 if (count++ === 0) {
325 return new Promise(
326 (resolve, reject) => signal?.addEventListener('abort', () => reject(new Error('timed out'))),
327 );
328 }
329 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
330 };
331
332 const client = new Cloudflare({
333 apiKey: '144c9defac04969c7bfad8efaa8ea194',
334 apiEmail: 'user@example.com',
335 timeout: 10,
336 fetch: testFetch,
337 });
338
339 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
340 expect(count).toEqual(2);
341 expect(
342 await client
343 .request({ path: '/foo', method: 'get' })
344 .asResponse()
345 .then((r) => r.text()),
346 ).toEqual(JSON.stringify({ a: 1 }));
347 expect(count).toEqual(3);
348 });
349
350 test('retry count header', async () => {
351 let count = 0;
352 let capturedRequest: RequestInit | undefined;
353 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
354 count++;
355 if (count <= 2) {
356 return new Response(undefined, {
357 status: 429,
358 headers: {
359 'Retry-After': '0.1',
360 },
361 });
362 }
363 capturedRequest = init;
364 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
365 };
366
367 const client = new Cloudflare({
368 apiKey: '144c9defac04969c7bfad8efaa8ea194',
369 apiEmail: 'user@example.com',
370 fetch: testFetch,
371 maxRetries: 4,
372 });
373
374 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
375
376 expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toEqual('2');
377 expect(count).toEqual(3);
378 });
379
380 test('omit retry count header', async () => {
381 let count = 0;
382 let capturedRequest: RequestInit | undefined;
383 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
384 count++;
385 if (count <= 2) {
386 return new Response(undefined, {
387 status: 429,
388 headers: {
389 'Retry-After': '0.1',
390 },
391 });
392 }
393 capturedRequest = init;
394 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
395 };
396 const client = new Cloudflare({
397 apiKey: '144c9defac04969c7bfad8efaa8ea194',
398 apiEmail: 'user@example.com',
399 fetch: testFetch,
400 maxRetries: 4,
401 });
402
403 expect(
404 await client.request({
405 path: '/foo',
406 method: 'get',
407 headers: { 'X-Stainless-Retry-Count': null },
408 }),
409 ).toEqual({ a: 1 });
410
411 expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
412 });
413
414 test('omit retry count header by default', async () => {
415 let count = 0;
416 let capturedRequest: RequestInit | undefined;
417 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
418 count++;
419 if (count <= 2) {
420 return new Response(undefined, {
421 status: 429,
422 headers: {
423 'Retry-After': '0.1',
424 },
425 });
426 }
427 capturedRequest = init;
428 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
429 };
430 const client = new Cloudflare({
431 apiKey: '144c9defac04969c7bfad8efaa8ea194',
432 apiEmail: 'user@example.com',
433 fetch: testFetch,
434 maxRetries: 4,
435 defaultHeaders: { 'X-Stainless-Retry-Count': null },
436 });
437
438 expect(
439 await client.request({
440 path: '/foo',
441 method: 'get',
442 }),
443 ).toEqual({ a: 1 });
444
445 expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
446 });
447
448 test('overwrite retry count header', async () => {
449 let count = 0;
450 let capturedRequest: RequestInit | undefined;
451 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
452 count++;
453 if (count <= 2) {
454 return new Response(undefined, {
455 status: 429,
456 headers: {
457 'Retry-After': '0.1',
458 },
459 });
460 }
461 capturedRequest = init;
462 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
463 };
464 const client = new Cloudflare({
465 apiKey: '144c9defac04969c7bfad8efaa8ea194',
466 apiEmail: 'user@example.com',
467 fetch: testFetch,
468 maxRetries: 4,
469 });
470
471 expect(
472 await client.request({
473 path: '/foo',
474 method: 'get',
475 headers: { 'X-Stainless-Retry-Count': '42' },
476 }),
477 ).toEqual({ a: 1 });
478
479 expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toBe('42');
480 });
481
482 test('retry on 429 with retry-after', async () => {
483 let count = 0;
484 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
485 if (count++ === 0) {
486 return new Response(undefined, {
487 status: 429,
488 headers: {
489 'Retry-After': '0.1',
490 },
491 });
492 }
493 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
494 };
495
496 const client = new Cloudflare({
497 apiKey: '144c9defac04969c7bfad8efaa8ea194',
498 apiEmail: 'user@example.com',
499 fetch: testFetch,
500 });
501
502 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
503 expect(count).toEqual(2);
504 expect(
505 await client
506 .request({ path: '/foo', method: 'get' })
507 .asResponse()
508 .then((r) => r.text()),
509 ).toEqual(JSON.stringify({ a: 1 }));
510 expect(count).toEqual(3);
511 });
512
513 test('retry on 429 with retry-after-ms', async () => {
514 let count = 0;
515 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
516 if (count++ === 0) {
517 return new Response(undefined, {
518 status: 429,
519 headers: {
520 'Retry-After-Ms': '10',
521 },
522 });
523 }
524 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
525 };
526
527 const client = new Cloudflare({
528 apiKey: '144c9defac04969c7bfad8efaa8ea194',
529 apiEmail: 'user@example.com',
530 fetch: testFetch,
531 });
532
533 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
534 expect(count).toEqual(2);
535 expect(
536 await client
537 .request({ path: '/foo', method: 'get' })
538 .asResponse()
539 .then((r) => r.text()),
540 ).toEqual(JSON.stringify({ a: 1 }));
541 expect(count).toEqual(3);
542 });
543});