DocumentUpdaterControllerTests.coffee 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. SandboxedModule = require('sandboxed-module')
  2. sinon = require('sinon')
  3. require('chai').should()
  4. modulePath = require('path').join __dirname, '../../../app/js/DocumentUpdaterController'
  5. MockClient = require "./helpers/MockClient"
  6. describe "DocumentUpdaterController", ->
  7. beforeEach ->
  8. @project_id = "project-id-123"
  9. @doc_id = "doc-id-123"
  10. @callback = sinon.stub()
  11. @io = { "mock": "socket.io" }
  12. @EditorUpdatesController = SandboxedModule.require modulePath, requires:
  13. "logger-sharelatex": @logger = { error: sinon.stub(), log: sinon.stub(), warn: sinon.stub() }
  14. "settings-sharelatex": @settings =
  15. redis:
  16. documentupdater:
  17. key_schema:
  18. pendingUpdates: ({doc_id}) -> "PendingUpdates:#{doc_id}"
  19. "redis-sharelatex" :
  20. createClient: () =>
  21. @rclient = {}
  22. "./SafeJsonParse": @SafeJsonParse =
  23. parse: (data, cb) => cb null, JSON.parse(data)
  24. "./EventLogger": @EventLogger = {checkEventOrder: sinon.stub()}
  25. "metrics-sharelatex": @metrics = {inc: sinon.stub()}
  26. describe "listenForUpdatesFromDocumentUpdater", ->
  27. beforeEach ->
  28. @rclient.subscribe = sinon.stub()
  29. @rclient.on = sinon.stub()
  30. @EditorUpdatesController.listenForUpdatesFromDocumentUpdater()
  31. it "should subscribe to the doc-updater stream", ->
  32. @rclient.subscribe.calledWith("applied-ops").should.equal true
  33. it "should register a callback to handle updates", ->
  34. @rclient.on.calledWith("message").should.equal true
  35. describe "_processMessageFromDocumentUpdater", ->
  36. describe "with bad JSON", ->
  37. beforeEach ->
  38. @SafeJsonParse.parse = sinon.stub().callsArgWith 1, new Error("oops")
  39. @EditorUpdatesController._processMessageFromDocumentUpdater @io, "applied-ops", "blah"
  40. it "should log an error", ->
  41. @logger.error.called.should.equal true
  42. describe "with update", ->
  43. beforeEach ->
  44. @message =
  45. doc_id: @doc_id
  46. op: {t: "foo", p: 12}
  47. @EditorUpdatesController._applyUpdateFromDocumentUpdater = sinon.stub()
  48. @EditorUpdatesController._processMessageFromDocumentUpdater @io, "applied-ops", JSON.stringify(@message)
  49. it "should apply the update", ->
  50. @EditorUpdatesController._applyUpdateFromDocumentUpdater
  51. .calledWith(@io, @doc_id, @message.op)
  52. .should.equal true
  53. describe "with error", ->
  54. beforeEach ->
  55. @message =
  56. doc_id: @doc_id
  57. error: "Something went wrong"
  58. @EditorUpdatesController._processErrorFromDocumentUpdater = sinon.stub()
  59. @EditorUpdatesController._processMessageFromDocumentUpdater @io, "applied-ops", JSON.stringify(@message)
  60. it "should process the error", ->
  61. @EditorUpdatesController._processErrorFromDocumentUpdater
  62. .calledWith(@io, @doc_id, @message.error)
  63. .should.equal true
  64. describe "_applyUpdateFromDocumentUpdater", ->
  65. beforeEach ->
  66. @sourceClient = new MockClient()
  67. @otherClients = [new MockClient(), new MockClient()]
  68. @update =
  69. op: [ t: "foo", p: 12 ]
  70. meta: source: @sourceClient.id
  71. v: @version = 42
  72. doc: @doc_id
  73. @io.sockets =
  74. clients: sinon.stub().returns([@sourceClient, @otherClients..., @sourceClient]) # include a duplicate client
  75. describe "normally", ->
  76. beforeEach ->
  77. @EditorUpdatesController._applyUpdateFromDocumentUpdater @io, @doc_id, @update
  78. it "should send a version bump to the source client", ->
  79. @sourceClient.emit
  80. .calledWith("otUpdateApplied", v: @version, doc: @doc_id)
  81. .should.equal true
  82. @sourceClient.emit.calledOnce.should.equal true
  83. it "should get the clients connected to the document", ->
  84. @io.sockets.clients
  85. .calledWith(@doc_id)
  86. .should.equal true
  87. it "should send the full update to the other clients", ->
  88. for client in @otherClients
  89. client.emit
  90. .calledWith("otUpdateApplied", @update)
  91. .should.equal true
  92. describe "with a duplicate op", ->
  93. beforeEach ->
  94. @update.dup = true
  95. @EditorUpdatesController._applyUpdateFromDocumentUpdater @io, @doc_id, @update
  96. it "should send a version bump to the source client as usual", ->
  97. @sourceClient.emit
  98. .calledWith("otUpdateApplied", v: @version, doc: @doc_id)
  99. .should.equal true
  100. it "should not send anything to the other clients (they've already had the op)", ->
  101. for client in @otherClients
  102. client.emit
  103. .calledWith("otUpdateApplied")
  104. .should.equal false
  105. describe "_processErrorFromDocumentUpdater", ->
  106. beforeEach ->
  107. @clients = [new MockClient(), new MockClient()]
  108. @io.sockets =
  109. clients: sinon.stub().returns(@clients)
  110. @EditorUpdatesController._processErrorFromDocumentUpdater @io, @doc_id, "Something went wrong"
  111. it "should log a warning", ->
  112. @logger.warn.called.should.equal true
  113. it "should disconnect all clients in that document", ->
  114. @io.sockets.clients.calledWith(@doc_id).should.equal true
  115. for client in @clients
  116. client.disconnect.called.should.equal true