UNPKG

actionhero

Version:

actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks

492 lines (415 loc) 15.6 kB
var should = require('should'); var request = require('request'); var EventEmitter = require('events').EventEmitter var actionheroPrototype = require(__dirname + '/../../actionhero.js').actionheroPrototype; var actionhero = new actionheroPrototype(); var api; var clientA; var clientB; var clientC; var url var connectClients = function(callback){ // get actionheroClient in scope // TODO: Perhaps we read this from disk after server boot. eval( api.servers.servers.websocket.compileActionheroClientJS() ); var S = api.servers.servers.websocket.server.Socket; var url = 'http://localhost:' + api.config.servers.web.port; var clientAsocket = new S(url); var clientBsocket = new S(url); var clientCsocket = new S(url); clientA = new ActionheroClient({}, clientAsocket); clientB = new ActionheroClient({}, clientBsocket); clientC = new ActionheroClient({}, clientCsocket); setTimeout(function(){ callback(); }, 100); } describe('Server: Web Socket', function(){ before(function(done){ actionhero.start(function(err, a){ api = a; url = 'http://localhost:' + api.config.servers.web.port; api.config.servers.websocket.clientUrl = 'http://localhost:' + api.config.servers.web.port; connectClients(function(){ done(); }); }); }); after(function(done){ actionhero.stop(function(){ done(); }); }); it('socket client connections should work: client 1', function(done){ clientA.connect(function(err, data){ data.context.should.equal('response'); data.data.totalActions.should.equal(0); clientA.welcomeMessage.should.equal('Hello! Welcome to the actionhero api'); done(); }); }); it('socket client connections should work: client 2', function(done){ clientB.connect(function(err, data){ data.context.should.equal('response'); data.data.totalActions.should.equal(0); clientA.welcomeMessage.should.equal('Hello! Welcome to the actionhero api'); done(); }); }); it('socket client connections should work: client 3', function(done){ clientC.connect(function(err, data){ data.context.should.equal('response'); data.data.totalActions.should.equal(0); clientA.welcomeMessage.should.equal('Hello! Welcome to the actionhero api'); done(); }); }); it('I can get my connection details', function(done){ clientA.detailsView(function(response){ response.data.connectedAt.should.be.within(0, new Date().getTime()) response.data.remoteIP.should.equal('127.0.0.1'); done() }); }); it('can run actions with errors', function(done){ clientA.action('cacheTest', function(response){ response.error.should.equal('key is a required parameter for this action'); done(); }); }); it('can run actions properly', function(done){ clientA.action('cacheTest', {key: 'test key', value: 'test value'}, function(response){ should.not.exist(response.error); done(); }); }); it('does not have sticky params', function(done){ clientA.action('cacheTest', {key: 'test key', value: 'test value'}, function(response){ should.not.exist(response.error); response.cacheTestResults.loadResp.key.should.equal('cacheTest_test key'); response.cacheTestResults.loadResp.value.should.equal('test value'); clientA.action('cacheTest', function(response){ response.error.should.equal('key is a required parameter for this action'); done(); }); }); }); it('will limit how many simultaneous connections I can have', function(done){ var responses = []; clientA.action('sleepTest', {sleepDuration: 100}, function(response){ responses.push(response) }) clientA.action('sleepTest', {sleepDuration: 200}, function(response){ responses.push(response) }) clientA.action('sleepTest', {sleepDuration: 300}, function(response){ responses.push(response) }) clientA.action('sleepTest', {sleepDuration: 400}, function(response){ responses.push(response) }) clientA.action('sleepTest', {sleepDuration: 500}, function(response){ responses.push(response) }) clientA.action('sleepTest', {sleepDuration: 600}, function(response){ responses.push(response) }) setTimeout(function(){ responses.length.should.equal(6); for(var i in responses){ var response = responses[i]; if(i === 0 || i === '0'){ response.error.should.eql('you have too many pending requests'); } else { should.not.exist(response.error) } } done(); }, 1000); }); describe('files', function(){ it('can request file data', function(done){ clientA.file('simple.html', function(data){ should.not.exist(data.error); data.content.should.equal('<h1>ActionHero</h1>\\nI am a flat file being served to you via the API from ./public/simple.html<br />'); data.mime.should.equal('text/html'); data.length.should.equal(101); done(); }); }); it('missing files', function(done){ clientA.file('missing.html', function(data){ data.error.should.equal( api.config.errors.fileNotFound() ); data.mime.should.equal('text/html'); should.not.exist(data.content); done(); }); }); }); describe('chat', function(){ before(function(done){ api.chatRoom.addMiddleware({ name: 'join chat middleware', join: function(connection, room, callback){ api.chatRoom.broadcast({}, room, 'I have entered the room: ' + connection.id, function(e){ callback(); }); } }); api.chatRoom.addMiddleware({ name: 'leave chat middleware', leave: function(connection, room, callback){ api.chatRoom.broadcast({}, room, 'I have left the room: ' + connection.id, function(e){ callback(); }); } }); done(); }) after(function(done){ api.chatRoom.middleware = {}; api.chatRoom.globalMiddleware = []; done(); }) beforeEach(function(done){ clientA.roomAdd('defaultRoom',function(){ clientB.roomAdd('defaultRoom',function(){ clientC.roomAdd('defaultRoom',function(){ setTimeout(function(){ // timeout to skip welcome messages as clients join rooms done(); }, 100); }); }); }); }); afterEach(function(done){ clientA.roomLeave('defaultRoom',function(){ clientB.roomLeave('defaultRoom',function(){ clientC.roomLeave('defaultRoom',function(){ clientA.roomLeave('otherRoom',function(){ clientB.roomLeave('otherRoom',function(){ clientC.roomLeave('otherRoom',function(){ done(); }); }); }); }); }); }); }); it('can change rooms and get room details', function(done){ clientA.roomAdd('otherRoom', function(){ clientA.detailsView(function(response){ should.not.exist(response.error); response.data.rooms[0].should.equal('defaultRoom'); response.data.rooms[1].should.equal('otherRoom'); clientA.roomView('otherRoom', function(response){ response.data.membersCount.should.equal(1); done(); }); }); }); }); it('will update client room info when they change rooms', function(done){ clientA.rooms[0].should.equal('defaultRoom'); should.not.exist( clientA.rooms[1] ); clientA.roomAdd('otherRoom', function(response){ should.not.exist(response.error); clientA.rooms[0].should.equal('defaultRoom'); clientA.rooms[1].should.equal('otherRoom'); clientA.roomLeave('defaultRoom', function(response){ should.not.exist(response.error); clientA.rooms[0].should.equal('otherRoom'); should.not.exist( clientA.rooms[1] ); done(); }); }); }) it('Clients can talk to each other', function(done){ var listener = function(response){ clientA.removeListener('say', listener); response.context.should.equal('user'); response.message.should.equal('hello from client 2'); done(); }; clientA.on('say', listener); clientB.say('defaultRoom', 'hello from client 2'); }); it('The client say method does not rely on order', function(done){ var listener = function(response){ clientA.removeListener('say', listener); response.context.should.equal('user'); response.message.should.equal('hello from client 2'); done(); }; clientB.say = function(room, message, callback){ this.send({message: message, room: room, event: 'say'}, callback); } clientA.on('say', listener); clientB.say('defaultRoom', 'hello from client 2'); }); it('connections are notified when I join a room', function(done){ var listener = function(response){ clientA.removeListener('say', listener); response.context.should.equal('user'); response.message.should.equal('I have entered the room: ' + clientB.id); done(); }; clientA.roomAdd('otherRoom', function(){ clientA.on('say', listener); clientB.roomAdd('otherRoom'); }); }); it('connections are notified when I leave a room', function(done){ var listener = function(response){ clientA.removeListener('say', listener); response.context.should.equal('user'); response.message.should.equal('I have left the room: ' + clientB.id); done(); } clientA.on('say', listener); clientB.roomLeave('defaultRoom'); }); it('will not get messages for rooms I am not in', function(done){ clientB.roomAdd('otherRoom', function(response){ should.not.exist(response.error); clientB.rooms.length.should.equal(2); var listener = function(response){ clientC.removeListener('say', listener); should.not.exist(response); }; clientC.rooms.length.should.equal(1); clientC.on('say', listener); setTimeout(function(){ clientC.removeListener('say', listener); done(); }, 1000) clientB.say('otherRoom', 'you should not hear this'); }); }); it('connections can see member counts changing within rooms as folks join and leave', function(done){ clientA.roomView('defaultRoom', function(response){ response.data.membersCount.should.equal(3); clientB.roomLeave('defaultRoom', function(){ clientA.roomView('defaultRoom', function(response){ response.data.membersCount.should.equal(2); done(); }); }); }); }); describe('custom room member data', function(){ var currentSanitize; var currentGenerate; before(function(done){ //Ensure that default behavior works clientA.roomAdd('defaultRoom',function(){ clientA.roomView('defaultRoom', function(response){ response.data.room.should.equal('defaultRoom'); for( var key in response.data.members ){ ( response.data.members[key].type === undefined ).should.eql(true); } //save off current functions currentSanitize = api.chatRoom.sanitizeMemberDetails; currentGenerate = api.chatRoom.generateMemberDetails; //override functions api.chatRoom.sanitizeMemberDetails = function(data){ return { id: data.id, joinedAt: data.joinedAt, type: data.type }; } api.chatRoom.generateMemberDetails = function(connection){ return { id: connection.id, joinedAt: new Date().getTime(), type : connection.type }; } clientA.roomLeave('defaultRoom', function(){ done(); }); }); }); }); after(function(done){ api.chatRoom.joinCallbacks = {}; api.chatRoom.leaveCallbacks = {}; api.chatRoom.sanitizeMemberDetails = currentSanitize; api.chatRoom.generateMemberDetails = currentGenerate; //Check that everything is back to normal clientA.roomAdd('defaultRoom',function(){ clientA.roomView('defaultRoom', function(response){ response.data.room.should.equal('defaultRoom'); for( var key in response.data.members ){ ( response.data.members[key].type === undefined ).should.eql(true); } setTimeout(function(){ clientA.roomLeave('defaultRoom', function(){ done(); }); }, 100); }); }); }); it('should view non-default member data', function(done){ clientA.roomAdd('defaultRoom',function(){ clientA.roomView('defaultRoom', function(response){ response.data.room.should.equal('defaultRoom'); for( var key in response.data.members ){ response.data.members[key].type.should.eql('websocket'); } clientA.roomLeave('defaultRoom'); done(); }); }) }); }); }); describe('param collisions', function(){ var originalSimultaneousActions before(function(){ originalSimultaneousActions = api.config.general.simultaneousActions; api.config.general.simultaneousActions = 99999999; }); after(function(){ api.config.general.simultaneousActions = originalSimultaneousActions; }); it('will not have param colisions', function(done){ var completed = 0; var started = 0; var sleeps = [ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110 ]; var toComplete = function(sleep, response){ sleep.should.equal(response.sleepDuration); completed++; if(completed === started){ done(); } } sleeps.forEach(function(sleep){ started++; clientA.action('sleepTest', {sleepDuration: sleep}, function(response){ toComplete(sleep, response); }) }); }); }); describe('disconnect', function(){ beforeEach(function(done){ try{ clientA.disconnect(); clientB.disconnect(); clientC.disconnect(); }catch(e){} connectClients(function(){ clientA.connect(); clientB.connect(); clientC.connect(); setTimeout(done, 500); }); }); it('client can disconnect', function(done){ api.servers.servers.websocket.connections().length.should.equal(3); clientA.disconnect(); clientB.disconnect(); clientC.disconnect(); setTimeout(function(){ api.servers.servers.websocket.connections().length.should.equal(0); done(); }, 500); }); it('can be sent disconnect events from the server', function(done){ clientA.detailsView(function(response){ response.data.remoteIP.should.equal('127.0.0.1'); var count = 0 for(var id in api.connections.connections){ count++; api.connections.connections[id].destroy(); } count.should.equal(3); clientA.detailsView(function(){ throw new Error("should not get responst") }); setTimeout(done, 500) }); }); }); });