ShareJsUpdateManagerTests.coffee 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. modulePath = "../../../app/js/ShareJsUpdateManager.js"
  5. SandboxedModule = require('sandboxed-module')
  6. describe "ShareJsUpdateManager", ->
  7. beforeEach ->
  8. @project_id = "project-id-123"
  9. @doc_id = "document-id-123"
  10. @callback = sinon.stub()
  11. @ShareJsUpdateManager = SandboxedModule.require modulePath,
  12. requires:
  13. "./sharejs/server/model":
  14. class Model
  15. constructor: (@db) ->
  16. "./ShareJsDB" : @ShareJsDB = { mockDB: true }
  17. "redis" : createClient: () => @rclient = auth:->
  18. "logger-sharelatex": @logger = { log: sinon.stub() }
  19. globals:
  20. clearTimeout: @clearTimeout = sinon.stub()
  21. describe "applyUpdates", ->
  22. beforeEach ->
  23. @version = 34
  24. @model =
  25. applyOp: sinon.stub().callsArg(2)
  26. getSnapshot: sinon.stub()
  27. @ShareJsUpdateManager.getNewShareJsModel = sinon.stub().returns(@model)
  28. @ShareJsUpdateManager._listenForOps = sinon.stub()
  29. @ShareJsUpdateManager.removeDocFromCache = sinon.stub().callsArg(1)
  30. describe "with a text document", ->
  31. beforeEach ->
  32. @updates = [
  33. {p: 4, t: "foo"}
  34. {p: 6, t: "bar"}
  35. ]
  36. @updatedDocLines = ["one", "two"]
  37. describe "successfully", ->
  38. beforeEach (done) ->
  39. @model.getSnapshot.callsArgWith(1, null, {snapshot: @updatedDocLines.join("\n"), v: @version})
  40. @ShareJsUpdateManager.applyUpdates @project_id, @doc_id, @updates, (err, docLines, version) =>
  41. @callback(err, docLines, version)
  42. done()
  43. it "should create a new ShareJs model", ->
  44. @ShareJsUpdateManager.getNewShareJsModel
  45. .called.should.equal true
  46. it "should listen for ops on the model", ->
  47. @ShareJsUpdateManager._listenForOps
  48. .calledWith(@model)
  49. .should.equal true
  50. it "should send each update to ShareJs", ->
  51. for update in @updates
  52. @model.applyOp
  53. .calledWith("#{@project_id}:#{@doc_id}", update).should.equal true
  54. it "should get the updated doc lines", ->
  55. @model.getSnapshot
  56. .calledWith("#{@project_id}:#{@doc_id}")
  57. .should.equal true
  58. it "should return the updated doc lines", ->
  59. @callback.calledWith(null, @updatedDocLines, @version).should.equal true
  60. describe "when applyOp fails", ->
  61. beforeEach (done) ->
  62. @error = new Error("Something went wrong")
  63. @ShareJsUpdateManager._sendError = sinon.stub()
  64. @model.applyOp = sinon.stub().callsArgWith(2, @error)
  65. @ShareJsUpdateManager.applyUpdates @project_id, @doc_id, @updates, (err, docLines, version) =>
  66. @callback(err, docLines, version)
  67. done()
  68. it "should call sendError with the error", ->
  69. @ShareJsUpdateManager._sendError
  70. .calledWith(@project_id, @doc_id, @error)
  71. .should.equal true
  72. it "should call the callback with the error", ->
  73. @callback.calledWith(@error).should.equal true
  74. describe "when getSnapshot fails", ->
  75. beforeEach (done) ->
  76. @error = new Error("Something went wrong")
  77. @ShareJsUpdateManager._sendError = sinon.stub()
  78. @model.getSnapshot.callsArgWith(1, @error)
  79. @ShareJsUpdateManager.applyUpdates @project_id, @doc_id, @updates, (err, docLines, version) =>
  80. @callback(err, docLines, version)
  81. done()
  82. it "should call sendError with the error", ->
  83. @ShareJsUpdateManager._sendError
  84. .calledWith(@project_id, @doc_id, @error)
  85. .should.equal true
  86. it "should call the callback with the error", ->
  87. @callback.calledWith(@error).should.equal true
  88. describe "with a JSON document", ->
  89. beforeEach ->
  90. @updates = [
  91. {p: ["lines", 0], dl: { foo: "bar "}}
  92. ]
  93. @docLines = [text: "one", text: "two"]
  94. describe "successfully", ->
  95. beforeEach (done) ->
  96. @model.getSnapshot.callsArgWith(1, null, {snapshot: {lines: @docLines}, v: @version})
  97. @ShareJsUpdateManager.applyUpdates @project_id, @doc_id, @updates, (err, docLines, version) =>
  98. @callback(err, docLines, version)
  99. done()
  100. it "should create a new ShareJs model", ->
  101. @ShareJsUpdateManager.getNewShareJsModel
  102. .called.should.equal true
  103. it "should listen for ops on the model", ->
  104. @ShareJsUpdateManager._listenForOps
  105. .calledWith(@model)
  106. .should.equal true
  107. it "should send each update to ShareJs", ->
  108. for update in @updates
  109. @model.applyOp
  110. .calledWith("#{@project_id}:#{@doc_id}", update).should.equal true
  111. it "should get the updated doc lines", ->
  112. @model.getSnapshot
  113. .calledWith("#{@project_id}:#{@doc_id}")
  114. .should.equal true
  115. it "should return the updated doc lines", ->
  116. @callback.calledWith(null, @docLines, @version).should.equal true
  117. describe "_listenForOps", ->
  118. beforeEach ->
  119. @model = on: (event, callback) =>
  120. @callback = callback
  121. sinon.spy @model, "on"
  122. @ShareJsUpdateManager._listenForOps(@model)
  123. it "should listen to the model for updates", ->
  124. @model.on.calledWith("applyOp")
  125. .should.equal true
  126. describe "the callback", ->
  127. beforeEach ->
  128. @opData =
  129. op: {t: "foo", p: 1}
  130. meta: source: "bar"
  131. @rclient.publish = sinon.stub()
  132. @callback("#{@project_id}:#{@doc_id}", @opData)
  133. it "should publish the op to redis", ->
  134. @rclient.publish
  135. .calledWith("applied-ops", JSON.stringify(project_id: @project_id, doc_id: @doc_id, op: @opData))
  136. .should.equal true
  137. describe "_sendError", ->
  138. beforeEach ->
  139. @error_text = "Something went wrong"
  140. @rclient.publish = sinon.stub()
  141. @ShareJsUpdateManager._sendError(@project_id, @doc_id, new Error(@error_text))
  142. it "should publish the error to the redis stream", ->
  143. @rclient.publish
  144. .calledWith("applied-ops", JSON.stringify(project_id: @project_id, doc_id: @doc_id, error: @error_text))
  145. .should.equal true