Merge branch 'dev' into servo-level-up
* dev: Remove `self` references in favor of #bind Stop using #fetch here for now, it just breaks tests Just use arguments directly instead of array Remove Utils#bind in preference of built-in fn Consolidate logstring generation into private fn Experiment with Utils.fetch in Driver class Add #fetch Utility function Fix JSHint errors Fix an edge case and improve tests for API
This commit is contained in:
commit
ca2bf7e5a9
|
@ -29,7 +29,6 @@ module.exports = Adaptor = function Adaptor(opts) {
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.self = this;
|
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
this.connection = opts.connection;
|
this.connection = opts.connection;
|
||||||
};
|
};
|
||||||
|
|
26
lib/api.js
26
lib/api.js
|
@ -17,8 +17,6 @@ var express = require('express'),
|
||||||
var Logger = require('./logger');
|
var Logger = require('./logger');
|
||||||
|
|
||||||
var API = module.exports = function API(opts) {
|
var API = module.exports = function API(opts) {
|
||||||
var self = this;
|
|
||||||
|
|
||||||
if (opts == null) {
|
if (opts == null) {
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
|
@ -31,17 +29,17 @@ var API = module.exports = function API(opts) {
|
||||||
|
|
||||||
this.express.set('title', 'Cylon API Server');
|
this.express.set('title', 'Cylon API Server');
|
||||||
|
|
||||||
this.express.use(self.setupAuth());
|
this.express.use(this.setupAuth());
|
||||||
this.express.use(bodyParser());
|
this.express.use(bodyParser());
|
||||||
this.express.use(express["static"](__dirname + "/../node_modules/robeaux/"));
|
this.express.use(express["static"](__dirname + "/../node_modules/robeaux/"));
|
||||||
|
|
||||||
// set CORS headers for API requests
|
// set CORS headers for API requests
|
||||||
this.express.use(function(req, res, next) {
|
this.express.use(function(req, res, next) {
|
||||||
res.set("Access-Control-Allow-Origin", self.CORS || "*");
|
res.set("Access-Control-Allow-Origin", this.CORS || "*");
|
||||||
res.set("Access-Control-Allow-Headers", "Content-Type");
|
res.set("Access-Control-Allow-Headers", "Content-Type");
|
||||||
res.set('Content-Type', 'application/json');
|
res.set('Content-Type', 'application/json');
|
||||||
return next();
|
return next();
|
||||||
});
|
}.bind(this));
|
||||||
|
|
||||||
// extracts command params from request
|
// extracts command params from request
|
||||||
this.express.use(function(req, res, next) {
|
this.express.use(function(req, res, next) {
|
||||||
|
@ -58,13 +56,13 @@ var API = module.exports = function API(opts) {
|
||||||
|
|
||||||
for (var p in container) {
|
for (var p in container) {
|
||||||
req.commandParams.push(container[p]);
|
req.commandParams.push(container[p]);
|
||||||
};
|
}
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
// load route definitions
|
// load route definitions
|
||||||
this.express.use('/', require('./api/routes'))
|
this.express.use('/', require('./api/routes'));
|
||||||
};
|
};
|
||||||
|
|
||||||
API.prototype.defaults = {
|
API.prototype.defaults = {
|
||||||
|
@ -90,7 +88,7 @@ API.prototype.createServer = function createServer() {
|
||||||
cert: fs.readFileSync(this.ssl.cert)
|
cert: fs.readFileSync(this.ssl.cert)
|
||||||
}, this.express);
|
}, this.express);
|
||||||
} else {
|
} else {
|
||||||
Logger.warn("API using insecure connection. We recommend using an SSL certificate with Cylon.")
|
Logger.warn("API using insecure connection. We recommend using an SSL certificate with Cylon.");
|
||||||
this.server = this.express;
|
this.server = this.express;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -98,7 +96,7 @@ API.prototype.createServer = function createServer() {
|
||||||
API.prototype.setupAuth = function setupAuth() {
|
API.prototype.setupAuth = function setupAuth() {
|
||||||
var authfn = function auth(req, res, next) { next(); };
|
var authfn = function auth(req, res, next) { next(); };
|
||||||
|
|
||||||
if (typeof(this.auth) === 'object' && this.auth.type) {
|
if (!!this.auth && typeof(this.auth) === 'object' && this.auth.type) {
|
||||||
var type = this.auth.type,
|
var type = this.auth.type,
|
||||||
module = "./api/auth/" + type,
|
module = "./api/auth/" + type,
|
||||||
filename = path.normalize(__dirname + "/" + module + ".js"),
|
filename = path.normalize(__dirname + "/" + module + ".js"),
|
||||||
|
@ -113,13 +111,11 @@ API.prototype.setupAuth = function setupAuth() {
|
||||||
};
|
};
|
||||||
|
|
||||||
API.prototype.listen = function() {
|
API.prototype.listen = function() {
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.server.listen(this.port, this.host, null, function() {
|
this.server.listen(this.port, this.host, null, function() {
|
||||||
var title = self.express.get('title');
|
var title = this.express.get('title');
|
||||||
var protocol = self.ssl ? "https" : "http";
|
var protocol = this.ssl ? "https" : "http";
|
||||||
|
|
||||||
Logger.info(title + " is now online.");
|
Logger.info(title + " is now online.");
|
||||||
Logger.info("Listening at " + protocol + "://" + self.host + ":" + self.port);
|
Logger.info("Listening at " + protocol + "://" + this.host + ":" + this.port);
|
||||||
});
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,17 +37,16 @@ module.exports = Connection = function Connection(opts) {
|
||||||
opts.id = Math.floor(Math.random() * 10000);
|
opts.id = Math.floor(Math.random() * 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connect = Utils.bind(this.connect, this);
|
this.connect = this.connect.bind(this);
|
||||||
|
|
||||||
this.self = this;
|
|
||||||
this.robot = opts.robot;
|
this.robot = opts.robot;
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
this.connection_id = opts.id;
|
this.connection_id = opts.id;
|
||||||
this.port = opts.port;
|
this.port = opts.port;
|
||||||
this.adaptor = this.initAdaptor(opts);
|
this.adaptor = this.initAdaptor(opts);
|
||||||
|
|
||||||
Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this.self);
|
Utils.proxyFunctionsToObject(this.adaptor.commands, this.adaptor, this);
|
||||||
}
|
};
|
||||||
|
|
||||||
Utils.subclass(Connection, EventEmitter);
|
Utils.subclass(Connection, EventEmitter);
|
||||||
|
|
||||||
|
@ -69,15 +68,7 @@ Connection.prototype.toJSON = function() {
|
||||||
//
|
//
|
||||||
// Returns the result of the supplied callback function
|
// Returns the result of the supplied callback function
|
||||||
Connection.prototype.connect = function(callback) {
|
Connection.prototype.connect = function(callback) {
|
||||||
var msg = "Connecting to " + this.name;
|
var msg = this._logstring("Connecting to");
|
||||||
var msg = "Connecting to '" + this.name + "'";
|
|
||||||
|
|
||||||
if (this.port != null) {
|
|
||||||
msg += " on port " + this.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg += ".";
|
|
||||||
|
|
||||||
Logger.info(msg);
|
Logger.info(msg);
|
||||||
return this.adaptor.connect(callback);
|
return this.adaptor.connect(callback);
|
||||||
};
|
};
|
||||||
|
@ -88,14 +79,7 @@ Connection.prototype.connect = function(callback) {
|
||||||
//
|
//
|
||||||
// Returns nothing
|
// Returns nothing
|
||||||
Connection.prototype.disconnect = function(callback) {
|
Connection.prototype.disconnect = function(callback) {
|
||||||
var msg = "Disconnecting from '" + this.name + "'";
|
var msg = this._logstring("Disconnecting from");
|
||||||
|
|
||||||
if (this.port != null) {
|
|
||||||
msg += " on port " + this.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg += ".";
|
|
||||||
|
|
||||||
Logger.info(msg);
|
Logger.info(msg);
|
||||||
return this.adaptor.disconnect(callback);
|
return this.adaptor.disconnect(callback);
|
||||||
};
|
};
|
||||||
|
@ -108,7 +92,7 @@ Connection.prototype.disconnect = function(callback) {
|
||||||
// Returns the set-up adaptor
|
// Returns the set-up adaptor
|
||||||
Connection.prototype.initAdaptor = function(opts) {
|
Connection.prototype.initAdaptor = function(opts) {
|
||||||
Logger.debug("Loading adaptor '" + opts.adaptor + "'.");
|
Logger.debug("Loading adaptor '" + opts.adaptor + "'.");
|
||||||
return this.robot.initAdaptor(opts.adaptor, this.self, opts);
|
return this.robot.initAdaptor(opts.adaptor, this, opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public: Halt the adaptor's connection
|
// Public: Halt the adaptor's connection
|
||||||
|
@ -126,3 +110,15 @@ Connection.prototype.halt = function(callback) {
|
||||||
Logger.info(msg);
|
Logger.info(msg);
|
||||||
return this.disconnect(callback);
|
return this.disconnect(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Connection.prototype._logstring = function _logstring(action) {
|
||||||
|
var msg = action + " '" + this.name + "'";
|
||||||
|
|
||||||
|
if (this.port != null) {
|
||||||
|
msg += " on port " + this.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += ".";
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
};
|
||||||
|
|
|
@ -101,7 +101,7 @@ if (process.platform === "win32") {
|
||||||
readline.createInterface(io).on("SIGINT", function() {
|
readline.createInterface(io).on("SIGINT", function() {
|
||||||
process.emit("SIGINT");
|
process.emit("SIGINT");
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
process.on("SIGINT", function() {
|
process.on("SIGINT", function() {
|
||||||
Cylon.halt(function() {
|
Cylon.halt(function() {
|
||||||
|
|
|
@ -28,8 +28,8 @@ var Device = module.exports = function Device(opts) {
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.halt = Utils.bind(this.halt, this);
|
this.halt = this.halt.bind(this);
|
||||||
this.start = Utils.bind(this.start, this);
|
this.start = this.start.bind(this);
|
||||||
|
|
||||||
this.robot = opts.robot;
|
this.robot = opts.robot;
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
|
|
|
@ -12,11 +12,6 @@ var Basestar = require('./basestar'),
|
||||||
Logger = require('./logger'),
|
Logger = require('./logger'),
|
||||||
Utils = require('./utils');
|
Utils = require('./utils');
|
||||||
|
|
||||||
// The Driver class is a base class for Driver classes in external Cylon
|
|
||||||
// modules to use. It offers basic functions for starting/halting that
|
|
||||||
// descendant classes can use.
|
|
||||||
var Driver;
|
|
||||||
|
|
||||||
// Public: Creates a new Driver
|
// Public: Creates a new Driver
|
||||||
//
|
//
|
||||||
// opts - hash of acceptable params
|
// opts - hash of acceptable params
|
||||||
|
@ -24,12 +19,9 @@ var Driver;
|
||||||
// device - Device the driver will use to proxy commands/events
|
// device - Device the driver will use to proxy commands/events
|
||||||
//
|
//
|
||||||
// Returns a new Driver
|
// Returns a new Driver
|
||||||
module.exports = Driver = function Driver(opts) {
|
var Driver = module.exports = function Driver(opts) {
|
||||||
if (opts == null) {
|
opts = opts || {};
|
||||||
opts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.self = this;
|
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
this.device = opts.device;
|
this.device = opts.device;
|
||||||
this.connection = this.device.connection;
|
this.connection = this.device.connection;
|
||||||
|
|
|
@ -28,7 +28,7 @@ var DigitalPin = module.exports = function DigitalPin(opts) {
|
||||||
this.status = 'low';
|
this.status = 'low';
|
||||||
this.ready = false;
|
this.ready = false;
|
||||||
this.mode = opts.mode;
|
this.mode = opts.mode;
|
||||||
}
|
};
|
||||||
|
|
||||||
Utils.subclass(DigitalPin, EventEmitter);
|
Utils.subclass(DigitalPin, EventEmitter);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// The NullLogger is designed for cases where you want absolutely nothing to
|
// The NullLogger is designed for cases where you want absolutely nothing to
|
||||||
// print to anywhere. Every proxied method from the Logger returns a noop.
|
// print to anywhere. Every proxied method from the Logger returns a noop.
|
||||||
var NullLogger = module.exports = function NullLogger() {}
|
var NullLogger = module.exports = function NullLogger() {};
|
||||||
|
|
||||||
NullLogger.prototype.toString = function() {
|
NullLogger.prototype.toString = function() {
|
||||||
return "NullLogger";
|
return "NullLogger";
|
||||||
|
|
20
lib/robot.js
20
lib/robot.js
|
@ -53,8 +53,6 @@ var Robot = module.exports = function Robot(opts) {
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var methods = [
|
var methods = [
|
||||||
"toString",
|
"toString",
|
||||||
"registerDriver",
|
"registerDriver",
|
||||||
|
@ -70,8 +68,8 @@ var Robot = module.exports = function Robot(opts) {
|
||||||
];
|
];
|
||||||
|
|
||||||
methods.forEach(function(method) {
|
methods.forEach(function(method) {
|
||||||
self[method] = Utils.bind(self[method], self);
|
this[method] = this[method].bind(this);
|
||||||
});
|
}.bind(this));
|
||||||
|
|
||||||
this.name = opts.name || this.constructor.randomName();
|
this.name = opts.name || this.constructor.randomName();
|
||||||
this.connections = {};
|
this.connections = {};
|
||||||
|
@ -209,21 +207,19 @@ Robot.prototype._createDevice = function(device) {
|
||||||
//
|
//
|
||||||
// Returns the result of the work
|
// Returns the result of the work
|
||||||
Robot.prototype.start = function() {
|
Robot.prototype.start = function() {
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var begin = function(callback) {
|
var begin = function(callback) {
|
||||||
self.work.call(self, self);
|
this.work.call(this, this);
|
||||||
self.running = true;
|
this.running = true;
|
||||||
self.emit('working');
|
this.emit('working');
|
||||||
|
|
||||||
Logger.info('Working.');
|
Logger.info('Working.');
|
||||||
|
|
||||||
callback(null, true);
|
callback(null, true);
|
||||||
};
|
}.bind(this);
|
||||||
|
|
||||||
Async.series([
|
Async.series([
|
||||||
self.startConnections,
|
this.startConnections,
|
||||||
self.startDevices,
|
this.startDevices,
|
||||||
begin
|
begin
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (!!err) {
|
if (!!err) {
|
||||||
|
|
64
lib/utils.js
64
lib/utils.js
|
@ -121,8 +121,7 @@ var Utils = module.exports = {
|
||||||
|
|
||||||
var fn = function(method) {
|
var fn = function(method) {
|
||||||
return base[method] = function() {
|
return base[method] = function() {
|
||||||
var args = arguments.length >= 1 ? [].slice.call(arguments, 0) : [];
|
return target[method].apply(target, arguments);
|
||||||
return target[method].apply(target, args);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -161,26 +160,59 @@ var Utils = module.exports = {
|
||||||
return base;
|
return base;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Public: Binds an argument to a caller
|
// Public: Analogue to Ruby's Hash#fetch method for looking up object
|
||||||
|
// properties.
|
||||||
//
|
//
|
||||||
// fn - function to bind
|
// obj - object to do property lookup on
|
||||||
// me - value for 'this' scope inside the function
|
// property - string property name to attempt to look up
|
||||||
|
// fallback - either:
|
||||||
|
// - a fallback value to return if `property` can't be found
|
||||||
|
// - a function to be executed if `property` can't be found. The function
|
||||||
|
// will be passed `property` as an argument.
|
||||||
//
|
//
|
||||||
// Examples
|
// Examples
|
||||||
//
|
//
|
||||||
// var me = { hello: "Hello World" };
|
// var object = { property: "hello world" };
|
||||||
// var proxy = { boundMethod: function() { return this.hello; } };
|
// fetch(object, "property");
|
||||||
|
// //=> "hello world"
|
||||||
//
|
//
|
||||||
// proxy.boundMethod = bind(proxy.boundMethod, me);
|
// fetch(object, "notaproperty", "default value");
|
||||||
// proxy.boundMethod();
|
// //=> "default value"
|
||||||
//
|
//
|
||||||
// //=> "Hello World"
|
// var notFound = function(prop) { return prop + " not found!" };
|
||||||
|
// fetch(object, "notaproperty", notFound)
|
||||||
|
// // "notaproperty not found!"
|
||||||
//
|
//
|
||||||
// Returns a function wrapper
|
// var badFallback = function(prop) { prop + " not found!" };
|
||||||
bind: function bind(fn, me) {
|
// fetch(object, "notaproperty", badFallback)
|
||||||
return function() {
|
// // Error: no return value from provided callback function
|
||||||
return fn.apply(me, arguments);
|
//
|
||||||
};
|
// fetch(object, "notaproperty");
|
||||||
|
// // Error: key not found: "notaproperty"
|
||||||
|
//
|
||||||
|
// Returns the value of obj[property], a fallback value, or the results of
|
||||||
|
// running 'fallback'. If the property isn't found, and 'fallback' hasn't been
|
||||||
|
// provided, will throw an error.
|
||||||
|
fetch: function(obj, property, fallback) {
|
||||||
|
if (obj.hasOwnProperty(property)) {
|
||||||
|
return obj[property];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallback === void 0) {
|
||||||
|
throw new Error('key not found: "' + property + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(fallback) === 'function') {
|
||||||
|
var value = fallback(property);
|
||||||
|
|
||||||
|
if (value === void 0) {
|
||||||
|
throw new Error('no return value from provided fallback function');
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Public: Adds necessary utils to global namespace, along with base class
|
// Public: Adds necessary utils to global namespace, along with base class
|
||||||
|
|
|
@ -11,10 +11,6 @@ describe("Adaptor", function() {
|
||||||
var adaptor = new Adaptor({ name: 'adaptor', connection: connection });
|
var adaptor = new Adaptor({ name: 'adaptor', connection: connection });
|
||||||
|
|
||||||
describe("#constructor", function() {
|
describe("#constructor", function() {
|
||||||
it("sets @self as a reference to the adaptor", function() {
|
|
||||||
expect(adaptor.self).to.be.eql(adaptor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets @name to the provided name", function() {
|
it("sets @name to the provided name", function() {
|
||||||
expect(adaptor.name).to.be.eql('adaptor');
|
expect(adaptor.name).to.be.eql('adaptor');
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,77 +2,182 @@
|
||||||
|
|
||||||
var express = require('express'),
|
var express = require('express'),
|
||||||
https = require('https'),
|
https = require('https'),
|
||||||
fs = require('fs');
|
fs = require('fs'),
|
||||||
|
path = require('path');
|
||||||
|
|
||||||
var API = source('api'),
|
var API = source('api'),
|
||||||
Utils = source('utils');
|
Utils = source('utils'),
|
||||||
|
Logger = source('logger');
|
||||||
|
|
||||||
|
var MockRequest = require('../support/mock_request'),
|
||||||
|
MockResponse = require('../support/mock_response');
|
||||||
|
|
||||||
describe("API", function() {
|
describe("API", function() {
|
||||||
var api, opts;
|
var api;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
api = new API();
|
||||||
|
});
|
||||||
|
|
||||||
describe("constructor", function() {
|
describe("constructor", function() {
|
||||||
|
var mod;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
stub(https, 'createServer').returns({ listen: spy() });
|
mod = new API({
|
||||||
|
host: "0.0.0.0",
|
||||||
api = new API(opts);
|
port: "1234"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
it("sets @express to an Express instance", function() {
|
||||||
https.createServer.restore();
|
expect(api.express.listen).to.be.a('function');
|
||||||
|
})
|
||||||
|
|
||||||
|
it("sets default values", function() {
|
||||||
|
var sslPath = path.normalize(__dirname + "/../../lib/api/ssl/");
|
||||||
|
|
||||||
|
expect(api.host).to.be.eql('127.0.0.1');
|
||||||
|
expect(api.port).to.be.eql('3000');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets @opts to the passed opts object", function() {
|
it("overrides default values if passed to constructor", function() {
|
||||||
expect(api.opts).to.be.eql(opts);
|
expect(mod.host).to.be.eql('0.0.0.0');
|
||||||
|
expect(mod.port).to.be.eql('1234');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets @host to 127.0.0.1 by default", function() {
|
it("sets the server title", function() {
|
||||||
expect(api.host).to.be.eql("127.0.0.1")
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets @post to 3000 by default", function() {
|
|
||||||
expect(api.port).to.be.eql("3000")
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets @express to an Express server instance", function() {
|
|
||||||
expect(api.express).to.be.a('function');
|
|
||||||
|
|
||||||
var methods = ['get', 'post', 'put', 'delete'];
|
|
||||||
|
|
||||||
for (var i = 0; i < methods.length; i++) {
|
|
||||||
expect(api.express[methods[i]]).to.be.a('function');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets @server.https to a https.createServer", function() {
|
|
||||||
expect(https.createServer).to.be.calledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets the server's title", function() {
|
|
||||||
var title = api.express.get('title');
|
var title = api.express.get('title');
|
||||||
expect(title).to.be.eql("Cylon API Server");
|
expect(title).to.be.eql('Cylon API Server');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("default", function() {
|
||||||
|
var d = API.prototype.defaults;
|
||||||
|
|
||||||
describe("ssl disabled", function () {
|
it("host", function() {
|
||||||
|
expect(d.host).to.be.eql('127.0.0.1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("port", function() {
|
||||||
|
expect(d.port).to.be.eql('3000');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("auth", function() {
|
||||||
|
expect(d.auth).to.be.eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("CORS", function() {
|
||||||
|
expect(d.CORS).to.be.eql('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ssl", function() {
|
||||||
|
var sslDir = path.normalize(__dirname + "/../../lib/api/ssl/");
|
||||||
|
expect(d.ssl.key).to.be.eql(sslDir + "server.key");
|
||||||
|
expect(d.ssl.cert).to.be.eql(sslDir + "server.crt");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#createServer", function() {
|
||||||
|
it("sets @express to an express server", function() {
|
||||||
|
api.express = null;
|
||||||
|
api.createServer();
|
||||||
|
expect(api.express).to.be.a('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
context("if SSL is configured", function() {
|
||||||
|
it("sets @server to a https.Server instance", function() {
|
||||||
|
api.createServer();
|
||||||
|
expect(api.server).to.be.an.instanceOf(https.Server);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context("if SSL is not configured", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
api.ssl = false;
|
||||||
|
stub(Logger, 'warn');
|
||||||
|
api.createServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
Logger.warn.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs that the API is insecure", function() {
|
||||||
|
expect(Logger.warn).to.be.calledWithMatch("insecure connection")
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets @server to @express", function() {
|
||||||
|
expect(api.server).to.be.eql(api.express);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#setupAuth", function() {
|
||||||
|
context("when auth.type is basic", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
api.auth = { type: 'basic', user: 'user', pass: 'pass' }
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a basic auth middleware function', function() {
|
||||||
|
var fn = api.setupAuth(),
|
||||||
|
req = new MockRequest(),
|
||||||
|
res = new MockResponse(),
|
||||||
|
next = spy();
|
||||||
|
|
||||||
|
var auth = 'Basic ' + new Buffer('user:pass').toString('base64')
|
||||||
|
|
||||||
|
req.headers.authorization = auth;
|
||||||
|
|
||||||
|
fn(req, res, next);
|
||||||
|
expect(next).to.be.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context("when auth is null", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
api.auth = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a pass-through middleware function", function() {
|
||||||
|
var fn = api.setupAuth(),
|
||||||
|
next = spy();
|
||||||
|
|
||||||
|
fn(null, null, next);
|
||||||
|
expect(next).to.be.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#listen", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
stub(https, 'createServer').returns({ listen: spy() });
|
// we create a plain HTTP server to avoid a log message from Node
|
||||||
|
api.ssl = false;
|
||||||
|
api.createServer();
|
||||||
|
api.express.set('title', 'Cylon API Server');
|
||||||
|
|
||||||
opts = { ssl: false }
|
stub(api.server, 'listen').yields();
|
||||||
|
stub(Logger, 'info');
|
||||||
|
|
||||||
api = new API(opts);
|
api.listen();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
https.createServer.restore();
|
api.server.listen.restore();
|
||||||
|
Logger.info.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't create https server", function() {
|
it("listens on the configured host and port", function() {
|
||||||
expect(https.createServer).not.to.be.calledWith();
|
expect(api.server.listen).to.be.calledWith('3000', '127.0.0.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context("when the server is running", function() {
|
||||||
|
it("logs that it's online and listening", function() {
|
||||||
|
var online = "Cylon API Server is now online.",
|
||||||
|
listening = "Listening at http://127.0.0.1:3000";
|
||||||
|
|
||||||
|
expect(Logger.info).to.be.calledWith(online);
|
||||||
|
expect(Logger.info).to.be.calledWith(listening);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,10 +13,6 @@ describe("Connection", function() {
|
||||||
var connection = robot.connections.loopback;
|
var connection = robot.connections.loopback;
|
||||||
|
|
||||||
describe("#constructor", function() {
|
describe("#constructor", function() {
|
||||||
it("sets @self as a circular reference", function() {
|
|
||||||
expect(connection.self).to.be.eql(connection);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets @robot to the passed robot", function() {
|
it("sets @robot to the passed robot", function() {
|
||||||
expect(connection.robot).to.be.eql(robot);
|
expect(connection.robot).to.be.eql(robot);
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,10 +18,6 @@ describe("Driver", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor", function() {
|
describe("#constructor", function() {
|
||||||
it("sets @self as a reference to the driver", function() {
|
|
||||||
expect(driver.self).to.be.eql(driver);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets @name to the provided name", function() {
|
it("sets @name to the provided name", function() {
|
||||||
expect(driver.name).to.be.eql('driver');
|
expect(driver.name).to.be.eql('driver');
|
||||||
});
|
});
|
||||||
|
|
|
@ -164,9 +164,8 @@ describe("Utils", function() {
|
||||||
|
|
||||||
var TestClass = (function() {
|
var TestClass = (function() {
|
||||||
function TestClass() {
|
function TestClass() {
|
||||||
this.self = this;
|
|
||||||
this.testInstance = new ProxyClass;
|
this.testInstance = new ProxyClass;
|
||||||
utils.proxyFunctionsToObject(methods, this.testInstance, this.self, true);
|
utils.proxyFunctionsToObject(methods, this.testInstance, this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TestClass;
|
return TestClass;
|
||||||
|
@ -204,22 +203,51 @@ describe("Utils", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#bind", function() {
|
describe("#fetch", function() {
|
||||||
var me = { hello: "Hello World" },
|
var fetch = utils.fetch,
|
||||||
proxy = { boundMethod: function() { return this.hello; } };
|
obj = { property: 'hello world', 'false': false, 'null': null };
|
||||||
|
|
||||||
it("binds the 'this' scope for the method", function() {
|
context("if the property exists on the object", function() {
|
||||||
proxy.boundMethod = function() { return this.hello; };
|
it("returns the value", function() {
|
||||||
proxy.boundMethod = utils.bind(proxy.boundMethod, me);
|
expect(fetch(obj, 'property')).to.be.eql('hello world');
|
||||||
|
expect(fetch(obj, 'false')).to.be.eql(false);
|
||||||
expect(proxy.boundMethod()).to.eql("Hello World");
|
expect(fetch(obj, 'null')).to.be.eql(null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("passes arguments along to bound functions", function() {
|
context("if the property doesn't exist on the object", function() {
|
||||||
proxy.boundMethod = function(hello, world) { return [hello, world]; };
|
context("and no fallback value has been provided", function() {
|
||||||
proxy.boundMethod = utils.bind(proxy.boundMethod, me);
|
it("throws an Error", function() {
|
||||||
|
var fn = function() { return fetch(obj, "notaproperty"); };
|
||||||
|
expect(fn).to.throw(Error, 'key not found: "notaproperty"');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(proxy.boundMethod("Hello", "World")).to.eql(["Hello", "World"]);
|
context("and a fallback value has been provided", function() {
|
||||||
})
|
it('returns the fallback value', function() {
|
||||||
|
expect(fetch(obj, 'notakey', 'fallback')).to.be.eql('fallback');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context("and a fallback function has been provided", function() {
|
||||||
|
context("if the function has no return value", function() {
|
||||||
|
it("throws an Error", function() {
|
||||||
|
var fn = function() { fetch(obj, 'notakey', function() {}); },
|
||||||
|
str = 'no return value from provided fallback function';
|
||||||
|
|
||||||
|
expect(fn).to.throw(Error, str);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context("if the function returns a value", function() {
|
||||||
|
it("returns the value returned by the fallback function", function() {
|
||||||
|
var fn = function(key) { return "Couldn't find " + key },
|
||||||
|
value = "Couldn't find notakey";
|
||||||
|
|
||||||
|
expect(fetch(obj, 'notakey', fn)).to.be.eql(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue