'use strict'; var should = require('chai').should(); var sinon = require('sinon'); var EventEmitter = require('events').EventEmitter; var proxyquire = require('proxyquire'); var httpStub = { createServer: sinon.spy() }; var httpsStub = { createServer: sinon.spy() }; var fsStub = { readFileSync: function(arg1) { return arg1 + '-buffer'; } }; var fakeSocketListener = new EventEmitter(); var fakeSocket = new EventEmitter(); fakeSocket.on('test/event1', function(data) { data.should.equal('testdata'); done(); }); fakeSocketListener.emit('connection', fakeSocket); fakeSocket.emit('subscribe', 'test/event1'); var WebService = proxyquire('../../lib/services/web', {http: httpStub, https: httpsStub, fs: fsStub}); describe('WebService', function() { var defaultNode = new EventEmitter(); describe('#start', function() { beforeEach(function() { httpStub.createServer.reset(); httpsStub.createServer.reset(); }); it('should create an http server if no options are specified and node is not configured for https', function(done) { var web = new WebService({node: defaultNode}); web.deriveHttpsOptions = sinon.spy(); web.start(function(err) { should.not.exist(err); httpStub.createServer.called.should.equal(true); done(); }); }); it('should create an https server if no options are specified and node is configured for https', function(done) { var node = new EventEmitter(); node.https = true; var web = new WebService({node: node}); web.transformHttpsOptions = sinon.spy(); web.start(function(err) { should.not.exist(err); httpsStub.createServer.called.should.equal(true); done(); }); }); }); describe('#stop', function() { it('should close the server if it exists', function(done) { var web = new WebService({node: defaultNode}); web.server = { close: sinon.spy() }; web.stop(function(err) { should.not.exist(err); web.server.close.callCount.should.equal(1); done(); }); }); }); describe('#setupAllRoutes', function() { it('should call setupRoutes on each module', function() { var node = { on: sinon.spy(), services: { one: { setupRoutes: sinon.spy(), getRoutePrefix: sinon.stub().returns('one') }, two: { setupRoutes: sinon.spy(), getRoutePrefix: sinon.stub().returns('two') } } }; var web = new WebService({node: node}); web.app = { use: sinon.spy() }; web.setupAllRoutes(); node.services.one.setupRoutes.callCount.should.equal(1); should.exist(node.services.one.setupRoutes.args[0][0].engine); should.exist(node.services.one.setupRoutes.args[0][0].get); should.exist(node.services.one.setupRoutes.args[0][0].post); should.exist(node.services.one.setupRoutes.args[0][0].set); node.services.two.setupRoutes.callCount.should.equal(1); }); }); describe('#createMethodsMap', function() { it('should create the methodsMap correctly', function(done) { var Module1 = function() {}; Module1.prototype.getAPIMethods = function() { return [ ['one', this, this.one, 1], ['two', this, this.two, 2] ]; }; Module1.prototype.one = function(param1, callback) { callback(null, param1); }; Module1.prototype.two = function(param1, param2, callback) { callback(null, param1 + param2); }; var module1 = new Module1(); var node = { on: sinon.spy(), getAllAPIMethods: sinon.stub().returns(module1.getAPIMethods()) }; var web = new WebService({node: node}); web.createMethodsMap(); Object.keys(web.methodsMap).length.should.equal(2); web.methodsMap.one.args.should.equal(1); web.methodsMap.two.args.should.equal(2); web.methodsMap.one.fn(1, function(err, result) { should.not.exist(err); result.should.equal(1); web.methodsMap.two.fn(1, 2, function(err, result) { should.not.exist(err); result.should.equal(3); done(); }); }); }); }); describe('#getEventNames', function() { it('should get event names', function() { var Module1 = function() {}; Module1.prototype.getPublishEvents = function() { return [ { name: 'event1', extraEvents: ['event2'] } ]; }; var module1 = new Module1(); var node = { on: sinon.spy(), getAllPublishEvents: sinon.stub().returns(module1.getPublishEvents()) }; var web = new WebService({node: node}); var events = web.getEventNames(); events.should.deep.equal(['event1', 'event2']); }); it('should throw an error if there is a duplicate event', function() { var Module1 = function() {}; Module1.prototype.getPublishEvents = function() { return [ { name: 'event1', extraEvents: ['event1'] } ]; }; var module1 = new Module1(); var node = { on: sinon.spy(), getAllPublishEvents: sinon.stub().returns(module1.getPublishEvents()) }; var web = new WebService({node: node}); (function() { var events = web.getEventNames(); }).should.throw('Duplicate event event1'); }); }); describe('#socketHandler', function() { var bus = new EventEmitter(); var Module1 = function() {}; Module1.prototype.getPublishEvents = function() { return [ { name: 'event1', extraEvents: ['event2'] } ]; }; var module1 = new Module1(); var node = { on: sinon.spy(), openBus: sinon.stub().returns(bus), getAllPublishEvents: sinon.stub().returns(module1.getPublishEvents()) }; var web; var socket; it('on message should call socketMessageHandler', function(done) { web = new WebService({node: node}); web.eventNames = web.getEventNames(); web.socketMessageHandler = function(param1) { param1.should.equal('data'); done(); }; socket = new EventEmitter(); web.socketHandler(socket); socket.emit('message', 'data'); }); it('on subscribe should call bus.subscribe', function(done) { bus.subscribe = function(param1) { param1.should.equal('data'); done(); }; socket.emit('subscribe', 'data'); }); it('on unsubscribe should call bus.unsubscribe', function(done) { bus.unsubscribe = function(param1) { param1.should.equal('data'); done(); }; socket.emit('unsubscribe', 'data'); }); it('publish events from bus should be emitted from socket', function(done) { socket.once('event2', function(param1, param2) { param1.should.equal('param1'); param2.should.equal('param2'); done(); }); socket.connected = true; bus.emit('event2', 'param1', 'param2'); }); it('on disconnect should close bus', function(done) { bus.close = function() { done(); }; socket.emit('disconnect'); }); }); describe('#socketMessageHandler', function() { var node = { on: sinon.spy() }; var web = new WebService({node: node}); web.methodsMap = { one: { fn: function(param1, param2, callback) { var result = param1 + param2; if(result > 0) { return callback(null, result); } else { return callback(new Error('error')); } }, args: 2 } }; it('should give a Method Not Found error if method does not exist', function(done) { var message = { method: 'two', params: [1, 2] } web.socketMessageHandler(message, function(response) { should.exist(response.error); response.error.message.should.equal('Method Not Found'); done(); }); }); it('should call the method and return the result', function(done) { var message = { method: 'one', params: [1, 2] }; web.socketMessageHandler(message, function(response) { should.not.exist(response.error); response.result.should.equal(3); done(); }); }); it('should give an error if there is a param count mismatch', function(done) { var message = { method: 'one', params: [1] }; web.socketMessageHandler(message, function(response) { should.exist(response.error); response.error.message.should.equal('Expected 2 parameter(s)'); done(); }); }); it('should give an error if the method gave an error', function(done) { var message = { method: 'one', params: [-1, -2] }; web.socketMessageHandler(message, function(response) { should.exist(response.error); response.error.message.should.equal('Error: error'); done(); }); }); }); describe('#deriveHttpsOptions', function() { it('should read key and cert from files specified', function() { var web = new WebService({ node: defaultNode, https: true, httpsOptions: { key: 'key', cert: 'cert' } }); web.transformHttpsOptions(); web.httpsOptions.key.should.equal('key-buffer'); web.httpsOptions.cert.should.equal('cert-buffer'); }); it('should throw an error if https is specified but key or cert is not specified', function() { var web = new WebService({ node: defaultNode, https: true, httpsOptions: { key: 'key' } }); (function() { web.transformHttpsOptions(); }).should.throw('Missing https options'); }); }); });