WebsocketLoadBalancerTests.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /* eslint-disable
  2. no-return-assign,
  3. */
  4. // TODO: This file was created by bulk-decaffeinate.
  5. // Fix any style issues and re-enable lint.
  6. /*
  7. * decaffeinate suggestions:
  8. * DS101: Remove unnecessary use of Array.from
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  11. */
  12. const SandboxedModule = require('sandboxed-module');
  13. const sinon = require('sinon');
  14. require('chai').should();
  15. const modulePath = require('path').join(__dirname, '../../../app/js/WebsocketLoadBalancer');
  16. describe("WebsocketLoadBalancer", function() {
  17. beforeEach(function() {
  18. this.rclient = {};
  19. this.RoomEvents = {on: sinon.stub()};
  20. this.WebsocketLoadBalancer = SandboxedModule.require(modulePath, { requires: {
  21. "./RedisClientManager": {
  22. createClientList: () => []
  23. },
  24. "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }),
  25. "./SafeJsonParse": (this.SafeJsonParse =
  26. {parse: (data, cb) => cb(null, JSON.parse(data))}),
  27. "./EventLogger": {checkEventOrder: sinon.stub()},
  28. "./HealthCheckManager": {check: sinon.stub()},
  29. "./RoomManager" : (this.RoomManager = {eventSource: sinon.stub().returns(this.RoomEvents)}),
  30. "./ChannelManager": (this.ChannelManager = {publish: sinon.stub()}),
  31. "./ConnectedUsersManager": (this.ConnectedUsersManager = {refreshClient: sinon.stub()})
  32. }
  33. });
  34. this.io = {};
  35. this.WebsocketLoadBalancer.rclientPubList = [{publish: sinon.stub()}];
  36. this.WebsocketLoadBalancer.rclientSubList = [{
  37. subscribe: sinon.stub(),
  38. on: sinon.stub()
  39. }];
  40. this.room_id = "room-id";
  41. this.message = "otUpdateApplied";
  42. return this.payload = ["argument one", 42];});
  43. describe("emitToRoom", function() {
  44. beforeEach(function() {
  45. return this.WebsocketLoadBalancer.emitToRoom(this.room_id, this.message, ...Array.from(this.payload));
  46. });
  47. return it("should publish the message to redis", function() {
  48. return this.ChannelManager.publish
  49. .calledWith(this.WebsocketLoadBalancer.rclientPubList[0], "editor-events", this.room_id, JSON.stringify({
  50. room_id: this.room_id,
  51. message: this.message,
  52. payload: this.payload
  53. }))
  54. .should.equal(true);
  55. });
  56. });
  57. describe("emitToAll", function() {
  58. beforeEach(function() {
  59. this.WebsocketLoadBalancer.emitToRoom = sinon.stub();
  60. return this.WebsocketLoadBalancer.emitToAll(this.message, ...Array.from(this.payload));
  61. });
  62. return it("should emit to the room 'all'", function() {
  63. return this.WebsocketLoadBalancer.emitToRoom
  64. .calledWith("all", this.message, ...Array.from(this.payload))
  65. .should.equal(true);
  66. });
  67. });
  68. describe("listenForEditorEvents", function() {
  69. beforeEach(function() {
  70. this.WebsocketLoadBalancer._processEditorEvent = sinon.stub();
  71. return this.WebsocketLoadBalancer.listenForEditorEvents();
  72. });
  73. it("should subscribe to the editor-events channel", function() {
  74. return this.WebsocketLoadBalancer.rclientSubList[0].subscribe
  75. .calledWith("editor-events")
  76. .should.equal(true);
  77. });
  78. return it("should process the events with _processEditorEvent", function() {
  79. return this.WebsocketLoadBalancer.rclientSubList[0].on
  80. .calledWith("message", sinon.match.func)
  81. .should.equal(true);
  82. });
  83. });
  84. return describe("_processEditorEvent", function() {
  85. describe("with bad JSON", function() {
  86. beforeEach(function() {
  87. this.isRestrictedUser = false;
  88. this.SafeJsonParse.parse = sinon.stub().callsArgWith(1, new Error("oops"));
  89. return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", "blah");
  90. });
  91. return it("should log an error", function() {
  92. return this.logger.error.called.should.equal(true);
  93. });
  94. });
  95. describe("with a designated room", function() {
  96. beforeEach(function() {
  97. this.io.sockets = {
  98. clients: sinon.stub().returns([
  99. {id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
  100. {id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
  101. {id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}} // duplicate client
  102. ])
  103. };
  104. const data = JSON.stringify({
  105. room_id: this.room_id,
  106. message: this.message,
  107. payload: this.payload
  108. });
  109. return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
  110. });
  111. return it("should send the message to all (unique) clients in the room", function() {
  112. this.io.sockets.clients
  113. .calledWith(this.room_id)
  114. .should.equal(true);
  115. this.emit1.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
  116. this.emit2.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
  117. return this.emit3.called.should.equal(false);
  118. });
  119. }); // duplicate client should be ignored
  120. describe("with a designated room, and restricted clients, not restricted message", function() {
  121. beforeEach(function() {
  122. this.io.sockets = {
  123. clients: sinon.stub().returns([
  124. {id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
  125. {id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
  126. {id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}}, // duplicate client
  127. {id: 'client-id-4', emit: (this.emit4 = sinon.stub()), ol_context: {is_restricted_user: true}}
  128. ])
  129. };
  130. const data = JSON.stringify({
  131. room_id: this.room_id,
  132. message: this.message,
  133. payload: this.payload
  134. });
  135. return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
  136. });
  137. return it("should send the message to all (unique) clients in the room", function() {
  138. this.io.sockets.clients
  139. .calledWith(this.room_id)
  140. .should.equal(true);
  141. this.emit1.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
  142. this.emit2.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
  143. this.emit3.called.should.equal(false); // duplicate client should be ignored
  144. return this.emit4.called.should.equal(true);
  145. });
  146. }); // restricted client, but should be called
  147. describe("with a designated room, and restricted clients, restricted message", function() {
  148. beforeEach(function() {
  149. this.io.sockets = {
  150. clients: sinon.stub().returns([
  151. {id: 'client-id-1', emit: (this.emit1 = sinon.stub()), ol_context: {}},
  152. {id: 'client-id-2', emit: (this.emit2 = sinon.stub()), ol_context: {}},
  153. {id: 'client-id-1', emit: (this.emit3 = sinon.stub()), ol_context: {}}, // duplicate client
  154. {id: 'client-id-4', emit: (this.emit4 = sinon.stub()), ol_context: {is_restricted_user: true}}
  155. ])
  156. };
  157. const data = JSON.stringify({
  158. room_id: this.room_id,
  159. message: (this.restrictedMessage = 'new-comment'),
  160. payload: this.payload
  161. });
  162. return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
  163. });
  164. return it("should send the message to all (unique) clients in the room, who are not restricted", function() {
  165. this.io.sockets.clients
  166. .calledWith(this.room_id)
  167. .should.equal(true);
  168. this.emit1.calledWith(this.restrictedMessage, ...Array.from(this.payload)).should.equal(true);
  169. this.emit2.calledWith(this.restrictedMessage, ...Array.from(this.payload)).should.equal(true);
  170. this.emit3.called.should.equal(false); // duplicate client should be ignored
  171. return this.emit4.called.should.equal(false);
  172. });
  173. }); // restricted client, should not be called
  174. return describe("when emitting to all", function() {
  175. beforeEach(function() {
  176. this.io.sockets =
  177. {emit: (this.emit = sinon.stub())};
  178. const data = JSON.stringify({
  179. room_id: "all",
  180. message: this.message,
  181. payload: this.payload
  182. });
  183. return this.WebsocketLoadBalancer._processEditorEvent(this.io, "editor-events", data);
  184. });
  185. return it("should send the message to all clients", function() {
  186. return this.emit.calledWith(this.message, ...Array.from(this.payload)).should.equal(true);
  187. });
  188. });
  189. });
  190. });