@villedemontreal/http-request
Version:
HTTP utilities - send HTTP requests with proper headers, etc.
625 lines • 30.9 kB
JavaScript
;
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