From ca749fd03ed2dd3c4ea59d827fefb5f9d060088f Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Mon, 16 Jun 2014 11:42:33 -0700 Subject: [PATCH] Fix an edge case and improve tests for API --- lib/api.js | 2 +- test/specs/api.spec.js | 189 ++++++++++++++++++++++++++++++++--------- 2 files changed, 148 insertions(+), 43 deletions(-) diff --git a/lib/api.js b/lib/api.js index 84ce2be..9098f84 100644 --- a/lib/api.js +++ b/lib/api.js @@ -98,7 +98,7 @@ API.prototype.createServer = function createServer() { API.prototype.setupAuth = function setupAuth() { 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, module = "./api/auth/" + type, filename = path.normalize(__dirname + "/" + module + ".js"), diff --git a/test/specs/api.spec.js b/test/specs/api.spec.js index 4eb39da..47f9304 100644 --- a/test/specs/api.spec.js +++ b/test/specs/api.spec.js @@ -2,77 +2,182 @@ var express = require('express'), https = require('https'), - fs = require('fs'); + fs = require('fs'), + path = require('path'); 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() { - var api, opts; + var api; + + beforeEach(function() { + api = new API(); + }); describe("constructor", function() { + var mod; beforeEach(function() { - stub(https, 'createServer').returns({ listen: spy() }); - - api = new API(opts); + mod = new API({ + host: "0.0.0.0", + port: "1234" + }); }); - afterEach(function() { - https.createServer.restore(); + it("sets @express to an Express instance", function() { + 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() { - expect(api.opts).to.be.eql(opts); + it("overrides default values if passed to constructor", function() { + 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() { - 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() { + it("sets the server title", function() { 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() { - 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() { - https.createServer.restore(); + api.server.listen.restore(); + Logger.info.restore(); }); - it("doesn't create https server", function() { - expect(https.createServer).not.to.be.calledWith(); + it("listens on the configured host and port", function() { + 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); + }); + }); }); });