From 1afebd12a14cab488b920a8421e9fbf902bea796 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 22 Jul 2019 11:23:43 +0100 Subject: [PATCH] unit tests --- .../test/unit/coffee/ChannelManager.coffee | 93 +++++++++- .../test/unit/coffee/RoomManagerTests.coffee | 175 +++++++++++++++++- 2 files changed, 255 insertions(+), 13 deletions(-) diff --git a/services/real-time/test/unit/coffee/ChannelManager.coffee b/services/real-time/test/unit/coffee/ChannelManager.coffee index 314e9914e1..4ed852ddcf 100644 --- a/services/real-time/test/unit/coffee/ChannelManager.coffee +++ b/services/real-time/test/unit/coffee/ChannelManager.coffee @@ -6,28 +6,103 @@ SandboxedModule = require('sandboxed-module') describe 'ChannelManager', -> beforeEach -> - @project_id = "project-id-123" - @user_id = "user-id-123" - @user = {_id: @user_id} - @callback = sinon.stub() + @rclient = {} + @other_rclient = {} @ChannelManager = SandboxedModule.require modulePath, requires: "settings-sharelatex": @settings = {} + "metrics-sharelatex": @metrics = {inc: sinon.stub()} "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } describe "subscribe", -> - - describe "when the project room is empty", -> - describe "when there are other clients in the project room", -> + describe "when there is no existing subscription for this redis client", -> + beforeEach -> + @rclient.subscribe = sinon.stub() + @ChannelManager.subscribe @rclient, "applied-ops", "1234567890abcdef" + + it "should subscribe to the redis channel", -> + @rclient.subscribe.calledWithExactly("applied-ops:1234567890abcdef").should.equal true + + describe "when there is an existing subscription for this redis client", -> + beforeEach -> + @rclient.subscribe = sinon.stub() + @ChannelManager.subscribe @rclient, "applied-ops", "1234567890abcdef" + @rclient.subscribe = sinon.stub() # discard the original stub + @ChannelManager.subscribe @rclient, "applied-ops", "1234567890abcdef" + + it "should not subscribe to the redis channel", -> + @rclient.subscribe.called.should.equal false + + describe "when there is an existing subscription for another redis client but not this one", -> + beforeEach -> + @other_rclient.subscribe = sinon.stub() + @ChannelManager.subscribe @other_rclient, "applied-ops", "1234567890abcdef" + @rclient.subscribe = sinon.stub() # discard the original stub + @ChannelManager.subscribe @rclient, "applied-ops", "1234567890abcdef" + + it "should subscribe to the redis channel on this redis client", -> + @rclient.subscribe.calledWithExactly("applied-ops:1234567890abcdef").should.equal true describe "unsubscribe", -> - describe "when the doc room is empty", -> + describe "when there is no existing subscription for this redis client", -> + beforeEach -> + @rclient.unsubscribe = sinon.stub() + @ChannelManager.unsubscribe @rclient, "applied-ops", "1234567890abcdef" - describe "when there are other clients in the doc room", -> + it "should not unsubscribe from the redis channel", -> + @rclient.unsubscribe.called.should.equal false + + + describe "when there is an existing subscription for this another redis client but not this one", -> + beforeEach -> + @other_rclient.subscribe = sinon.stub() + @rclient.unsubscribe = sinon.stub() + @ChannelManager.subscribe @other_rclient, "applied-ops", "1234567890abcdef" + @ChannelManager.unsubscribe @rclient, "applied-ops", "1234567890abcdef" + + it "should not unsubscribe from the redis channel on this client", -> + @rclient.unsubscribe.called.should.equal false + + describe "when there is an existing subscription for this redis client", -> + beforeEach -> + @rclient.subscribe = sinon.stub() + @rclient.unsubscribe = sinon.stub() + @ChannelManager.subscribe @rclient, "applied-ops", "1234567890abcdef" + @ChannelManager.unsubscribe @rclient, "applied-ops", "1234567890abcdef" + + it "should unsubscribe from the redis channel", -> + @rclient.unsubscribe.calledWithExactly("applied-ops:1234567890abcdef").should.equal true describe "publish", -> describe "when the channel is 'all'", -> + beforeEach -> + @rclient.publish = sinon.stub() + @ChannelManager.publish @rclient, "applied-ops", "all", "random-message" + + it "should publish on the base channel", -> + @rclient.publish.calledWithExactly("applied-ops", "random-message").should.equal true describe "when the channel has an specific id", -> + + describe "when the individual channel setting is false", -> + beforeEach -> + @rclient.publish = sinon.stub() + @settings.publishOnIndividualChannels = false + @ChannelManager.publish @rclient, "applied-ops", "1234567890abcdef", "random-message" + + it "should publish on the per-id channel", -> + @rclient.publish.calledWithExactly("applied-ops", "random-message").should.equal true + @rclient.publish.calledOnce.should.equal true + + describe "when the individual channel setting is true", -> + beforeEach -> + @rclient.publish = sinon.stub() + @settings.publishOnIndividualChannels = true + @ChannelManager.publish @rclient, "applied-ops", "1234567890abcdef", "random-message" + + it "should publish on the per-id channel", -> + @rclient.publish.calledWithExactly("applied-ops:1234567890abcdef", "random-message").should.equal true + @rclient.publish.calledOnce.should.equal true + diff --git a/services/real-time/test/unit/coffee/RoomManagerTests.coffee b/services/real-time/test/unit/coffee/RoomManagerTests.coffee index 75d241cf6b..2f78b33c52 100644 --- a/services/real-time/test/unit/coffee/RoomManagerTests.coffee +++ b/services/real-time/test/unit/coffee/RoomManagerTests.coffee @@ -7,31 +7,198 @@ SandboxedModule = require('sandboxed-module') describe 'RoomManager', -> beforeEach -> @project_id = "project-id-123" - @user_id = "user-id-123" - @user = {_id: @user_id} - @callback = sinon.stub() + @doc_id = "doc-id-456" + @other_doc_id = "doc-id-789" + @client = {namespace: {name: ''}, id: "first-client"} @RoomManager = SandboxedModule.require modulePath, requires: "settings-sharelatex": @settings = {} "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + @RoomManager._clientsInRoom = sinon.stub() + @RoomEvents = @RoomManager.eventSource() + sinon.spy(@RoomEvents, 'emit') describe "joinProject", -> describe "when the project room is empty", -> + beforeEach -> + @RoomManager._clientsInRoom + .withArgs(@client, @project_id) + .onFirstCall().returns(0) + .onSecondCall().returns(1) + @client.join = sinon.stub() + @RoomManager.joinProject @client, @project_id + + it "should join the room using the id", -> + @client.join.calledWithExactly(@project_id).should.equal true + + it "should emit a 'project-active' event with the id", -> + @RoomEvents.emit.calledWithExactly('project-active', @project_id).should.equal true + describe "when there are other clients in the project room", -> + beforeEach -> + @RoomManager._clientsInRoom + .withArgs(@client, @project_id) + .onFirstCall().returns(123) + .onSecondCall().returns(124) + @client.join = sinon.stub() + @RoomManager.joinProject @client, @project_id + + it "should join the room using the id", -> + @client.join.called.should.equal true + + it "should not emit any events", -> + @RoomEvents.emit.called.should.equal false + + describe "joinDoc", -> describe "when the doc room is empty", -> + beforeEach -> + @RoomManager._clientsInRoom + .withArgs(@client, @doc_id) + .onFirstCall().returns(0) + .onSecondCall().returns(1) + @client.join = sinon.stub() + @RoomManager.joinDoc @client, @doc_id + + it "should join the room using the id", -> + @client.join.calledWithExactly(@doc_id).should.equal true + + it "should emit a 'doc-active' event with the id", -> + @RoomEvents.emit.calledWithExactly('doc-active', @doc_id).should.equal true + describe "when there are other clients in the doc room", -> + beforeEach -> + @RoomManager._clientsInRoom + .withArgs(@client, @doc_id) + .onFirstCall().returns(123) + .onSecondCall().returns(124) + @client.join = sinon.stub() + @RoomManager.joinDoc @client, @doc_id + + it "should join the room using the id", -> + @client.join.called.should.equal true + + it "should not emit any events", -> + @RoomEvents.emit.called.should.equal false + + describe "leaveDoc", -> describe "when doc room will be empty after this client has left", -> + beforeEach -> + @RoomManager._clientsInRoom + .withArgs(@client, @doc_id) + .onFirstCall().returns(1) + .onSecondCall().returns(0) + @client.leave = sinon.stub() + @RoomManager.leaveDoc @client, @doc_id + + it "should leave the room using the id", -> + @client.leave.calledWithExactly(@doc_id).should.equal true + + it "should emit a 'doc-empty' event with the id", -> + @RoomEvents.emit.calledWithExactly('doc-empty', @doc_id).should.equal true + + describe "when there are other clients in the doc room", -> + beforeEach -> + @RoomManager._clientsInRoom + .withArgs(@client, @doc_id) + .onFirstCall().returns(123) + .onSecondCall().returns(122) + @client.leave = sinon.stub() + @RoomManager.leaveDoc @client, @doc_id + + it "should leave the room using the id", -> + @client.leave.calledWithExactly(@doc_id).should.equal true + + it "should not emit any events", -> + @RoomEvents.emit.called.should.equal false + + describe "leaveProjectAndDocs", -> - describe "when the client is connected to multiple docs", -> \ No newline at end of file + describe "when the client is connected to the project and multiple docs", -> + + beforeEach -> + @RoomManager._roomsClientIsIn = sinon.stub().returns [@project_id, @doc_id, @other_doc_id] + @client.join = sinon.stub() + @client.leave = sinon.stub() + + describe "when this is the only client connected", -> + + beforeEach -> + # first and secondc calls are for the join, + # calls 2 and 3 are for the leave + @RoomManager._clientsInRoom + .withArgs(@client, @doc_id) + .onCall(0).returns(0) + .onSecondCall().returns(1) + .onCall(2).returns(1) + .onCall(3).returns(0) + @RoomManager._clientsInRoom + .withArgs(@client, @other_doc_id) + .onCall(0).returns(0) + .onCall(1).returns(1) + .onCall(2).returns(1) + .onCall(3).returns(0) + @RoomManager._clientsInRoom + .withArgs(@client, @project_id) + .onCall(0).returns(0) + .onCall(1).returns(1) + .onCall(2).returns(1) + .onCall(3).returns(0) + # put the client in the rooms + @RoomManager.joinProject(@client, @project_id) + @RoomManager.joinDoc(@client, @doc_id) + @RoomManager.joinDoc(@client, @other_doc_id) + # now leave the project + @RoomManager.leaveProjectAndDocs @client + + it "should leave all the docs", -> + @client.leave.calledWithExactly(@doc_id).should.equal true + @client.leave.calledWithExactly(@other_doc_id).should.equal true + + it "should leave the project", -> + @client.leave.calledWithExactly(@project_id).should.equal true + + it "should emit a 'doc-empty' event with the id for each doc", -> + @RoomEvents.emit.calledWithExactly('doc-empty', @doc_id).should.equal true + @RoomEvents.emit.calledWithExactly('doc-empty', @other_doc_id).should.equal true + + it "should emit a 'project-empty' event with the id for the project", -> + @RoomEvents.emit.calledWithExactly('project-empty', @project_id).should.equal true + + describe "when other clients are still connected", -> + + beforeEach -> + @RoomManager._clientsInRoom + .withArgs(@client, @doc_id) + .onFirstCall().returns(123) + .onSecondCall().returns(122) + @RoomManager._clientsInRoom + .withArgs(@client, @other_doc_id) + .onFirstCall().returns(123) + .onSecondCall().returns(122) + @RoomManager._clientsInRoom + .withArgs(@client, @project_id) + .onFirstCall().returns(123) + .onSecondCall().returns(122) + @RoomManager.leaveProjectAndDocs @client + + it "should leave all the docs", -> + @client.leave.calledWithExactly(@doc_id).should.equal true + @client.leave.calledWithExactly(@other_doc_id).should.equal true + + it "should leave the project", -> + @client.leave.calledWithExactly(@project_id).should.equal true + + it "should not emit any events", -> + @RoomEvents.emit.called.should.equal false \ No newline at end of file