overleaf/services/real-time/test/unit/coffee/RoomManagerTests.js

359 lines
13 KiB
JavaScript
Raw Normal View History

/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const chai = require('chai');
const {
expect
} = chai;
const should = chai.should();
const sinon = require("sinon");
const modulePath = "../../../app/js/RoomManager.js";
const SandboxedModule = require('sandboxed-module');
describe('RoomManager', function() {
beforeEach(function() {
this.project_id = "project-id-123";
this.doc_id = "doc-id-456";
this.other_doc_id = "doc-id-789";
this.client = {namespace: {name: ''}, id: "first-client"};
this.RoomManager = SandboxedModule.require(modulePath, { requires: {
"settings-sharelatex": (this.settings = {}),
"logger-sharelatex": (this.logger = { log: sinon.stub(), warn: sinon.stub(), error: sinon.stub() }),
"metrics-sharelatex": (this.metrics = { gauge: sinon.stub() })
}
});
this.RoomManager._clientsInRoom = sinon.stub();
this.RoomManager._clientAlreadyInRoom = sinon.stub();
this.RoomEvents = this.RoomManager.eventSource();
sinon.spy(this.RoomEvents, 'emit');
return sinon.spy(this.RoomEvents, 'once');
});
2019-07-19 06:58:40 -04:00
describe("emitOnCompletion", () => describe("when a subscribe errors", function() {
afterEach(function() {
return process.removeListener("unhandledRejection", this.onUnhandled);
});
beforeEach(function(done) {
this.onUnhandled = error => {
this.unhandledError = error;
return done(new Error(`unhandledRejection: ${error.message}`));
};
process.on("unhandledRejection", this.onUnhandled);
let reject = undefined;
const subscribePromise = new Promise((_, r) => reject = r);
const promises = [subscribePromise];
const eventName = "project-subscribed-123";
this.RoomEvents.once(eventName, () => setTimeout(done, 100));
this.RoomManager.emitOnCompletion(promises, eventName);
return setTimeout(() => reject(new Error("subscribe failed")));
});
return it("should keep going", function() {
return expect(this.unhandledError).to.not.exist;
});
}));
describe("joinProject", function() {
2019-07-19 06:58:40 -04:00
describe("when the project room is empty", function() {
beforeEach(function(done) {
this.RoomManager._clientsInRoom
.withArgs(this.client, this.project_id)
.onFirstCall().returns(0);
this.client.join = sinon.stub();
this.callback = sinon.stub();
this.RoomEvents.on('project-active', id => {
return setTimeout(() => {
return this.RoomEvents.emit(`project-subscribed-${id}`);
}
, 100);
});
return this.RoomManager.joinProject(this.client, this.project_id, err => {
this.callback(err);
return done();
});
});
it("should emit a 'project-active' event with the id", function() {
return this.RoomEvents.emit.calledWithExactly('project-active', this.project_id).should.equal(true);
});
it("should listen for the 'project-subscribed-id' event", function() {
return this.RoomEvents.once.calledWith(`project-subscribed-${this.project_id}`).should.equal(true);
});
return it("should join the room using the id", function() {
return this.client.join.calledWithExactly(this.project_id).should.equal(true);
});
});
return describe("when there are other clients in the project room", function() {
beforeEach(function() {
this.RoomManager._clientsInRoom
.withArgs(this.client, this.project_id)
2019-07-22 06:23:43 -04:00
.onFirstCall().returns(123)
.onSecondCall().returns(124);
this.client.join = sinon.stub();
return this.RoomManager.joinProject(this.client, this.project_id);
});
it("should join the room using the id", function() {
return this.client.join.called.should.equal(true);
});
return it("should not emit any events", function() {
return this.RoomEvents.emit.called.should.equal(false);
});
});
});
describe("joinDoc", function() {
describe("when the doc room is empty", function() {
beforeEach(function(done) {
this.RoomManager._clientsInRoom
.withArgs(this.client, this.doc_id)
.onFirstCall().returns(0);
this.client.join = sinon.stub();
this.callback = sinon.stub();
this.RoomEvents.on('doc-active', id => {
return setTimeout(() => {
return this.RoomEvents.emit(`doc-subscribed-${id}`);
}
, 100);
});
return this.RoomManager.joinDoc(this.client, this.doc_id, err => {
this.callback(err);
return done();
});
});
it("should emit a 'doc-active' event with the id", function() {
return this.RoomEvents.emit.calledWithExactly('doc-active', this.doc_id).should.equal(true);
});
it("should listen for the 'doc-subscribed-id' event", function() {
return this.RoomEvents.once.calledWith(`doc-subscribed-${this.doc_id}`).should.equal(true);
});
return it("should join the room using the id", function() {
return this.client.join.calledWithExactly(this.doc_id).should.equal(true);
});
});
return describe("when there are other clients in the doc room", function() {
beforeEach(function() {
this.RoomManager._clientsInRoom
.withArgs(this.client, this.doc_id)
2019-07-22 06:23:43 -04:00
.onFirstCall().returns(123)
.onSecondCall().returns(124);
this.client.join = sinon.stub();
return this.RoomManager.joinDoc(this.client, this.doc_id);
});
it("should join the room using the id", function() {
return this.client.join.called.should.equal(true);
});
return it("should not emit any events", function() {
return this.RoomEvents.emit.called.should.equal(false);
});
});
});
describe("leaveDoc", function() {
describe("when doc room will be empty after this client has left", function() {
beforeEach(function() {
this.RoomManager._clientAlreadyInRoom
.withArgs(this.client, this.doc_id)
.returns(true);
this.RoomManager._clientsInRoom
.withArgs(this.client, this.doc_id)
.onCall(0).returns(0);
this.client.leave = sinon.stub();
return this.RoomManager.leaveDoc(this.client, this.doc_id);
});
it("should leave the room using the id", function() {
return this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
});
return it("should emit a 'doc-empty' event with the id", function() {
return this.RoomEvents.emit.calledWithExactly('doc-empty', this.doc_id).should.equal(true);
});
});
describe("when there are other clients in the doc room", function() {
beforeEach(function() {
this.RoomManager._clientAlreadyInRoom
.withArgs(this.client, this.doc_id)
.returns(true);
this.RoomManager._clientsInRoom
.withArgs(this.client, this.doc_id)
.onCall(0).returns(123);
this.client.leave = sinon.stub();
return this.RoomManager.leaveDoc(this.client, this.doc_id);
});
it("should leave the room using the id", function() {
return this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
});
return it("should not emit any events", function() {
return this.RoomEvents.emit.called.should.equal(false);
});
});
return describe("when the client is not in the doc room", function() {
beforeEach(function() {
this.RoomManager._clientAlreadyInRoom
.withArgs(this.client, this.doc_id)
.returns(false);
this.RoomManager._clientsInRoom
.withArgs(this.client, this.doc_id)
.onCall(0).returns(0);
this.client.leave = sinon.stub();
return this.RoomManager.leaveDoc(this.client, this.doc_id);
});
it("should not leave the room", function() {
return this.client.leave.called.should.equal(false);
});
return it("should not emit any events", function() {
return this.RoomEvents.emit.called.should.equal(false);
});
});
});
return describe("leaveProjectAndDocs", () => describe("when the client is connected to the project and multiple docs", function() {
beforeEach(function() {
this.RoomManager._roomsClientIsIn = sinon.stub().returns([this.project_id, this.doc_id, this.other_doc_id]);
this.client.join = sinon.stub();
return this.client.leave = sinon.stub();
});
describe("when this is the only client connected", function() {
beforeEach(function(done) {
// first call is for the join,
// second for the leave
this.RoomManager._clientsInRoom
.withArgs(this.client, this.doc_id)
.onCall(0).returns(0)
.onCall(1).returns(0);
this.RoomManager._clientsInRoom
.withArgs(this.client, this.other_doc_id)
.onCall(0).returns(0)
.onCall(1).returns(0);
this.RoomManager._clientsInRoom
.withArgs(this.client, this.project_id)
.onCall(0).returns(0)
.onCall(1).returns(0);
this.RoomManager._clientAlreadyInRoom
.withArgs(this.client, this.doc_id)
.returns(true)
.withArgs(this.client, this.other_doc_id)
.returns(true)
.withArgs(this.client, this.project_id)
.returns(true);
this.RoomEvents.on('project-active', id => {
return setTimeout(() => {
return this.RoomEvents.emit(`project-subscribed-${id}`);
}
, 100);
});
this.RoomEvents.on('doc-active', id => {
return setTimeout(() => {
return this.RoomEvents.emit(`doc-subscribed-${id}`);
}
, 100);
});
// put the client in the rooms
return this.RoomManager.joinProject(this.client, this.project_id, () => {
return this.RoomManager.joinDoc(this.client, this.doc_id, () => {
return this.RoomManager.joinDoc(this.client, this.other_doc_id, () => {
// now leave the project
this.RoomManager.leaveProjectAndDocs(this.client);
return done();
});
});
});
});
it("should leave all the docs", function() {
this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
return this.client.leave.calledWithExactly(this.other_doc_id).should.equal(true);
});
it("should leave the project", function() {
return this.client.leave.calledWithExactly(this.project_id).should.equal(true);
});
it("should emit a 'doc-empty' event with the id for each doc", function() {
this.RoomEvents.emit.calledWithExactly('doc-empty', this.doc_id).should.equal(true);
return this.RoomEvents.emit.calledWithExactly('doc-empty', this.other_doc_id).should.equal(true);
});
return it("should emit a 'project-empty' event with the id for the project", function() {
return this.RoomEvents.emit.calledWithExactly('project-empty', this.project_id).should.equal(true);
});
});
return describe("when other clients are still connected", function() {
beforeEach(function() {
this.RoomManager._clientsInRoom
.withArgs(this.client, this.doc_id)
.onFirstCall().returns(123)
.onSecondCall().returns(122);
this.RoomManager._clientsInRoom
.withArgs(this.client, this.other_doc_id)
.onFirstCall().returns(123)
.onSecondCall().returns(122);
this.RoomManager._clientsInRoom
.withArgs(this.client, this.project_id)
.onFirstCall().returns(123)
.onSecondCall().returns(122);
this.RoomManager._clientAlreadyInRoom
.withArgs(this.client, this.doc_id)
.returns(true)
.withArgs(this.client, this.other_doc_id)
.returns(true)
.withArgs(this.client, this.project_id)
.returns(true);
return this.RoomManager.leaveProjectAndDocs(this.client);
});
it("should leave all the docs", function() {
this.client.leave.calledWithExactly(this.doc_id).should.equal(true);
return this.client.leave.calledWithExactly(this.other_doc_id).should.equal(true);
});
it("should leave the project", function() {
return this.client.leave.calledWithExactly(this.project_id).should.equal(true);
});
return it("should not emit any events", function() {
return this.RoomEvents.emit.called.should.equal(false);
});
});
}));
});