cloudflare/cloudflare-typescript

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v4.0.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

tests/index.test.ts

501lines · 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('custom signal', async () => {
105 const client = new Cloudflare({
106 baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
107 apiKey: '144c9defac04969c7bfad8efaa8ea194',
108 apiEmail: 'user@example.com',
109 fetch: (...args) => {
110 return new Promise((resolve, reject) =>
111 setTimeout(
112 () =>
113 defaultFetch(...args)
114 .then(resolve)
115 .catch(reject),
116 300,
117 ),
118 );
119 },
120 });
121
122 const controller = new AbortController();
123 setTimeout(() => controller.abort(), 200);
124
125 const spy = jest.spyOn(client, 'request');
126
127 await expect(client.get('/foo', { signal: controller.signal })).rejects.toThrowError(APIUserAbortError);
128 expect(spy).toHaveBeenCalledTimes(1);
129 });
130
131 test('normalized method', async () => {
132 let capturedRequest: RequestInit | undefined;
133 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
134 capturedRequest = init;
135 return new Response(JSON.stringify({}), { headers: { 'Content-Type': 'application/json' } });
136 };
137
138 const client = new Cloudflare({
139 baseURL: 'http://localhost:5000/',
140 apiKey: '144c9defac04969c7bfad8efaa8ea194',
141 apiEmail: 'user@example.com',
142 fetch: testFetch,
143 });
144
145 await client.patch('/foo');
146 expect(capturedRequest?.method).toEqual('PATCH');
147 });
148
149 describe('baseUrl', () => {
150 test('trailing slash', () => {
151 const client = new Cloudflare({
152 baseURL: 'http://localhost:5000/custom/path/',
153 apiKey: '144c9defac04969c7bfad8efaa8ea194',
154 apiEmail: 'user@example.com',
155 });
156 expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo');
157 });
158
159 test('no trailing slash', () => {
160 const client = new Cloudflare({
161 baseURL: 'http://localhost:5000/custom/path',
162 apiKey: '144c9defac04969c7bfad8efaa8ea194',
163 apiEmail: 'user@example.com',
164 });
165 expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo');
166 });
167
168 afterEach(() => {
169 process.env['CLOUDFLARE_BASE_URL'] = undefined;
170 });
171
172 test('explicit option', () => {
173 const client = new Cloudflare({
174 baseURL: 'https://example.com',
175 apiKey: '144c9defac04969c7bfad8efaa8ea194',
176 apiEmail: 'user@example.com',
177 });
178 expect(client.baseURL).toEqual('https://example.com');
179 });
180
181 test('env variable', () => {
182 process.env['CLOUDFLARE_BASE_URL'] = 'https://example.com/from_env';
183 const client = new Cloudflare({
184 apiKey: '144c9defac04969c7bfad8efaa8ea194',
185 apiEmail: 'user@example.com',
186 });
187 expect(client.baseURL).toEqual('https://example.com/from_env');
188 });
189
190 test('empty env variable', () => {
191 process.env['CLOUDFLARE_BASE_URL'] = ''; // empty
192 const client = new Cloudflare({
193 apiKey: '144c9defac04969c7bfad8efaa8ea194',
194 apiEmail: 'user@example.com',
195 });
196 expect(client.baseURL).toEqual('https://api.cloudflare.com/client/v4');
197 });
198
199 test('blank env variable', () => {
200 process.env['CLOUDFLARE_BASE_URL'] = ' '; // blank
201 const client = new Cloudflare({
202 apiKey: '144c9defac04969c7bfad8efaa8ea194',
203 apiEmail: 'user@example.com',
204 });
205 expect(client.baseURL).toEqual('https://api.cloudflare.com/client/v4');
206 });
207 });
208
209 test('maxRetries option is correctly set', () => {
210 const client = new Cloudflare({
211 maxRetries: 4,
212 apiKey: '144c9defac04969c7bfad8efaa8ea194',
213 apiEmail: 'user@example.com',
214 });
215 expect(client.maxRetries).toEqual(4);
216
217 // default
218 const client2 = new Cloudflare({
219 apiKey: '144c9defac04969c7bfad8efaa8ea194',
220 apiEmail: 'user@example.com',
221 });
222 expect(client2.maxRetries).toEqual(2);
223 });
224
225 test('with environment variable arguments', () => {
226 // set options via env var
227 process.env['CLOUDFLARE_API_KEY'] = '144c9defac04969c7bfad8efaa8ea194';
228 process.env['CLOUDFLARE_EMAIL'] = 'user@example.com';
229 const client = new Cloudflare();
230 expect(client.apiKey).toBe('144c9defac04969c7bfad8efaa8ea194');
231 expect(client.apiEmail).toBe('user@example.com');
232 });
233
234 test('with overridden environment variable arguments', () => {
235 // set options via env var
236 process.env['CLOUDFLARE_API_KEY'] = 'another 144c9defac04969c7bfad8efaa8ea194';
237 process.env['CLOUDFLARE_EMAIL'] = 'another user@example.com';
238 const client = new Cloudflare({
239 apiKey: '144c9defac04969c7bfad8efaa8ea194',
240 apiEmail: 'user@example.com',
241 });
242 expect(client.apiKey).toBe('144c9defac04969c7bfad8efaa8ea194');
243 expect(client.apiEmail).toBe('user@example.com');
244 });
245});
246
247describe('request building', () => {
248 const client = new Cloudflare({ apiKey: '144c9defac04969c7bfad8efaa8ea194', apiEmail: 'user@example.com' });
249
250 describe('Content-Length', () => {
251 test('handles multi-byte characters', () => {
252 const { req } = client.buildRequest({ path: '/foo', method: 'post', body: { value: '—' } });
253 expect((req.headers as Record<string, string>)['content-length']).toEqual('20');
254 });
255
256 test('handles standard characters', () => {
257 const { req } = client.buildRequest({ path: '/foo', method: 'post', body: { value: 'hello' } });
258 expect((req.headers as Record<string, string>)['content-length']).toEqual('22');
259 });
260 });
261
262 describe('custom headers', () => {
263 test('handles undefined', () => {
264 const { req } = client.buildRequest({
265 path: '/foo',
266 method: 'post',
267 body: { value: 'hello' },
268 headers: { 'X-Foo': 'baz', 'x-foo': 'bar', 'x-Foo': undefined, 'x-baz': 'bam', 'X-Baz': null },
269 });
270 expect((req.headers as Record<string, string>)['x-foo']).toEqual('bar');
271 expect((req.headers as Record<string, string>)['x-Foo']).toEqual(undefined);
272 expect((req.headers as Record<string, string>)['X-Foo']).toEqual(undefined);
273 expect((req.headers as Record<string, string>)['x-baz']).toEqual(undefined);
274 });
275 });
276});
277
278describe('retries', () => {
279 test('retry on timeout', async () => {
280 let count = 0;
281 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
282 if (count++ === 0) {
283 return new Promise(
284 (resolve, reject) => signal?.addEventListener('abort', () => reject(new Error('timed out'))),
285 );
286 }
287 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
288 };
289
290 const client = new Cloudflare({
291 apiKey: '144c9defac04969c7bfad8efaa8ea194',
292 apiEmail: 'user@example.com',
293 timeout: 10,
294 fetch: testFetch,
295 });
296
297 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
298 expect(count).toEqual(2);
299 expect(
300 await client
301 .request({ path: '/foo', method: 'get' })
302 .asResponse()
303 .then((r) => r.text()),
304 ).toEqual(JSON.stringify({ a: 1 }));
305 expect(count).toEqual(3);
306 });
307
308 test('retry count header', async () => {
309 let count = 0;
310 let capturedRequest: RequestInit | undefined;
311 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
312 count++;
313 if (count <= 2) {
314 return new Response(undefined, {
315 status: 429,
316 headers: {
317 'Retry-After': '0.1',
318 },
319 });
320 }
321 capturedRequest = init;
322 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
323 };
324
325 const client = new Cloudflare({
326 apiKey: '144c9defac04969c7bfad8efaa8ea194',
327 apiEmail: 'user@example.com',
328 fetch: testFetch,
329 maxRetries: 4,
330 });
331
332 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
333
334 expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toEqual('2');
335 expect(count).toEqual(3);
336 });
337
338 test('omit retry count header', async () => {
339 let count = 0;
340 let capturedRequest: RequestInit | undefined;
341 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
342 count++;
343 if (count <= 2) {
344 return new Response(undefined, {
345 status: 429,
346 headers: {
347 'Retry-After': '0.1',
348 },
349 });
350 }
351 capturedRequest = init;
352 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
353 };
354 const client = new Cloudflare({
355 apiKey: '144c9defac04969c7bfad8efaa8ea194',
356 apiEmail: 'user@example.com',
357 fetch: testFetch,
358 maxRetries: 4,
359 });
360
361 expect(
362 await client.request({
363 path: '/foo',
364 method: 'get',
365 headers: { 'X-Stainless-Retry-Count': null },
366 }),
367 ).toEqual({ a: 1 });
368
369 expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
370 });
371
372 test('omit retry count header by default', async () => {
373 let count = 0;
374 let capturedRequest: RequestInit | undefined;
375 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
376 count++;
377 if (count <= 2) {
378 return new Response(undefined, {
379 status: 429,
380 headers: {
381 'Retry-After': '0.1',
382 },
383 });
384 }
385 capturedRequest = init;
386 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
387 };
388 const client = new Cloudflare({
389 apiKey: '144c9defac04969c7bfad8efaa8ea194',
390 apiEmail: 'user@example.com',
391 fetch: testFetch,
392 maxRetries: 4,
393 defaultHeaders: { 'X-Stainless-Retry-Count': null },
394 });
395
396 expect(
397 await client.request({
398 path: '/foo',
399 method: 'get',
400 }),
401 ).toEqual({ a: 1 });
402
403 expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
404 });
405
406 test('overwrite retry count header', async () => {
407 let count = 0;
408 let capturedRequest: RequestInit | undefined;
409 const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
410 count++;
411 if (count <= 2) {
412 return new Response(undefined, {
413 status: 429,
414 headers: {
415 'Retry-After': '0.1',
416 },
417 });
418 }
419 capturedRequest = init;
420 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
421 };
422 const client = new Cloudflare({
423 apiKey: '144c9defac04969c7bfad8efaa8ea194',
424 apiEmail: 'user@example.com',
425 fetch: testFetch,
426 maxRetries: 4,
427 });
428
429 expect(
430 await client.request({
431 path: '/foo',
432 method: 'get',
433 headers: { 'X-Stainless-Retry-Count': '42' },
434 }),
435 ).toEqual({ a: 1 });
436
437 expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toBe('42');
438 });
439
440 test('retry on 429 with retry-after', async () => {
441 let count = 0;
442 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
443 if (count++ === 0) {
444 return new Response(undefined, {
445 status: 429,
446 headers: {
447 'Retry-After': '0.1',
448 },
449 });
450 }
451 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
452 };
453
454 const client = new Cloudflare({
455 apiKey: '144c9defac04969c7bfad8efaa8ea194',
456 apiEmail: 'user@example.com',
457 fetch: testFetch,
458 });
459
460 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
461 expect(count).toEqual(2);
462 expect(
463 await client
464 .request({ path: '/foo', method: 'get' })
465 .asResponse()
466 .then((r) => r.text()),
467 ).toEqual(JSON.stringify({ a: 1 }));
468 expect(count).toEqual(3);
469 });
470
471 test('retry on 429 with retry-after-ms', async () => {
472 let count = 0;
473 const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {
474 if (count++ === 0) {
475 return new Response(undefined, {
476 status: 429,
477 headers: {
478 'Retry-After-Ms': '10',
479 },
480 });
481 }
482 return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
483 };
484
485 const client = new Cloudflare({
486 apiKey: '144c9defac04969c7bfad8efaa8ea194',
487 apiEmail: 'user@example.com',
488 fetch: testFetch,
489 });
490
491 expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
492 expect(count).toEqual(2);
493 expect(
494 await client
495 .request({ path: '/foo', method: 'get' })
496 .asResponse()
497 .then((r) => r.text()),
498 ).toEqual(JSON.stringify({ a: 1 }));
499 expect(count).toEqual(3);
500 });
501});
502