cloudflare/cloudflare-typescript

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v4.2.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

tests/index.test.ts

511lines · 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', () => {
31 const { req } = 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', () => {
36 const { req } = 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`', () => {
45 const { req } = 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
219 test('maxRetries option is correctly set', () => {
220 const client = new Cloudflare({
221 maxRetries: 4,
222 apiKey: '144c9defac04969c7bfad8efaa8ea194',
223 apiEmail: 'user@example.com',
224 });
225 expect(client.maxRetries).toEqual(4);
226
227 // default
228 const client2 = new Cloudflare({
229 apiKey: '144c9defac04969c7bfad8efaa8ea194',
230 apiEmail: 'user@example.com',
231 });
232 expect(client2.maxRetries).toEqual(2);
233 });
234
235 test('with environment variable arguments', () => {
236 // set options via env var
237 process.env['CLOUDFLARE_API_KEY'] = '144c9defac04969c7bfad8efaa8ea194';
238 process.env['CLOUDFLARE_EMAIL'] = 'user@example.com';
239 const client = new Cloudflare();
240 expect(client.apiKey).toBe('144c9defac04969c7bfad8efaa8ea194');
241 expect(client.apiEmail).toBe('user@example.com');
242 });
243
244 test('with overridden environment variable arguments', () => {
245 // set options via env var
246 process.env['CLOUDFLARE_API_KEY'] = 'another 144c9defac04969c7bfad8efaa8ea194';
247 process.env['CLOUDFLARE_EMAIL'] = 'another user@example.com';
248 const client = new Cloudflare({
249 apiKey: '144c9defac04969c7bfad8efaa8ea194',
250 apiEmail: 'user@example.com',
251 });
252 expect(client.apiKey).toBe('144c9defac04969c7bfad8efaa8ea194');
253 expect(client.apiEmail).toBe('user@example.com');
254 });
255});
256
257describe('request building', () => {
258 const client = new Cloudflare({ apiKey: '144c9defac04969c7bfad8efaa8ea194', apiEmail: 'user@example.com' });
259
260 describe('Content-Length', () => {
261 test('handles multi-byte characters', () => {
262 const { req } = client.buildRequest({ path: '/foo', method: 'post', body: { value: '—' } });
263 expect((req.headers as Record<string, string>)['content-length']).toEqual('20');
264 });
265
266 test('handles standard characters', () => {
267 const { req } = client.buildRequest({ path: '/foo', method: 'post', body: { value: 'hello' } });
268 expect((req.headers as Record<string, string>)['content-length']).toEqual('22');
269 });
270 });
271
272 describe('custom headers', () => {
273 test('handles undefined', () => {
274 const { req } = client.buildRequest({
275 path: '/foo',
276 method: 'post',
277 body: { value: 'hello' },
278 headers: { 'X-Foo': 'baz', 'x-foo': 'bar', 'x-Foo': undefined, 'x-baz': 'bam', 'X-Baz': null },
279 });
280 expect((req.headers as Record<string, string>)['x-foo']).toEqual('bar');
281 expect((req.headers as Record<string, string>)['x-Foo']).toEqual(undefined);
282 expect((req.headers as Record<string, string>)['X-Foo']).toEqual(undefined);
283 expect((req.headers as Record<string, string>)['x-baz']).toEqual(undefined);
284 });
285 });
286});
287
288describe('retries', () => {
289 test('retry on timeout', async () => {
290 let count = 0;
291 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
292 if (count++ === 0) {
293 return new Promise(
294 (resolve, reject) => signal?.addEventListener('abort', () => reject(new Error('timed out'))),
295 );
296 }
297 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
298 };
299
300 const client = new Cloudflare({
301 apiKey: '144c9defac04969c7bfad8efaa8ea194',
302 apiEmail: 'user@example.com',
303 timeout: 10,
304 fetch: testFetch,
305 });
306
307 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
308 expect(count).toEqual(2);
309 expect(
310 await client
311 .request({ path: '/foo', method: 'get' })
312 .asResponse()
313 .then((r) => r.text()),
314 ).toEqual(JSON.stringify({ a: 1 }));
315 expect(count).toEqual(3);
316 });
317
318 test('retry count header', async () => {
319 let count = 0;
320 let capturedRequest: RequestInit | undefined;
321 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
322 count++;
323 if (count <= 2) {
324 return new Response(undefined, {
325 status: 429,
326 headers: {
327 'Retry-After': '0.1',
328 },
329 });
330 }
331 capturedRequest = init;
332 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
333 };
334
335 const client = new Cloudflare({
336 apiKey: '144c9defac04969c7bfad8efaa8ea194',
337 apiEmail: 'user@example.com',
338 fetch: testFetch,
339 maxRetries: 4,
340 });
341
342 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
343
344 expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toEqual('2');
345 expect(count).toEqual(3);
346 });
347
348 test('omit retry count header', async () => {
349 let count = 0;
350 let capturedRequest: RequestInit | undefined;
351 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
352 count++;
353 if (count <= 2) {
354 return new Response(undefined, {
355 status: 429,
356 headers: {
357 'Retry-After': '0.1',
358 },
359 });
360 }
361 capturedRequest = init;
362 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
363 };
364 const client = new Cloudflare({
365 apiKey: '144c9defac04969c7bfad8efaa8ea194',
366 apiEmail: 'user@example.com',
367 fetch: testFetch,
368 maxRetries: 4,
369 });
370
371 expect(
372 await client.request({
373 path: '/foo',
374 method: 'get',
375 headers: { 'X-Stainless-Retry-Count': null },
376 }),
377 ).toEqual({ a: 1 });
378
379 expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
380 });
381
382 test('omit retry count header by default', async () => {
383 let count = 0;
384 let capturedRequest: RequestInit | undefined;
385 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
386 count++;
387 if (count <= 2) {
388 return new Response(undefined, {
389 status: 429,
390 headers: {
391 'Retry-After': '0.1',
392 },
393 });
394 }
395 capturedRequest = init;
396 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
397 };
398 const client = new Cloudflare({
399 apiKey: '144c9defac04969c7bfad8efaa8ea194',
400 apiEmail: 'user@example.com',
401 fetch: testFetch,
402 maxRetries: 4,
403 defaultHeaders: { 'X-Stainless-Retry-Count': null },
404 });
405
406 expect(
407 await client.request({
408 path: '/foo',
409 method: 'get',
410 }),
411 ).toEqual({ a: 1 });
412
413 expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
414 });
415
416 test('overwrite retry count header', async () => {
417 let count = 0;
418 let capturedRequest: RequestInit | undefined;
419 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
420 count++;
421 if (count <= 2) {
422 return new Response(undefined, {
423 status: 429,
424 headers: {
425 'Retry-After': '0.1',
426 },
427 });
428 }
429 capturedRequest = init;
430 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
431 };
432 const client = new Cloudflare({
433 apiKey: '144c9defac04969c7bfad8efaa8ea194',
434 apiEmail: 'user@example.com',
435 fetch: testFetch,
436 maxRetries: 4,
437 });
438
439 expect(
440 await client.request({
441 path: '/foo',
442 method: 'get',
443 headers: { 'X-Stainless-Retry-Count': '42' },
444 }),
445 ).toEqual({ a: 1 });
446
447 expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toBe('42');
448 });
449
450 test('retry on 429 with retry-after', async () => {
451 let count = 0;
452 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
453 if (count++ === 0) {
454 return new Response(undefined, {
455 status: 429,
456 headers: {
457 'Retry-After': '0.1',
458 },
459 });
460 }
461 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
462 };
463
464 const client = new Cloudflare({
465 apiKey: '144c9defac04969c7bfad8efaa8ea194',
466 apiEmail: 'user@example.com',
467 fetch: testFetch,
468 });
469
470 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
471 expect(count).toEqual(2);
472 expect(
473 await client
474 .request({ path: '/foo', method: 'get' })
475 .asResponse()
476 .then((r) => r.text()),
477 ).toEqual(JSON.stringify({ a: 1 }));
478 expect(count).toEqual(3);
479 });
480
481 test('retry on 429 with retry-after-ms', async () => {
482 let count = 0;
483 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
484 if (count++ === 0) {
485 return new Response(undefined, {
486 status: 429,
487 headers: {
488 'Retry-After-Ms': '10',
489 },
490 });
491 }
492 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
493 };
494
495 const client = new Cloudflare({
496 apiKey: '144c9defac04969c7bfad8efaa8ea194',
497 apiEmail: 'user@example.com',
498 fetch: testFetch,
499 });
500
501 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
502 expect(count).toEqual(2);
503 expect(
504 await client
505 .request({ path: '/foo', method: 'get' })
506 .asResponse()
507 .then((r) => r.text()),
508 ).toEqual(JSON.stringify({ a: 1 }));
509 expect(count).toEqual(3);
510 });
511});
512