1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const nock = require("nock");
|
4 | const querystring = require("querystring");
|
5 | const http_1 = require("./http");
|
6 | nock.disableNetConnect();
|
7 | let api;
|
8 | beforeEach(() => {
|
9 | api = nock('https://api.jdxcode.com');
|
10 | });
|
11 | afterEach(() => {
|
12 | api.done();
|
13 | });
|
14 | afterEach(() => {
|
15 | nock.cleanAll();
|
16 | });
|
17 | describe('HTTP.get()', () => {
|
18 | test('makes a GET request', async () => {
|
19 | api.get('/').reply(200, { message: 'ok' });
|
20 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com');
|
21 | expect(body).toEqual({ message: 'ok' });
|
22 | });
|
23 | test('makes a GET request', async () => {
|
24 | api.get('/').reply(200, { message: 'ok' }, {
|
25 | 'content-type': 'application/json; charset=UTF-8',
|
26 | });
|
27 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com');
|
28 | expect(body).toEqual({ message: 'ok' });
|
29 | });
|
30 | test('gets headers', async () => {
|
31 | api.get('/').reply(200, { message: 'ok' }, { myheader: 'ok' });
|
32 | let { body, headers } = await http_1.HTTP.get('https://api.jdxcode.com');
|
33 | expect(body).toEqual({ message: 'ok' });
|
34 | expect(headers).toMatchObject({ myheader: 'ok' });
|
35 | });
|
36 | test('can build a new HTTP with defaults', async () => {
|
37 | const MyHTTP = http_1.HTTP.create({ host: 'api.jdxcode.com' });
|
38 | api.get('/').reply(200, { message: 'ok' });
|
39 | let { body } = await MyHTTP.get('/');
|
40 | expect(body).toEqual({ message: 'ok' });
|
41 | });
|
42 | test('makes a request to a port', async () => {
|
43 | api = nock('https://api.jdxcode.com:3000');
|
44 | api.get('/').reply(200, { message: 'ok' });
|
45 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com:3000');
|
46 | expect(body).toEqual({ message: 'ok' });
|
47 | });
|
48 | test('allows specifying the port', async () => {
|
49 | api = nock('https://api.jdxcode.com:3000');
|
50 | api.get('/').reply(200, { message: 'ok' });
|
51 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com', { port: 3000 });
|
52 | expect(body).toEqual({ message: 'ok' });
|
53 | });
|
54 | test('makes a http GET request', async () => {
|
55 | api = nock('http://api.jdxcode.com');
|
56 | api.get('/').reply(200, { message: 'ok' });
|
57 | let { body } = await http_1.HTTP.get('http://api.jdxcode.com');
|
58 | expect(body).toEqual({ message: 'ok' });
|
59 | });
|
60 | test('can set default user agent', async () => {
|
61 | http_1.HTTP.defaults.headers = { 'user-agent': 'mynewuseragent' };
|
62 | api
|
63 | .matchHeader('user-agent', `mynewuseragent`)
|
64 | .get('/')
|
65 | .reply(200, { message: 'ok' });
|
66 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com/');
|
67 | expect(body).toEqual({ message: 'ok' });
|
68 | delete http_1.HTTP.defaults.headers['user-agent'];
|
69 | });
|
70 | test('can set user agent as a global', async () => {
|
71 | global['http-call'] = { userAgent: 'mynewuseragent' };
|
72 | api
|
73 | .matchHeader('user-agent', `mynewuseragent`)
|
74 | .get('/')
|
75 | .reply(200, { message: 'ok' });
|
76 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com/');
|
77 | expect(body).toEqual({ message: 'ok' });
|
78 | delete global['http-call'];
|
79 | });
|
80 | test('sets user-agent header', async () => {
|
81 | api
|
82 | .matchHeader('user-agent', `http-call/${require('../package.json').version} node-${process.version}`)
|
83 | .get('/')
|
84 | .reply(200, { message: 'ok' });
|
85 | await http_1.HTTP.get('https://api.jdxcode.com');
|
86 | });
|
87 | test('sets custom headers', async () => {
|
88 | api
|
89 | .matchHeader('foo', 'bar')
|
90 | .get('/')
|
91 | .reply(200);
|
92 | let headers = { foo: 'bar' };
|
93 | await http_1.HTTP.get('https://api.jdxcode.com', { headers });
|
94 | });
|
95 | test('does not fail on undefined header', async () => {
|
96 | api.get('/').reply(200);
|
97 | let headers = { foo: undefined };
|
98 | await http_1.HTTP.get('https://api.jdxcode.com', { headers });
|
99 | });
|
100 | describe('wait mocked out', () => {
|
101 | let wait = http_1.HTTP.prototype._wait;
|
102 | beforeAll(() => {
|
103 | http_1.HTTP.prototype._wait = jest.fn();
|
104 | });
|
105 | afterAll(() => {
|
106 | http_1.HTTP.prototype._wait = wait;
|
107 | });
|
108 | test('retries then succeeds', async () => {
|
109 | api.get('/').replyWithError({ message: 'timed out 1', code: 'ETIMEDOUT' });
|
110 | api.get('/').replyWithError({ message: 'timed out 2', code: 'ETIMEDOUT' });
|
111 | api.get('/').replyWithError({ message: 'timed out 3', code: 'ETIMEDOUT' });
|
112 | api.get('/').replyWithError({ message: 'timed out 4', code: 'ETIMEDOUT' });
|
113 | api.get('/').reply(200, { message: 'foo' });
|
114 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com');
|
115 | expect(body).toEqual({ message: 'foo' });
|
116 | });
|
117 | test('retries 5 times on ETIMEDOUT', async () => {
|
118 | expect.assertions(1);
|
119 | api.get('/').replyWithError({ message: 'timed out 1', code: 'ETIMEDOUT' });
|
120 | api.get('/').replyWithError({ message: 'timed out 2', code: 'ETIMEDOUT' });
|
121 | api.get('/').replyWithError({ message: 'timed out 3', code: 'ETIMEDOUT' });
|
122 | api.get('/').replyWithError({ message: 'timed out 4', code: 'ETIMEDOUT' });
|
123 | api.get('/').replyWithError({ message: 'timed out 5', code: 'ETIMEDOUT' });
|
124 | api.get('/').replyWithError({ message: 'timed out 6', code: 'ETIMEDOUT' });
|
125 | try {
|
126 | await http_1.HTTP.get('https://api.jdxcode.com');
|
127 | }
|
128 | catch (err) {
|
129 | expect(err.message).toEqual('timed out 6');
|
130 | }
|
131 | });
|
132 | });
|
133 | test('retries on ENOTFOUND', async () => {
|
134 | api.get('/').replyWithError({ message: 'not found', code: 'ENOTFOUND' });
|
135 | api.get('/').reply(200, { message: 'foo' });
|
136 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com');
|
137 | expect(body).toMatchObject({ message: 'foo' });
|
138 | });
|
139 | test('errors on EFOOBAR', async () => {
|
140 | expect.assertions(1);
|
141 | api.get('/').replyWithError({ message: 'oom', code: 'OUT_OF_MEM' });
|
142 | try {
|
143 | await http_1.HTTP.get('https://api.jdxcode.com');
|
144 | }
|
145 | catch (err) {
|
146 | expect(err.message).toEqual('oom');
|
147 | }
|
148 | });
|
149 | test('displays 404 error', async () => {
|
150 | expect.assertions(2);
|
151 | api.get('/').reply(404, 'oops! not found');
|
152 | try {
|
153 | await http_1.HTTP.get('https://api.jdxcode.com');
|
154 | }
|
155 | catch (err) {
|
156 | expect(err.statusCode).toEqual(404);
|
157 | expect(err.message).toEqual(`HTTP Error 404 for GET https://api.jdxcode.com:443/
|
158 | oops! not found`);
|
159 | }
|
160 | });
|
161 | test('displays error message', async () => {
|
162 | expect.assertions(3);
|
163 | api.get('/').reply(404, { message: 'uh oh', otherinfo: [1, 2, 3] });
|
164 | try {
|
165 | await http_1.HTTP.get('https://api.jdxcode.com');
|
166 | }
|
167 | catch (err) {
|
168 | expect(err.statusCode).toEqual(404);
|
169 | expect(err.message).toEqual(`HTTP Error 404 for GET https://api.jdxcode.com:443/
|
170 | uh oh`);
|
171 | expect(err.body).toMatchObject({ otherinfo: [1, 2, 3] });
|
172 | }
|
173 | });
|
174 | test('displays object error', async () => {
|
175 | expect.assertions(3);
|
176 | api.get('/').reply(404, { otherinfo: [1, 2, 3] });
|
177 | try {
|
178 | await http_1.HTTP.get('https://api.jdxcode.com');
|
179 | }
|
180 | catch (err) {
|
181 | expect(err.statusCode).toEqual(404);
|
182 | expect(err.message).toEqual(`HTTP Error 404 for GET https://api.jdxcode.com:443/
|
183 | { otherinfo: [ 1, 2, 3 ] }`);
|
184 | expect(err.body).toMatchObject({ otherinfo: [1, 2, 3] });
|
185 | }
|
186 | });
|
187 | test('follows redirect', async () => {
|
188 | api.get('/foo1').reply(302, null, { Location: 'https://api.jdxcode.com/foo2' });
|
189 | api.get('/foo2').reply(302, null, { Location: 'https://api.jdxcode.com/foo3' });
|
190 | api.get('/foo3').reply(200, { success: true });
|
191 | await http_1.HTTP.get('https://api.jdxcode.com/foo1');
|
192 | });
|
193 | test('follows redirect only 10 times', async () => {
|
194 | api.get('/foo1').reply(302, null, { Location: 'https://api.jdxcode.com/foo2' });
|
195 | api.get('/foo2').reply(302, null, { Location: 'https://api.jdxcode.com/foo3' });
|
196 | api.get('/foo3').reply(302, null, { Location: 'https://api.jdxcode.com/foo4' });
|
197 | api.get('/foo4').reply(302, null, { Location: 'https://api.jdxcode.com/foo5' });
|
198 | api.get('/foo5').reply(302, null, { Location: 'https://api.jdxcode.com/foo6' });
|
199 | api.get('/foo6').reply(302, null, { Location: 'https://api.jdxcode.com/foo7' });
|
200 | api.get('/foo7').reply(302, null, { Location: 'https://api.jdxcode.com/foo8' });
|
201 | api.get('/foo8').reply(302, null, { Location: 'https://api.jdxcode.com/foo9' });
|
202 | api.get('/foo9').reply(302, null, { Location: 'https://api.jdxcode.com/foo10' });
|
203 | api.get('/foo10').reply(302, null, { Location: 'https://api.jdxcode.com/foo11' });
|
204 | api.get('/foo11').reply(302, null, { Location: 'https://api.jdxcode.com/foo12' });
|
205 | expect.assertions(1);
|
206 | try {
|
207 | await http_1.HTTP.get('https://api.jdxcode.com/foo1');
|
208 | }
|
209 | catch (err) {
|
210 | expect(err.message).toEqual('Redirect loop at https://api.jdxcode.com:443/foo11');
|
211 | }
|
212 | });
|
213 | });
|
214 | describe('HTTP.post()', () => {
|
215 | test('makes a POST request', async () => {
|
216 | api.post('/', { foo: 'bar' }).reply(200, { message: 'ok' });
|
217 | let { body } = await http_1.HTTP.post('https://api.jdxcode.com', { body: { foo: 'bar' } });
|
218 | expect(body).toEqual({ message: 'ok' });
|
219 | });
|
220 | test('does not include a body if no body is passed in', async () => {
|
221 | api.post('/').reply(200, { message: 'ok' });
|
222 | let { body } = await http_1.HTTP.post('https://api.jdxcode.com');
|
223 | expect(body).toEqual({ message: 'ok' });
|
224 | });
|
225 | test('faithfully passes custom-encoded content-types', async () => {
|
226 | let apiEncoded = nock('https://api.jdxcode.com', {
|
227 | reqheaders: {
|
228 | 'Content-Type': 'application/x-www-form-urlencoded',
|
229 | },
|
230 | });
|
231 | let body = {
|
232 | karate: 'chop',
|
233 | judo: 'throw',
|
234 | taewkondo: 'kick',
|
235 | jujitsu: 'strangle',
|
236 | };
|
237 | let options = {
|
238 | headers: {
|
239 | 'Content-Type': 'application/x-www-form-urlencoded',
|
240 | },
|
241 | body: querystring.stringify(body),
|
242 | };
|
243 | apiEncoded.post('/', querystring.stringify(body)).reply(200, { message: 'ok' });
|
244 | let rsp = await http_1.HTTP.post('https://api.jdxcode.com/', options);
|
245 | expect(rsp.body).toEqual({ message: 'ok' });
|
246 | });
|
247 | });
|
248 | describe('HTTP.parseBody()', () => {
|
249 | let body;
|
250 | let http;
|
251 | beforeEach(() => {
|
252 | body = {
|
253 | karate: 'chop',
|
254 | judo: 'throw',
|
255 | taewkondo: 'kick',
|
256 | jujitsu: 'strangle',
|
257 | };
|
258 | http = new http_1.HTTP('www.duckduckgo.com', { body });
|
259 | });
|
260 | it('sets the Content-Length', () => {
|
261 | expect(http.options.headers['Content-Length']).toEqual(Buffer.byteLength(JSON.stringify(body)).toString());
|
262 | });
|
263 | it('sets the Content-Type to JSON when Content-Type is unspecified', () => {
|
264 | expect(http.options.headers['content-type']).toEqual('application/json');
|
265 | });
|
266 | it('does not set the Content Type if it already exists', () => {
|
267 | let options = {
|
268 | headers: {
|
269 | 'Content-Type': 'application/x-www-form-urlencoded',
|
270 | },
|
271 | body: querystring.stringify(body),
|
272 | };
|
273 | http = new http_1.HTTP('www.duckduckgo.com', options);
|
274 | expect(http.options.headers['content-type']).toEqual('application/x-www-form-urlencoded');
|
275 | });
|
276 | it('resets the value for http.body object', () => {
|
277 | expect(http.body).toBe(undefined);
|
278 | });
|
279 | it('sets the requestBody to the body contents', () => {
|
280 | expect(http.options.body).toBe(JSON.stringify(body));
|
281 | });
|
282 | describe('with next-range header', () => {
|
283 | beforeEach(() => {
|
284 | api
|
285 | .get('/')
|
286 | .reply(206, [1, 2, 3], {
|
287 | 'next-range': '4',
|
288 | })
|
289 | .get('/')
|
290 |
|
291 | .reply(206, [4, 5, 6], {
|
292 | 'next-range': '7',
|
293 | })
|
294 | .get('/')
|
295 |
|
296 | .reply(206, [7, 8, 9]);
|
297 | });
|
298 | test('gets next body when next-range is set', async () => {
|
299 | let { body } = await http_1.HTTP.get('https://api.jdxcode.com');
|
300 | expect(body).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
301 | });
|
302 | });
|
303 | });
|
304 | describe('HTTP.put()', () => {
|
305 | test('makes a PUT request', async () => {
|
306 | api.put('/', { foo: 'bar' }).reply(200, { message: 'ok' });
|
307 | let { body } = await http_1.HTTP.put('https://api.jdxcode.com', { body: { foo: 'bar' } });
|
308 | expect(body).toEqual({ message: 'ok' });
|
309 | });
|
310 | });
|
311 | describe('HTTP.patch()', () => {
|
312 | test('makes a PATCH request', async () => {
|
313 | api.patch('/', { foo: 'bar' }).reply(200, { message: 'ok' });
|
314 | let { body } = await http_1.HTTP.patch('https://api.jdxcode.com', { body: { foo: 'bar' } });
|
315 | expect(body).toEqual({ message: 'ok' });
|
316 | });
|
317 | });
|
318 | describe('HTTP.delete()', () => {
|
319 | test('makes a DELETE request', async () => {
|
320 | api.delete('/', { foo: 'bar' }).reply(200, { message: 'ok' });
|
321 | let { body } = await http_1.HTTP.delete('https://api.jdxcode.com', { body: { foo: 'bar' } });
|
322 | expect(body).toEqual({ message: 'ok' });
|
323 | });
|
324 | });
|
325 | describe('HTTP.stream()', () => {
|
326 | test('streams a response', async (done) => {
|
327 | api = nock('http://api.jdxcode.com');
|
328 | api.get('/').reply(200, { message: 'ok' });
|
329 | let { response } = await http_1.HTTP.stream('http://api.jdxcode.com');
|
330 | response.setEncoding('utf8');
|
331 | response.on('data', data => expect(data).toEqual('{"message":"ok"}'));
|
332 | response.on('end', done);
|
333 | });
|
334 | });
|