2020-06-23 13:29:59 -04:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
2020-06-23 13:29:59 -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
|
|
|
|
2020-06-23 13:29:59 -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)
|
2020-06-23 13:29:59 -04:00
|
|
|
.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)
|
2020-06-23 13:29:59 -04:00
|
|
|
.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);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
});
|