From 7b275e9e0efa5cee13def8002556d39f0fd48035 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 17 Nov 2014 12:23:30 +0000 Subject: [PATCH] Add acceptence tests for leaving(disconnecting) from a project --- services/real-time/app/coffee/Router.coffee | 8 +- .../real-time/config/settings.defaults.coffee | 2 + .../coffee/LeaveProjectTests.coffee | 112 ++++++++++++++++-- .../helpers/MockDocUpdaterServer.coffee | 9 ++ .../helpers/MockTrackChangesServer.coffee | 21 ++++ 5 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 services/real-time/test/acceptance/coffee/helpers/MockTrackChangesServer.coffee diff --git a/services/real-time/app/coffee/Router.coffee b/services/real-time/app/coffee/Router.coffee index 7872727377..85a7940b7a 100644 --- a/services/real-time/app/coffee/Router.coffee +++ b/services/real-time/app/coffee/Router.coffee @@ -5,7 +5,7 @@ HttpController = require "./HttpController" Utils = require "./Utils" module.exports = Router = - _handleError: (callback, error, client, method, extraAttrs = {}) -> + _handleError: (callback = ((error) ->), error, client, method, extraAttrs = {}) -> Utils.getClientAttributes client, ["project_id", "doc_id", "user_id"], (_, attrs) -> for key, value of extraAttrs attrs[key] = value @@ -13,7 +13,6 @@ module.exports = Router = attrs.err = error logger.error attrs, "server side error in #{method}" # Don't return raw error to prevent leaking server side info - console.log "CALLING CALLBACK", callback return callback {message: "Something went wrong"} configure: (app, io, session) -> @@ -44,6 +43,11 @@ module.exports = Router = else callback(null, args...) + client.on "disconnect", () -> + WebsocketController.leaveProject io, client, (err) -> + if err? + Router._handleError null, err, client, "leaveProject" + client.on "joinDoc", (doc_id, fromVersion, callback) -> # fromVersion is optional diff --git a/services/real-time/config/settings.defaults.coffee b/services/real-time/config/settings.defaults.coffee index 7d9ddf184c..ccaa02e523 100644 --- a/services/real-time/config/settings.defaults.coffee +++ b/services/real-time/config/settings.defaults.coffee @@ -15,6 +15,8 @@ module.exports = url: "http://localhost:3000" documentupdater: url: "http://localhost:3003" + trackchanges: + url: "http://localhost:3015" security: sessionSecret: "secret-please-change" diff --git a/services/real-time/test/acceptance/coffee/LeaveProjectTests.coffee b/services/real-time/test/acceptance/coffee/LeaveProjectTests.coffee index 9698abd2c2..2d1361a18b 100644 --- a/services/real-time/test/acceptance/coffee/LeaveProjectTests.coffee +++ b/services/real-time/test/acceptance/coffee/LeaveProjectTests.coffee @@ -1,16 +1,114 @@ +RealTimeClient = require "./helpers/RealTimeClient" +MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer" +MockTrackChangesServer = require "./helpers/MockTrackChangesServer" +FixturesManager = require "./helpers/FixturesManager" + +async = require "async" + describe "leaveProject", -> + before (done) -> + MockDocUpdaterServer.run (error) -> + return done(error) if error? + MockTrackChangesServer.run done + describe "with other clients in the project", -> - it "should emit a disconnect message to the room" + before (done) -> + async.series [ + (cb) => + FixturesManager.setUpProject { + privilegeLevel: "owner" + project: { + name: "Test Project" + } + }, (e, {@project_id, @user_id}) => cb() + + (cb) => + @clientA = RealTimeClient.connect() + @clientA.on "connect", cb + + (cb) => + @clientB = RealTimeClient.connect() + @clientB.on "connect", cb + + @clientBDisconnectMessages = [] + @clientB.on "clientTracking.clientDisconnected", (data) => + @clientBDisconnectMessages.push data + + (cb) => + @clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) => + cb(error) + + (cb) => + @clientB.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) => + cb(error) + + (cb) => + # leaveProject is called when the client disconnects + @clientA.on "disconnect", () -> cb() + @clientA.disconnect() + + (cb) => + # The API waits a little while before flushing changes + setTimeout done, require("../../../app/js/WebsocketController").FLUSH_IF_EMPTY_DELAY * 2 + + ], done + + it "should emit a disconnect message to the room", -> + @clientBDisconnectMessages.should.deep.equal [@clientA.socket.sessionid] - it "should no longer list the client in connected users" + it "should no longer list the client in connected users", (done) -> + @clientB.emit "clientTracking.getConnectedUsers", (error, users) => + for user in users + if user.client_id == @clientA.socket.sessionid + throw "Expected clientA to not be listed in connected users" + return done() - it "should not flush the project to the document updater" + it "should not flush the project to the document updater", -> + MockDocUpdaterServer.deleteProject + .calledWith(@project_id) + .should.equal false - it "should not flush the project in track changes" + it "should not flush the project in track changes", -> + MockTrackChangesServer.flushProject + .calledWith(@project_id) + .should.equal false describe "with no other clients in the project", -> - it "should flush the project to the document updater" - - it "should flush the project in track changes" + before (done) -> + async.series [ + (cb) => + FixturesManager.setUpProject { + privilegeLevel: "owner" + project: { + name: "Test Project" + } + }, (e, {@project_id, @user_id}) => cb() + + (cb) => + @clientA = RealTimeClient.connect() + @clientA.on "connect", cb + + (cb) => + @clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) => + cb(error) + + (cb) => + # leaveProject is called when the client disconnects + @clientA.on "disconnect", () -> cb() + @clientA.disconnect() + + (cb) => + # The API waits a little while before flushing changes + setTimeout done, require("../../../app/js/WebsocketController").FLUSH_IF_EMPTY_DELAY * 2 + ], done + + it "should flush the project to the document updater", -> + MockDocUpdaterServer.deleteProject + .calledWith(@project_id) + .should.equal true + it "should flush the project in track changes", -> + MockTrackChangesServer.flushProject + .calledWith(@project_id) + .should.equal true \ No newline at end of file diff --git a/services/real-time/test/acceptance/coffee/helpers/MockDocUpdaterServer.coffee b/services/real-time/test/acceptance/coffee/helpers/MockDocUpdaterServer.coffee index 094d10aceb..0f196371b3 100644 --- a/services/real-time/test/acceptance/coffee/helpers/MockDocUpdaterServer.coffee +++ b/services/real-time/test/acceptance/coffee/helpers/MockDocUpdaterServer.coffee @@ -12,6 +12,8 @@ module.exports = MockDocUpdaterServer = null, MockDocUpdaterServer.docs["#{project_id}:#{doc_id}"] ) + deleteProject: sinon.stub().callsArg(1) + getDocumentRequest: (req, res, next) -> {project_id, doc_id} = req.params {fromVersion} = req.query @@ -19,6 +21,12 @@ module.exports = MockDocUpdaterServer = MockDocUpdaterServer.getDocument project_id, doc_id, fromVersion, (error, data) -> return next(error) if error? res.json data + + deleteProjectRequest: (req, res, next) -> + {project_id} = req.params + MockDocUpdaterServer.deleteProject project_id, (error) -> + return next(error) if error? + res.sendStatus 204 running: false run: (callback = (error) ->) -> @@ -26,6 +34,7 @@ module.exports = MockDocUpdaterServer = return callback() app = express() app.get "/project/:project_id/doc/:doc_id", MockDocUpdaterServer.getDocumentRequest + app.delete "/project/:project_id", MockDocUpdaterServer.deleteProjectRequest app.listen 3003, (error) -> MockDocUpdaterServer.running = true callback(error) diff --git a/services/real-time/test/acceptance/coffee/helpers/MockTrackChangesServer.coffee b/services/real-time/test/acceptance/coffee/helpers/MockTrackChangesServer.coffee new file mode 100644 index 0000000000..e7ddbf5cea --- /dev/null +++ b/services/real-time/test/acceptance/coffee/helpers/MockTrackChangesServer.coffee @@ -0,0 +1,21 @@ +sinon = require "sinon" +express = require "express" + +module.exports = MockTrackChangesServer = + flushProject: sinon.stub().callsArg(1) + + flushProjectRequest: (req, res, next) -> + {project_id} = req.params + MockTrackChangesServer.flushProject project_id, (error) -> + return next(error) if error? + res.sendStatus 204 + + running: false + run: (callback = (error) ->) -> + if MockTrackChangesServer.running + return callback() + app = express() + app.post "/project/:project_id/flush", MockTrackChangesServer.flushProjectRequest + app.listen 3015, (error) -> + MockTrackChangesServer.running = true + callback(error) \ No newline at end of file