UNPKG

@villedemontreal/http-request

Version:

HTTP utilities - send HTTP requests with proper headers, etc.

625 lines 30.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const src_1 = require("@villedemontreal/general-utils/dist/src"); const chai_1 = require("chai"); const express = require("express"); const HttpStatusCodes = require("http-status-codes"); const _ = require("lodash"); const superagent = require("superagent"); const configs_1 = require("./config/configs"); const constants_1 = require("./config/constants"); const httpUtils_1 = require("./httpUtils"); const testingConfigurations_1 = require("./utils/testingConfigurations"); // eslint-disable-next-line @typescript-eslint/no-require-imports const superagentMocker = require('superagent-mocker'); // ========================================== // Set Testing configurations // ========================================== (0, testingConfigurations_1.setTestingConfigurations)(); describe('httpUtils', () => { describe('urlJoin', () => { it('single param with slash', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com/'), 'http://google.com'); }); it('single param no slash', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com'), 'http://google.com'); }); it('with slashes', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com/', 'foo/', '/bar/'), 'http://google.com/foo/bar'); }); it('without slashes', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com', 'foo', 'bar'), 'http://google.com/foo/bar'); }); it('without double slashes', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com/', '//foo/', '//bar/'), 'http://google.com/foo/bar'); }); it('with slashes without text', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com/', '///', '//bar/'), 'http://google.com/bar'); }); it('with slashes and empty text', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com/', '', '//bar/'), 'http://google.com/bar'); }); it('with slashes and null text', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com/', null, '//bar/'), 'http://google.com/bar'); }); it('with slashes and undefined text', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://google.com/', undefined, '//bar/'), 'http://google.com/bar'); }); it('with http 2 slashes', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://', 'google.com', 'foo', 'bar'), 'http://google.com/foo/bar'); }); it('with http 1 slash', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http:/', 'google.com', 'foo', 'bar'), 'http://google.com/foo/bar'); }); it('with http no slash', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http:', 'google.com', 'foo', 'bar'), 'http://google.com/foo/bar'); }); it('another example', async () => { chai_1.assert.equal(httpUtils_1.httpUtils.urlJoin('http://api.montreal.ca/accounts/', '/inum', '@5441521452', 'tickets'), 'http://api.montreal.ca/accounts/inum/@5441521452/tickets'); }); }); describe('send', () => { describe('mocked', () => { let mock; before(async () => { mock = superagentMocker(superagent); mock.get('http://localhost/test', (req) => { return { body: { headers: req.headers, }, }; }); }); after(async () => { mock.clearRoutes(); mock.unmock(superagent); }); it('URL must have a hostname', async () => { const request = superagent.get('/test'); try { await httpUtils_1.httpUtils.send(request); chai_1.assert.fail('expected send to throw an error'); } catch (err) { chai_1.assert.strictEqual(err.message, 'The URL in your request MUST have a protocol and a hostname. Received: /test'); } }); it('The Correlation Id is set automatically', async () => { const currentCid = configs_1.configs.correlationId; const request = superagent.get('http://localhost/test').set('titi', '123'); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isOk(response); chai_1.assert.isOk(response.status); chai_1.assert.strictEqual(response.status, 200); chai_1.assert.isObject(response.body); chai_1.assert.isObject(response.body.headers); const headers = response.body.headers; chai_1.assert.strictEqual(headers.titi, '123'); chai_1.assert.strictEqual(headers['x-correlation-id'], currentCid); }); it('Regular response response', async () => { for (const status of [200, 201, 301, 400, 404, 500, 501]) { mock.get('http://localhost/test', () => { return { status, body: { msg: 'titi', }, }; }); const request = superagent.get('http://localhost/test'); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isOk(response); chai_1.assert.isOk(response.status); chai_1.assert.strictEqual(response.status, status); chai_1.assert.isObject(response.body); chai_1.assert.strictEqual(response.body.msg, 'titi'); } }); it('Timeouts are added, if not already set', async () => { const request = superagent.get('http://localhost/test').set('titi', '123'); chai_1.assert.isUndefined(request['_responseTimeout']); chai_1.assert.isUndefined(request['_timeout']); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isOk(response); chai_1.assert.strictEqual(request['_responseTimeout'], constants_1.constants.request.timeoutsDefault.response); chai_1.assert.strictEqual(request['_timeout'], constants_1.constants.request.timeoutsDefault.deadline); }); it('Response timeout already set', async () => { const request = superagent.get('http://localhost/test').set('titi', '123'); request.timeout({ response: 55555, }); chai_1.assert.strictEqual(request['_responseTimeout'], 55555); chai_1.assert.isUndefined(request['_timeout']); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isOk(response); chai_1.assert.strictEqual(request['_responseTimeout'], 55555); chai_1.assert.strictEqual(request['_timeout'], constants_1.constants.request.timeoutsDefault.deadline); }); it('Deadline timeout already set', async () => { const request = superagent.get('http://localhost/test').set('titi', '123'); request.timeout({ deadline: 55555, }); chai_1.assert.isUndefined(request['_responseTimeout']); chai_1.assert.strictEqual(request['_timeout'], 55555); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isOk(response); chai_1.assert.strictEqual(request['_responseTimeout'], constants_1.constants.request.timeoutsDefault.response); chai_1.assert.strictEqual(request['_timeout'], 55555); }); it('Both timeouts timeout already set', async () => { const request = superagent.get('http://localhost/test').set('titi', '123'); request.timeout({ deadline: 55555, response: 66666, }); chai_1.assert.strictEqual(request['_responseTimeout'], 66666); chai_1.assert.strictEqual(request['_timeout'], 55555); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isOk(response); chai_1.assert.strictEqual(request['_responseTimeout'], 66666); chai_1.assert.strictEqual(request['_timeout'], 55555); }); }); describe('Network/Server error', () => { it('Network/Server error', async () => { const mock = superagentMocker(superagent); mock.get('http://localhost/test', () => { throw new Error('Network error'); }); try { const request = superagent.get('http://localhost/test'); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isNotOk(response); chai_1.assert.fail(); } catch { /* ok */ } }); }); describe('not mocked', () => { it('Errors are handled properly', async () => { try { const request = superagent.get('httttp://nope').timeout(100); const response = await httpUtils_1.httpUtils.send(request); chai_1.assert.isNotOk(response); chai_1.assert.fail(); } catch (err) { chai_1.assert.isObject(err); chai_1.assert.isTrue('msg' in err); chai_1.assert.isTrue('originalError' in err); } }); }); }); describe(`Express request related tests`, () => { let app; let server; let port; let expressRequest; async function startServer(caseSensitive) { app = express(); app.set('case sensitive routing', caseSensitive); app.get('/', async (req, res) => { expressRequest = req; res.sendStatus(HttpStatusCodes.OK); }); port = await src_1.utils.findFreePort(); server = app.listen(port); } async function send(pathAndQueryString) { const superagentRequest = superagent.get(`http://localhost:${port}${pathAndQueryString}`); const response = await httpUtils_1.httpUtils.send(superagentRequest); chai_1.assert.strictEqual(response.status, HttpStatusCodes.OK); } describe(`Query params functions - Case sensitive`, () => { before(async () => { // ========================================== // Set the configs for case sensitivity! // ========================================== (0, testingConfigurations_1.setTestingConfigurations)(true); await startServer(true); }); after(() => { server.close(); }); it(`no query params`, async () => { await send(`/`); const values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); chai_1.assert.deepEqual(values, []); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); chai_1.assert.deepEqual(value, undefined); value = httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k'); chai_1.assert.deepEqual(value, undefined); value = httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k'); chai_1.assert.deepEqual(value, undefined); value = httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); chai_1.assert.deepEqual(value, undefined); }); it(`one query params - simple string`, async () => { await send(`/?k=toto`); let values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); chai_1.assert.deepEqual(values, ['toto']); values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'K'); // case sensitive chai_1.assert.deepEqual(values, []); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); chai_1.assert.deepEqual(value, 'toto'); value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'K'); // case sensitive chai_1.assert.deepEqual(value, undefined); let error; try { httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } value = httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k', (errMsg, val) => { chai_1.assert.isOk(errMsg); chai_1.assert.deepEqual(val, 'toto'); return undefined; }); chai_1.assert.deepEqual(value, undefined); try { httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } value = httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k', (errMsg, val) => { chai_1.assert.isOk(errMsg); chai_1.assert.deepEqual(val, 'toto'); return 123; }); chai_1.assert.deepEqual(value, 123); try { httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } value = httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k', (errMsg, val) => { chai_1.assert.isOk(errMsg); chai_1.assert.deepEqual(val, 'toto'); return 123; }); chai_1.assert.deepEqual(value, 123); }); it('one query params - date string', async () => { const dateStr = '2020-04-21T17:13:33.107Z'; await send(`/?k=${encodeURIComponent(dateStr)}`); const values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); chai_1.assert.deepEqual(values, [dateStr]); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); chai_1.assert.deepEqual(value, dateStr); value = httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k'); chai_1.assert.isTrue(_.isDate(value)); chai_1.assert.deepEqual(value, new Date(dateStr)); value = httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k', () => { chai_1.assert.fail(); }); chai_1.assert.deepEqual(value, new Date(dateStr)); let error; try { httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } try { value = httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k', () => { throw new Error(`Custom Error`); }); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } chai_1.assert.deepEqual(error.message, `Custom Error`); try { httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } try { value = httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k', () => { throw new Error(`Custom Error`); }); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } chai_1.assert.deepEqual(error.message, `Custom Error`); }); it('one query params - number string', async () => { const testNumber = 123; await send(`/?k=${testNumber}`); const values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); chai_1.assert.deepEqual(values, [`${testNumber}`]); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); chai_1.assert.deepEqual(value, `${testNumber}`); // ========================================== // Well, it seems '123' can actually be parsed // to a valid date. What can you do? // ========================================== value = httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k'); chai_1.assert.deepEqual(value, new Date(`${testNumber}`)); value = httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k'); chai_1.assert.deepEqual(value, testNumber); value = httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k', () => { chai_1.assert.fail(); }); chai_1.assert.deepEqual(value, testNumber); let error; try { httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } }); it(`one query params - boolean`, async () => { await send(`/?k=true`); let value = httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); chai_1.assert.deepEqual(value, true); await send(`/?k=TrUe`); value = httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); chai_1.assert.deepEqual(value, true); await send(`/?k=false`); value = httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); chai_1.assert.deepEqual(value, false); await send(`/?k=0`); let error; try { httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } await send(`/?k=1`); try { httpUtils_1.httpUtils.getQueryParamOneAsBoolean(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } }); it('two different query params', async () => { await send(`/?k1=123&k2=titi`); let values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k1'); chai_1.assert.deepEqual(values, ['123']); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k1'); chai_1.assert.deepEqual(value, '123'); values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k2'); chai_1.assert.deepEqual(values, ['titi']); value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k2'); chai_1.assert.deepEqual(value, 'titi'); }); it('two different query params, different only by casing!', async () => { await send(`/?k=123&K=titi`); let values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); chai_1.assert.deepEqual(values, ['123']); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); chai_1.assert.deepEqual(value, '123'); values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'K'); chai_1.assert.deepEqual(values, ['titi']); value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'K'); chai_1.assert.deepEqual(value, 'titi'); }); it('One query param with multiple values - first value is a number, second value is a date', async () => { const dateStr = '2020-04-21T17:13:33.107Z'; const testNumber = 123; await send(`/?k=${testNumber}&k=${encodeURIComponent(dateStr)}`); const values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); chai_1.assert.deepEqual(values, [`${testNumber}`, dateStr]); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); chai_1.assert.deepEqual(value, dateStr); // last value wins // last value wins and is parsable to a Date value = httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k'); chai_1.assert.deepEqual(value, new Date(dateStr)); // last value wins and can't be parsed to a number let error; try { httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k'); } catch (err) { error = err; } if (!error) { chai_1.assert.fail(); } }); it('One query param with multiple values - first value is a date, second value is a number', async () => { const dateStr = '2020-04-21T17:13:33.107Z'; const testNumber = 123; await send(`/?k=${encodeURIComponent(dateStr)}&k=${testNumber}`); const values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); chai_1.assert.deepEqual(values, [dateStr, `${testNumber}`]); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); chai_1.assert.deepEqual(value, `${testNumber}`); // last value wins // last value wins and CAN be parsed to a Date... // Yep, '123' can be parsed as date. value = httpUtils_1.httpUtils.getQueryParamOneAsDate(expressRequest, 'k'); chai_1.assert.deepEqual(value, new Date(`${testNumber}`)); // last value wins and can be parsed to a number value = httpUtils_1.httpUtils.getQueryParamOneAsNumber(expressRequest, 'k'); chai_1.assert.deepEqual(value, testNumber); }); }); describe('Query params functions - Case insensitive', () => { before(async () => { // ========================================== // Set the configs for case insensitivity! // ========================================== (0, testingConfigurations_1.setTestingConfigurations)(false); await startServer(false); }); after(() => { server.close(); }); it('two different query params, different by casing!', async () => { await send(`/?k=123&K=titi`); let values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'k'); // lowercase chai_1.assert.deepEqual(values, ['123', 'titi']); let value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'k'); // lowercase chai_1.assert.deepEqual(value, 'titi'); // last value wins values = httpUtils_1.httpUtils.getQueryParamAll(expressRequest, 'K'); // uppercase chai_1.assert.deepEqual(values, ['123', 'titi']); value = httpUtils_1.httpUtils.getQueryParamOne(expressRequest, 'K'); // uppercase chai_1.assert.deepEqual(value, 'titi'); // last value wins }); }); describe('getOrderBys - Case insensitive', () => { before(async () => { (0, testingConfigurations_1.setTestingConfigurations)(false); await startServer(false); }); after(() => { server.close(); }); it('Nil', async () => { await send(`/`); let orderBys = httpUtils_1.httpUtils.getOrderBys(null); chai_1.assert.deepEqual(orderBys, []); orderBys = httpUtils_1.httpUtils.getOrderBys(undefined); chai_1.assert.deepEqual(orderBys, []); }); it('No orderBys', async () => { await send(`/`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, []); }); it('one orderBy, default is asc', async () => { await send(`/?orderBy=name`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, [ { key: 'name', direction: src_1.OrderByDirection.ASC, }, ]); }); it('one orderBy, explicit asc', async () => { await send(`/?orderBy=+name`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, [ { key: 'name', direction: src_1.OrderByDirection.ASC, }, ]); }); it('one orderBy, desc', async () => { await send(`/?orderBy=-name`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, [ { key: 'name', direction: src_1.OrderByDirection.DESC, }, ]); }); it('multiple orderBys', async () => { await send(`/?orderBy=-name,age,+nick,-color`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, [ { key: 'name', direction: src_1.OrderByDirection.DESC, }, { key: 'age', direction: src_1.OrderByDirection.ASC, }, { key: 'nick', direction: src_1.OrderByDirection.ASC, }, { key: 'color', direction: src_1.OrderByDirection.DESC, }, ]); }); it('The case sensitivity of the "orderBy" key is not important', async () => { await send(`/?ORDERBY=-name`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, [ { key: 'name', direction: src_1.OrderByDirection.DESC, }, ]); }); it('The case sensitivity of the orderBy *value* is kept', async () => { await send(`/?orderBy=-NAME`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, [ { key: 'NAME', direction: src_1.OrderByDirection.DESC, }, ]); }); }); describe('getOrderBys - Case sensitive', () => { before(async () => { (0, testingConfigurations_1.setTestingConfigurations)(true); await startServer(true); }); after(() => { server.close(); }); it('The case sensitivity of the "orderBy" key is important', async () => { await send(`/?ORDERBY=-name`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, []); }); it('The case sensitivity of the orderBy *value* is kept', async () => { await send(`/?orderBy=-NAME`); const orderBys = httpUtils_1.httpUtils.getOrderBys(expressRequest); chai_1.assert.deepEqual(orderBys, [ { key: 'NAME', direction: src_1.OrderByDirection.DESC, }, ]); }); }); }); }); //# sourceMappingURL=httpUtils.test.js.map