Skip to content

Commit

Permalink
bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mrak committed Mar 27, 2021
1 parent d28a61e commit b030cab
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 135 deletions.
50 changes: 20 additions & 30 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ function onError (err, port, location) {
process.exit();
}

function onEndpointLoaded (endpoint) {
out.notice('Loaded: ' + endpoint.request.method + ' ' + endpoint.request.url);
}

function setupStartOptions (options) {
let key;

Expand Down Expand Up @@ -81,6 +77,7 @@ class Stubby {
}

async start (o) {
const self = this;
const options = setupStartOptions(o);

await this.stop();
Expand All @@ -91,22 +88,24 @@ class Stubby {
if (options.datadir != null) { this.endpoints.datadir = options.datadir; }
if (options['case-sensitive-headers'] != null) { this.endpoints.caseSensitiveHeaders = options['case-sensitive-headers']; }

await this.endpoints.create(options.data, onEndpointLoaded);
this.endpoints.create(options.data).forEach((endpoint) => {
out.notice('Loaded: ' + endpoint.request.method + ' ' + endpoint.request.url);
});

this.tlsPortal = https.createServer(createHttpsOptions(options), new Stubs(this.endpoints).server);
this.tlsPortal.on('listening', function () { onListening('Stubs', options.tls, 'https', options.location); });
this.tlsPortal.on('error', function (err) { onError(err, options.tls, options.location); });
this.tlsPortal.listen(options.tls, options.location);
await new Promise((resolve) => self.tlsPortal.listen(options.tls, options.location, resolve));

this.stubsPortal = http.createServer(new Stubs(this.endpoints).server);
this.stubsPortal.on('listening', function () { onListening('Stubs', options.stubs, 'http', options.location); });
this.stubsPortal.on('error', function (err) { onError(err, options.stubs, options.location); });
this.stubsPortal.listen(options.stubs, options.location);
await new Promise((resolve) => this.stubsPortal.listen(options.stubs, options.location, resolve));

this.adminPortal = http.createServer(new Admin(this.endpoints).server);
this.adminPortal.on('listening', function () { onListening('Admin', options.admin, 'http', options.location); });
this.adminPortal.on('error', function (err) { onError(err, options.admin, options.location); });
this.adminPortal.listen(options.admin, options.location);
await new Promise((resolve) => this.adminPortal.listen(options.admin, options.location, resolve));

if (options.watch) { this.watcher = new Watcher(this.endpoints, options.watch); }

Expand All @@ -116,42 +115,33 @@ class Stubby {
async stop () {
if (this.watcher != null) { this.watcher.deactivate(); }

if (this.adminPortal && this.adminPortal.address()) { await promisify(this.adminPortal, 'close'); }
if (this.stubsPortal && this.stubsPortal.address()) { await promisify(this.stubsPortal, 'close'); }
if (this.tlsPortal && this.tlsPortal.address()) { await promisify(this.tlsPortal, 'close'); }
if (this.adminPortal && this.adminPortal.address()) await new Promise((resolve) => (this.adminPortal.close(resolve)));
if (this.stubsPortal && this.stubsPortal.address()) await new Promise((resolve) => (this.stubsPortal.close(resolve)));
if (this.tlsPortal && this.tlsPortal.address()) await new Promise((resolve) => (this.tlsPortal.close(resolve)));
}

async post (data) {
post (data) {
if (contract(data)) {
throw new Error(couldNotSave);
} else {
await this.endpoints.create(data);
this.endpoints.create(data);
}
}

async get (id) {
if (id == null) await this.endpoints.gather();
else await this.endpoints.retrieve(id);
get (id) {
if (id == null) return this.endpoints.gather();
else return this.endpoints.retrieve(id);
}

async put (id, data) {
put (id, data) {
if (contract(data)) throw new Error(couldNotSave);
else await this.endpoints.update(id, data);
else return this.endpoints.update(id, data);
}

async delete (id) {
if (id == null) await this.endpoints.deleteAll();
else await this.endpoints.delete(id);
delete (id) {
if (id == null) this.endpoints.deleteAll();
else this.endpoints.delete(id);
}
}

function promisify (obj, method) {
return new Promise((resolve, reject) => {
obj[method]((err) => {
if (err) reject(err);
else resolve();
});
});
}

module.exports.Stubby = Stubby;
21 changes: 9 additions & 12 deletions src/models/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ const NOT_FOUND = "Endpoint with the given id doesn't exist.";
const NO_MATCH = "Endpoint with given request doesn't exist.";
const sleep = util.promisify(setTimeout);

function noop () {}

class Endpoints {
constructor (data, datadir) {
if (datadir == null) { datadir = process.cwd(); }
Expand All @@ -24,50 +22,49 @@ class Endpoints {
this.create(data);
}

async create (data, callback) {
create (data) {
const self = this;
if (callback == null) callback = noop;

function insert (item) {
item = new Endpoint(item, self.datadir, self.caseSensitiveHeaders);
item.id = ++self.lastId;
self.db[item.id] = item;
callback(clone(item));
return item;
}

if (data instanceof Array) {
data.forEach(insert);
return data.map(insert);
} else if (data) {
insert(data);
return insert(data);
}
}

async retrieve (id) {
retrieve (id) {
if (!this.db[id]) throw new Error(NOT_FOUND);

return clone(this.db[id]);
}

async update (id, data) {
update (id, data) {
if (!this.db[id]) throw new Error(NOT_FOUND);

const endpoint = new Endpoint(data, this.datadir);
endpoint.id = id;
this.db[endpoint.id] = endpoint;
}

async delete (id) {
delete (id) {
if (!this.db[id]) throw new Error(NOT_FOUND);

delete this.db[id];
}

async deleteAll () {
deleteAll () {
delete this.db;
this.db = {};
}

async gather () {
gather () {
let id;
const all = [];

Expand Down
23 changes: 10 additions & 13 deletions src/portals/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,58 +52,55 @@ class Admin extends Portal {

if (id) {
try {
await this.endpoints.delete(id);
this.endpoints.delete(id);
self.noContent(response);
} catch { self.notFound(response); }
} else if (request.url === '/') {
try {
await this.endpoints.deleteAll();
this.endpoints.deleteAll();
self.noContent(response);
} catch { self.notFound(response); }
} else {
this.notSupported(response);
}
}

async goGET (request, response) {
goGET (request, response) {
const id = this.getId(request.url);

if (id) {
try {
const endpoint = await this.endpoints.retrieve(id);
const endpoint = this.endpoints.retrieve(id);
this.ok(response, endpoint);
} catch (err) { this.notFound(response); }
} else {
const data = await this.endpoints.gather();
const data = this.endpoints.gather();
if (data.length === 0) { this.noContent(response); } else { this.ok(response, data); }
}
}

async processPUT (id, data, response) {
processPUT (id, data, response) {
try { data = JSON.parse(data); } catch (e) { return this.badRequest(response); }

const errors = this.contract(data);
if (errors) { return this.badRequest(response, errors); }

try {
await this.endpoints.update(id, data);
this.endpoints.update(id, data);
this.noContent(response);
} catch (_) { this.notFound(response); }
}

async processPOST (data, response, request) {
processPOST (data, response, request) {
const self = this;

try { data = JSON.parse(data); } catch (e) { return this.badRequest(response); }

const errors = this.contract(data);
if (errors) { return this.badRequest(response, errors); }

function callback (endpoint) {
self.created(response, request, endpoint.id);
}

await this.endpoints.create(data, callback);
const endpoint = this.endpoints.create(data);
self.created(response, request, endpoint.id);
}

ok (response, result) {
Expand Down
12 changes: 7 additions & 5 deletions test/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ describe('Admin', function () {
this.sandbox.spy(console, 'info');

endpoints = {
create: this.sandbox.spy(),
create: this.sandbox.stub(),
retrieve: this.sandbox.spy(),
update: this.sandbox.spy(),
delete: this.sandbox.spy(),
deleteAll: this.sandbox.spy(),
gather: this.sandbox.spy()
gather: this.sandbox.stub()
};
sut = new Admin(endpoints, true);
request = {
Expand Down Expand Up @@ -208,7 +208,7 @@ describe('Admin', function () {

it('should call goPUT if method is PUT', function () {
this.sandbox.stub(sut, 'urlValid').returns(true);
this.sandbox.spy(sut, 'goPUT');
this.sandbox.stub(sut, 'goPUT');
request.method = 'PUT';

sut.server(request, response);
Expand All @@ -218,7 +218,7 @@ describe('Admin', function () {

it('should call goGET if method is GET', function () {
this.sandbox.stub(sut, 'urlValid').returns(true);
this.sandbox.spy(sut, 'goGET');
this.sandbox.stub(sut, 'goGET');

request.method = 'GET';
sut.server(request, response);
Expand All @@ -227,7 +227,7 @@ describe('Admin', function () {

it('should call goDELETE if method is DELETE', function () {
this.sandbox.stub(sut, 'urlValid').returns(true);
this.sandbox.spy(sut, 'goDELETE');
this.sandbox.stub(sut, 'goDELETE');
request.method = 'DELETE';

sut.server(request, response);
Expand Down Expand Up @@ -298,6 +298,7 @@ describe('Admin', function () {
describe('processPOST', function () {
it('should create item if data is JSON parsable', function () {
const data = '{"property":"value"}';
endpoints.create.returns({ id: 1 });

sut.processPOST(data, response, request);

Expand Down Expand Up @@ -358,6 +359,7 @@ describe('Admin', function () {
describe('goGET', function () {
it('should gather all for the root url', function () {
this.sandbox.stub(sut, 'getId').returns('');
endpoints.gather.returns([]);

sut.goGET(request, response);

Expand Down
48 changes: 1 addition & 47 deletions test/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,7 @@

const Endpoint = require('../src/models/endpoint');
const assert = require('assert');

function waitsFor (fn, message, range, finish, time) {
const min = range[0] != null ? range[0] : 0;
const max = range[1] != null ? range[1] : range;

if (time == null) { time = process.hrtime(); }

const temp = time == null ? process.hrtime() : process.hrtime(time);
const seconds = temp[0];
const nanoseconds = temp[1];
const elapsed = seconds * 1000 + nanoseconds / 1000000;

assert(elapsed < max, 'Timed out waiting ' + max + 'ms for ' + message);

if (fn()) {
assert(elapsed > min, 'Condition succeeded before ' + min + 'ms were up');
return finish();
}

setTimeout(function () {
waitsFor(fn, message, range, finish, time);
}, 1);
}

function compareOneWay (left, right) {
let key, value;

for (key in left) {
if (!Object.prototype.hasOwnProperty.call(left, key)) { continue; }

value = left[key];

if (right[key] !== value) { continue; }

if (typeof value === 'object') {
if (!compareObjects(value, right[key])) { continue; }
}

return false;
}

return true;
}

function compareObjects (one, two) {
return compareOneWay(one, two) && compareOneWay(two, one);
}
const waitsFor = require('./helpers/waits-for');

describe('Endpoint', function () {
beforeEach(function () {
Expand Down
Loading

0 comments on commit b030cab

Please sign in to comment.