2020-06-23 13:29:59 -04:00
|
|
|
/*
|
|
|
|
* decaffeinate suggestions:
|
|
|
|
* DS101: Remove unnecessary use of Array.from
|
|
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
|
|
*/
|
|
|
|
const SandboxedModule = require('sandboxed-module');
|
|
|
|
const sinon = require('sinon');
|
|
|
|
require('chai').should();
|
|
|
|
const modulePath = require('path').join(__dirname, '../../../app/js/WebsocketLoadBalancer');
|
|
|
|
|
|
|
|
describe("WebsocketLoadBalancer", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.rclient = {};
|
|
|
|
this.RoomEvents = {on: sinon.stub()};
|
|
|
|
this.WebsocketLoadBalancer = SandboxedModule.require(modulePath, { requires: {
|
|
|
|
"./RedisClientManager": {
|
2019-07-09 07:20:59 -04:00
|
|
|
createClientList: () => []
|
2020-06-23 13:29:59 -04:00
|
|
|
},
|
|
|
|
"logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }),
|
|
|
|
"./SafeJsonParse": (this.SafeJsonParse =
|
|
|
|
{parse: (data, cb) => cb(null, JSON.parse(data))}),
|
|
|
|
"./EventLogger": {checkEventOrder: sinon.stub()},
|
|
|
|
"./HealthCheckManager": {check: sinon.stub()},
|
|
|
|
"./RoomManager" : (this.RoomManager = {eventSource: sinon.stub().returns(this.RoomEvents)}),
|
|
|
|
"./ChannelManager": (this.ChannelManager = {publish: sinon.stub()}),
|
|
|
|
"./ConnectedUsersManager": (this.ConnectedUsersManager = {refreshClient: sinon.stub()})
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.io = {};
|
|
|
|
this.WebsocketLoadBalancer.rclientPubList = [{publish: sinon.stub()}];
|
|
|
|
this.WebsocketLoadBalancer.rclientSubList = [{
|
|
|
|
subscribe: sinon.stub(),
|
2014-11-13 11:03:37 -05:00
|
|
|
on: sinon.stub()
|
2020-06-23 13:29:59 -04:00
|
|
|
}];
|
|
|
|
|
|
|
|
this.room_id = "room-id";
|
|
|
|
this.message = "otUpdateApplied";
|
|
|
|
return this.payload = ["argument one", 42];});
|
|
|
|
|
|
|
|
describe("emitToRoom", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
return this.WebsocketLoadBalancer.emitToRoom(this.room_id, this.message, ...Array.from(this.payload));
|
|
|
|
});
|
|
|
|
|
|
|
|
return it("should publish the message to redis", function() {
|
|
|
|
return this.ChannelManager.publish
|
|
|
|
.calledWith(this.WebsocketLoadBalancer.rclientPubList[0], "editor-events", this.room_id, JSON.stringify({
|
|
|
|
room_id: this.room_id,
|
|
|
|
message: this.message,
|
|
|
|
payload: this.payload
|
|
|
|
}))
|
|
|
|
.should.equal(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("emitToAll", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.WebsocketLoadBalancer.emitToRoom = sinon.stub();
|
|
|
|
return this.WebsocketLoadBalancer.emitToAll(this.message, ...Array.from(this.payload));
|
|
|
|
});
|
|
|
|
|
|
|
|
return it("should emit to the room 'all'", function() {
|
|
|
|
return this.WebsocketLoadBalancer.emitToRoom
|
|
|
|
.calledWith("all", this.message, ...Array.from(this.payload))
|
|
|
|
.should.equal(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("listenForEditorEvents", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.WebsocketLoadBalancer._processEditorEvent = sinon.stub();
|
|
|
|
return this.WebsocketLoadBalancer.listenForEditorEvents();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("should subscribe to the editor-events channel", function() {
|
|
|
|
return this.WebsocketLoadBalancer.rclientSubList[0].subscribe
|
2014-11-13 11:03:37 -05:00
|
|
|
.calledWith("editor-events")
|
2020-06-23 13:29:59 -04:00
|
|
|
.should.equal(true);
|
|
|
|
});
|
2014-11-13 11:03:37 -05:00
|
|
|
|
2020-06-23 13:29:59 -04:00
|
|
|
return it("should process the events with _processEditorEvent", function() {
|
|
|
|
return this.WebsocketLoadBalancer.rclientSubList[0].on
|
2014-11-13 11:03:37 -05:00
|
|
|
.calledWith("message", sinon.match.func)
|
2020-06-23 13:29:59 -04:00
|
|
|
.should.equal(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return describe("_processEditorEvent", function() {
|
|
|
|
describe("with bad JSON", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.isRestrictedUser = false;
|
|
|
|
this.SafeJsonParse.parse = sinon.stub().callsArgWith(1, new Error("oops"));
|
|
|
|
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", "blah");
|
|
|
|
});
|
|
|
|
|
|
|
|
return it("should log an error", function() {
|
|
|
|
return this.logger.error.called.should.equal(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("with a designated room", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.io.sockets = {
|
2019-04-09 09:48:00 -04:00
|
|
|
clients: sinon.stub().returns([
|
2020-06-23 13:29:59 -04:00
|
|
|
{id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
|
|
|
|
{id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
|
|
|
|
{id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}} // duplicate client
|
2019-04-09 09:48:00 -04:00
|
|
|
])
|
2020-06-23 13:29:59 -04:00
|
|
|
};
|
|
|
|
const data = JSON.stringify({
|
|
|
|
room_id: this.room_id,
|
|
|
|
message: this.message,
|
|
|
|
payload: this.payload
|
|
|
|
});
|
|
|
|
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
|
|
|
});
|
|
|
|
|
|
|
|
return it("should send the message to all (unique) clients in the room", function() {
|
|
|
|
this.io.sockets.clients
|
|
|
|
.calledWith(this.room_id)
|
|
|
|
.should.equal(true);
|
|
|
|
this.emit1.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
|
|
|
this.emit2.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
|
|
|
return this.emit3.called.should.equal(false);
|
|
|
|
});
|
|
|
|
}); // duplicate client should be ignored
|
|
|
|
|
|
|
|
describe("with a designated room, and restricted clients, not restricted message", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.io.sockets = {
|
2019-10-30 09:52:36 -04:00
|
|
|
clients: sinon.stub().returns([
|
2020-06-23 13:29:59 -04:00
|
|
|
{id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
|
|
|
|
{id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
|
|
|
|
{id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}}, // duplicate client
|
|
|
|
{id: 'client-id-4', emit: (this.emit4 = sinon.stub()), ol_context: {is_restricted_user: true}}
|
2019-10-30 09:52:36 -04:00
|
|
|
])
|
2020-06-23 13:29:59 -04:00
|
|
|
};
|
|
|
|
const data = JSON.stringify({
|
|
|
|
room_id: this.room_id,
|
|
|
|
message: this.message,
|
|
|
|
payload: this.payload
|
|
|
|
});
|
|
|
|
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
|
|
|
});
|
|
|
|
|
|
|
|
return it("should send the message to all (unique) clients in the room", function() {
|
|
|
|
this.io.sockets.clients
|
|
|
|
.calledWith(this.room_id)
|
|
|
|
.should.equal(true);
|
|
|
|
this.emit1.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
|
|
|
this.emit2.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
|
|
|
this.emit3.called.should.equal(false); // duplicate client should be ignored
|
|
|
|
return this.emit4.called.should.equal(true);
|
|
|
|
});
|
|
|
|
}); // restricted client, but should be called
|
|
|
|
|
|
|
|
describe("with a designated room, and restricted clients, restricted message", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.io.sockets = {
|
2019-10-30 09:52:36 -04:00
|
|
|
clients: sinon.stub().returns([
|
2020-06-23 13:29:59 -04:00
|
|
|
{id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
|
|
|
|
{id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
|
|
|
|
{id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}}, // duplicate client
|
|
|
|
{id: 'client-id-4', emit: (this.emit4 = sinon.stub()), ol_context: {is_restricted_user: true}}
|
2019-10-30 09:52:36 -04:00
|
|
|
])
|
2020-06-23 13:29:59 -04:00
|
|
|
};
|
|
|
|
const data = JSON.stringify({
|
|
|
|
room_id: this.room_id,
|
|
|
|
message: (this.restrictedMessage = 'new-comment'),
|
|
|
|
payload: this.payload
|
|
|
|
});
|
|
|
|
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
|
|
|
});
|
|
|
|
|
|
|
|
return it("should send the message to all (unique) clients in the room, who are not restricted", function() {
|
|
|
|
this.io.sockets.clients
|
|
|
|
.calledWith(this.room_id)
|
|
|
|
.should.equal(true);
|
|
|
|
this.emit1.calledWith(this.restrictedMessage, ...Array.from(this.payload)).should.equal(true);
|
|
|
|
this.emit2.calledWith(this.restrictedMessage, ...Array.from(this.payload)).should.equal(true);
|
|
|
|
this.emit3.called.should.equal(false); // duplicate client should be ignored
|
|
|
|
return this.emit4.called.should.equal(false);
|
|
|
|
});
|
|
|
|
}); // restricted client, should not be called
|
|
|
|
|
|
|
|
return describe("when emitting to all", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.io.sockets =
|
|
|
|
{emit: (this.emit = sinon.stub())};
|
|
|
|
const data = JSON.stringify({
|
|
|
|
room_id: "all",
|
|
|
|
message: this.message,
|
|
|
|
payload: this.payload
|
|
|
|
});
|
|
|
|
return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
|
|
|
|
});
|
|
|
|
|
|
|
|
return it("should send the message to all clients", function() {
|
|
|
|
return this.emit.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|