Cleaning up the code.

Mostly trying to get test coverage up by refactoring and cleaning
methods.
This commit is contained in:
David Diaz 2017-12-22 18:52:40 -06:00
parent 35983addf7
commit 930a957bf3
14 changed files with 2163 additions and 700 deletions

View File

@ -0,0 +1,22 @@
function Request(request) {
if (Request.fail) {
throw new Error();
}
return {
request: {
headers: request.headers,
body: request.body,
uri: {
href: request.url
}
},
statusCode: 200,
headers: [],
body: '{"hello": "world"}'
};
}
Request.fail = false;
module.exports = Request;

19
__mocks__/requireg.js Normal file
View File

@ -0,0 +1,19 @@
module.exports = function(name) {
return function(settings) {
let response = {
name: `${name}`,
preRequest(request) {
request.wasModified = true;
return request;
},
postResponse(response) {
response.changed = true;
return response;
}
};
return response;
};
};

View File

@ -0,0 +1,56 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Request It should execute a request 1`] = `
Object {
"body": "{\\"hello\\": \\"world\\"}",
"request": Object {
"body": Object {
"username": "seich",
},
"endpoint": "http://martianwabbit.com/user",
"headers": Object {
"authentication": "BEARER abc123",
},
},
"response": Object {
"body": "{\\"hello\\": \\"world\\"}",
"headers": Array [],
"status": 200,
},
}
`;
exports[`Request It should execute a request 2`] = `
Object {
"body": "{\\"hello\\": \\"world\\"}",
"request": Object {
"body": Object {},
"endpoint": "http://martianwabbit.com/user",
"headers": Object {},
},
"response": Object {
"body": "{\\"hello\\": \\"world\\"}",
"headers": Array [],
"status": 200,
},
}
`;
exports[`Request It should use modifiers 1`] = `
Array [
Array [
Object {
"endpoint": "http://martianwabbit.com/user",
"headers": Object {},
"method": "GET",
"payload": Object {},
"query": Object {},
},
Object {
"alias": "show",
"host": "http://martianwabbit.com",
"request": "GET /user",
},
],
]
`;

View File

@ -1,4 +1,3 @@
const fs = require('fs');
const Beau = require('../beau'); const Beau = require('../beau');
const yaml = require('js-yaml'); const yaml = require('js-yaml');

View File

@ -1,17 +1,19 @@
const Request = require('../request'); const Request = require('../request');
const RequestCache = require('../requestCache'); const RequestCache = require('../requestCache');
const RequestList = require('../requestList'); const RequestList = require('../requestList');
const requestPromiseNativeMock = require('request-promise-native');
describe('Request', () => { describe('Request', () => {
let req; let req;
let cache; let cache;
let request; let request;
let requestWithoutDependencies;
beforeEach(() => { beforeEach(() => {
req = { req = {
request: 'POST /user', request: 'POST /user',
host: 'http://martianwabbit.com', host: 'http://martianwabbit.com',
alias: 'profile', alias: 'update',
params: { params: {
userId: '$profile.UserId' userId: '$profile.UserId'
}, },
@ -28,7 +30,13 @@ describe('Request', () => {
cache.add('$profile', { UserId: 14 }); cache.add('$profile', { UserId: 14 });
request = new Request(req); request = new Request(req);
requestWithoutDependencies = new Request({
host: 'http://martianwabbit.com',
request: 'GET /user',
alias: 'show'
});
requestPromiseNativeMock.fail = false;
}); });
test('It should load up the given request', () => { test('It should load up the given request', () => {
@ -44,4 +52,33 @@ describe('Request', () => {
expect(request.DEPENDENCIES).toContain('session'); expect(request.DEPENDENCIES).toContain('session');
expect(request.DEPENDENCIES).toContain('profile'); expect(request.DEPENDENCIES).toContain('profile');
}); });
test('It should execute a request', async () => {
await expect(request.exec([], cache)).resolves.toMatchSnapshot();
await expect(
requestWithoutDependencies.exec()
).resolves.toMatchSnapshot();
});
test('It should throw if the request fails', async () => {
requestPromiseNativeMock.fail = true;
await expect(requestWithoutDependencies.exec()).rejects.toThrow(Error);
});
test('It should use modifiers', async () => {
const preRequest = jest.fn();
const withPreRequest = [{ preRequest }];
const notCalled = jest.fn();
const nonModifiers = [{ notCalled }];
await requestWithoutDependencies.exec(withPreRequest);
expect(preRequest).toHaveBeenCalled();
expect(preRequest.mock.calls).toMatchSnapshot();
await requestWithoutDependencies.exec(nonModifiers);
expect(notCalled).not.toHaveBeenCalled();
});
}); });

View File

@ -10,13 +10,16 @@ describe('Request Cache', () => {
hello: 'World' hello: 'World'
}); });
cache.add('$array', [{ cache.add('$array', [
{
id: 1, id: 1,
name: 'Sergio' name: 'Sergio'
}, { },
{
id: 2, id: 2,
name: 'Angela' name: 'Angela'
}]); }
]);
}); });
it('should add keys to the cache', () => { it('should add keys to the cache', () => {
@ -29,23 +32,31 @@ describe('Request Cache', () => {
}); });
it('should throw when given an invalid path', () => { it('should throw when given an invalid path', () => {
expect(cache.get('$session.hello')).toThrow(); expect(() => cache.get('$session.world')).toThrow();
}); });
}); });
describe('parse', () => { describe('parse', () => {
it('should transform variables in strings using it\'s cache', () => { it("should transform variables in strings using it's cache", () => {
expect(cache.parse('Hello $session.hello')).toBe('Hello World'); expect(cache.parse('Hello $session.hello')).toBe('Hello World');
}); });
it('should go transform variables in all values when given an object', () => { it('should go transform variables in all values when given an object', () => {
let parsed = cache.parse({ hello: 'hello $session.hello', earth: '$session.hello' }); let parsed = cache.parse({
hello: 'hello $session.hello',
earth: '$session.hello'
});
expect(parsed.hello).toBe('hello World'); expect(parsed.hello).toBe('hello World');
expect(parsed.earth).toBe('World'); expect(parsed.earth).toBe('World');
}); });
it('should return every non-string value as-is', () => { it('should return every non-string value as-is', () => {
let parsed = cache.parse({ number: 1, nulled: null, truthy: false, hello: '$session.hello' }); let parsed = cache.parse({
number: 1,
nulled: null,
truthy: false,
hello: '$session.hello'
});
expect(parsed.number).toBe(1); expect(parsed.number).toBe(1);
expect(parsed.nulled).toBeNull(); expect(parsed.nulled).toBeNull();
expect(parsed.truthy).toBe(false); expect(parsed.truthy).toBe(false);
@ -53,16 +64,20 @@ describe('Request Cache', () => {
}); });
it('should parse arrays as well', () => { it('should parse arrays as well', () => {
let parsed = cache.parse({ hello: '$array.0.name' }) let parsed = cache.parse({ hello: '$array.0.name' });
expect(parsed.hello).toBe('Sergio'); expect(parsed.hello).toBe('Sergio');
}); });
it('should return an object when given an undefined value', () => { it('should return an object when given an undefined value', () => {
expect(Object.keys(cache.parse(undefined)).length).toBe(0) expect(Object.keys(cache.parse(undefined)).length).toBe(0);
}); });
it('should parse any value other than undefined', () => { it('should parse any value other than undefined', () => {
expect(cache.parse('Hello $session.hello')).toBe('Hello World'); expect(cache.parse('Hello $session.hello')).toBe('Hello World');
}); });
it('should return null when passed null', () => {
expect(cache.parse(null)).toBe(null);
});
}); });
}); });

View File

@ -1,8 +1,9 @@
const RequestList = require('../requestList'); const RequestList = require('../requestList');
const requestPromiseNativeMock = require('request-promise-native');
describe('RequestList', () => { describe('RequestList', () => {
let host = 'http://martianwabbit.com'; const host = 'http://martianwabbit.com';
let doc = { const doc = {
'POST /session': null, 'POST /session': null,
'Not a Request': null, 'Not a Request': null,
'GET /post': 'get-posts', 'GET /post': 'get-posts',
@ -15,13 +16,53 @@ describe('RequestList', () => {
} }
}; };
let requests;
beforeEach(() => {
requestPromiseNativeMock.fail = false;
requests = new RequestList(doc, {
HOST: host,
PLUGINS: [
{
'beau-jwt': {
data: {
secret: 'shhh.',
userId: 412
}
}
},
'beau-document'
]
});
});
it('should load valid requests', () => { it('should load valid requests', () => {
let requests = new RequestList(doc, { HOST: host }); const request = requests.list[0];
let request = requests.list[0];
expect(requests.list.length).toBe(3); expect(requests.list.length).toBe(3);
expect(request.VERB).toBe('POST'); expect(request.VERB).toBe('POST');
expect(request.ENDPOINT).toBe(host + '/session'); expect(request.ENDPOINT).toBe(host + '/session');
}); });
it('should fetch dependencies', () => {
requests.fetchDependencies(['get-posts']);
});
it('should load plugins', () => {
const pluginLessList = new RequestList();
expect(requests.modifiers.length).toBe(2);
expect(pluginLessList.modifiers.length).toBe(0);
});
it('should execute requests by alias.', async () => {
await requests.execByAlias('user');
});
it('should fail if the request fails', async () => {
requestPromiseNativeMock.fail = true;
await expect(requests.execByAlias('user')).rejects.toThrow(Error);
});
it('should fail if the alias is not found', async () => {
await expect(requests.execByAlias('notAnAlias')).rejects.toThrow(Error);
});
}); });

10
beau.js
View File

@ -9,22 +9,20 @@ class Beau {
PLUGINS: [] PLUGINS: []
}; };
this.configKeys = Object.keys(this.defaults); this.configKeys = Object.keys(this.defaults);
this.config = this.loadConfig(doc, this.defaults); this.config = this.loadConfig(doc);
this.requests = new RequestList(doc, this.config); this.requests = new RequestList(doc, this.config);
} }
loadConfig(doc, defaults = {}) { loadConfig(doc) {
var result = defaults; let result = this.defaults;
Object.keys(doc) Object.keys(doc)
.filter(k => this.configKeys.indexOf(k.toUpperCase()) > -1) .filter(k => this.configKeys.indexOf(k.toUpperCase()) > -1)
.forEach(k => result[k.toUpperCase()] = doc[k]); .forEach(k => (result[k.toUpperCase()] = doc[k]));
return result; return result;
} }
} }
module.exports = Beau; module.exports = Beau;

View File

@ -75,7 +75,7 @@ program
process.exit(0); process.exit(0);
} catch (err) { } catch (err) {
new Line().output(); new Line().output();
console.error(err); console.error(err.message);
process.exit(1); process.exit(1);
} }
}); });

2509
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "beau", "name": "beau",
"version": "0.4.0", "version": "0.4.1",
"description": "A tool for testing JSON APIs", "description": "A tool for testing JSON APIs",
"main": "beau.js", "main": "beau.js",
"author": "Sergio Diaz <seich@martianwabbit.com>", "author": "Sergio Diaz <seich@martianwabbit.com>",
@ -21,7 +21,7 @@
}, },
"repository": "git@github.com:Seich/Beau.git", "repository": "git@github.com:Seich/Beau.git",
"devDependencies": { "devDependencies": {
"jest": "^17.0.3" "jest": "22.0.4"
}, },
"jest": { "jest": {
"testEnvironment": "node", "testEnvironment": "node",

View File

@ -8,10 +8,18 @@ class Request {
let config = {}; let config = {};
this.originalRequest = req; this.originalRequest = req;
Object.keys(req).forEach(k => config[k.toUpperCase()] = req[k]); Object.keys(req).forEach(k => (config[k.toUpperCase()] = req[k]));
let { REQUEST, ALIAS, PAYLOAD, HOST, PARAMS, HEADERS, DOCUMENTATION } = config; const {
let { verb, endpoint } = this.parseRequest(REQUEST); REQUEST,
ALIAS,
PAYLOAD,
HOST,
PARAMS,
HEADERS,
DOCUMENTATION
} = config;
const { verb, endpoint } = this.parseRequest(REQUEST);
this.VERB = verb; this.VERB = verb;
this.ENDPOINT = HOST + endpoint; this.ENDPOINT = HOST + endpoint;
@ -41,14 +49,14 @@ class Request {
findDependencies(request, set = new Set()) { findDependencies(request, set = new Set()) {
if (typeof request === 'object') { if (typeof request === 'object') {
let keys = Object.keys(request).filter(key => key !== 'ALIAS'); const keys = Object.keys(request).filter(key => key !== 'ALIAS');
keys.forEach(key => { keys.forEach(key => {
set = this.findDependencies(request[key], set); set = this.findDependencies(request[key], set);
}); });
} else if (typeof request === 'string') { } else if (typeof request === 'string') {
let matches = request.match(replacementRegex) || []; const matches = request.match(replacementRegex) || [];
let deps = matches.map(m => m.split('.')[0].substring(1)); const deps = matches.map(m => m.split('.')[0].substring(1));
return new Set([...set, ...deps]); return new Set([...set, ...deps]);
} }
@ -56,12 +64,8 @@ class Request {
return set; return set;
} }
async exec(modifiers = [], requestList) { async exec(modifiers = [], cache = new RequestCache()) {
let dependencies = Array.from(this.DEPENDENCIES); const settings = {
let cache = await requestList.fetchDependencies(dependencies);
let settings = {
endpoint: cache.parse(this.ENDPOINT), endpoint: cache.parse(this.ENDPOINT),
method: this.VERB, method: this.VERB,
headers: cache.parse(this.HEADERS), headers: cache.parse(this.HEADERS),
@ -76,7 +80,7 @@ class Request {
}); });
try { try {
let response = await request({ const response = await request({
url: settings.endpoint, url: settings.endpoint,
method: settings.method, method: settings.method,
headers: settings.headers, headers: settings.headers,
@ -88,7 +92,7 @@ class Request {
resolveWithFullResponse: true resolveWithFullResponse: true
}); });
let results = { const results = {
request: { request: {
headers: response.request.headers, headers: response.request.headers,
body: response.request.body, body: response.request.body,

View File

@ -11,9 +11,7 @@ class RequestCache {
get(path) { get(path) {
let result = this.$cache; let result = this.$cache;
path.split('.').forEach(part => { path.split('.').forEach(part => {
if (result === undefined) return;
result = result[part]; result = result[part];
}); });
@ -40,7 +38,7 @@ class RequestCache {
} }
if (type === 'object') { if (type === 'object') {
Object.keys(item).forEach(k => item[k] = this.parse(item[k])); Object.keys(item).forEach(k => (item[k] = this.parse(item[k])));
return item; return item;
} }

View File

@ -13,14 +13,15 @@ class RequestList {
} }
async execByAlias(alias) { async execByAlias(alias) {
let request = this.list.find(r => r.ALIAS === alias); const request = this.list.find(r => r.ALIAS === alias);
if (typeof request === 'undefined') { if (typeof request === 'undefined') {
return Promise.reject(`${alias} not found among the requests.`); throw new Error(`${alias} not found among the requests.`);
} }
try { try {
let response = await request.exec(this.modifiers, this); await this.fetchDependencies(Array.from(request.DEPENDENCIES));
const response = await request.exec(this.modifiers, this.cache);
this.modifiers.forEach(mod => { this.modifiers.forEach(mod => {
if (typeof mod.postResponse !== 'undefined') { if (typeof mod.postResponse !== 'undefined') {
@ -44,13 +45,13 @@ class RequestList {
} }
loadRequests(doc) { loadRequests(doc) {
let requests = Object.keys(doc).filter(key => { const requests = Object.keys(doc).filter(key => {
let verb = key.split(' ')[0].toUpperCase(); const verb = key.split(' ')[0].toUpperCase();
return httpVerbs.indexOf(verb) > -1; return httpVerbs.indexOf(verb) > -1;
}); });
return requests.map(request => { return requests.map(request => {
let type = typeof doc[request]; const type = typeof doc[request];
if (type === 'string') { if (type === 'string') {
doc[request] = { doc[request] = {
@ -69,7 +70,7 @@ class RequestList {
loadPlugins() { loadPlugins() {
if (typeof this.config.PLUGINS === 'undefined') { if (typeof this.config.PLUGINS === 'undefined') {
return; return [];
} }
return this.config.PLUGINS.map(plugin => { return this.config.PLUGINS.map(plugin => {