PackManagerTests.coffee 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. assert = require('chai').assert
  4. should = chai.should()
  5. expect = chai.expect
  6. modulePath = "../../../../app/js/PackManager.js"
  7. SandboxedModule = require('sandboxed-module')
  8. {ObjectId} = require("mongojs")
  9. bson = require("bson")
  10. BSON = new bson.BSONPure()
  11. _ = require("underscore")
  12. tk = require "timekeeper"
  13. describe "PackManager", ->
  14. beforeEach ->
  15. tk.freeze(new Date())
  16. @PackManager = SandboxedModule.require modulePath, requires:
  17. "./mongojs" : { db: @db = {}, ObjectId: ObjectId, BSON: BSON }
  18. "./LockManager" : {}
  19. "./MongoAWS": {}
  20. "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() }
  21. @callback = sinon.stub()
  22. @doc_id = ObjectId().toString()
  23. @project_id = ObjectId().toString()
  24. @PackManager.MAX_COUNT = 512
  25. afterEach ->
  26. tk.reset()
  27. describe "insertCompressedUpdates", ->
  28. beforeEach ->
  29. @lastUpdate = {
  30. _id: "12345"
  31. pack: [
  32. { op: "op-1", meta: "meta-1", v: 1},
  33. { op: "op-2", meta: "meta-2", v: 2}
  34. ]
  35. n : 2
  36. sz : 100
  37. }
  38. @newUpdates = [
  39. { op: "op-3", meta: "meta-3", v: 3},
  40. { op: "op-4", meta: "meta-4", v: 4}
  41. ]
  42. @db.docHistory =
  43. save: sinon.stub().callsArg(1)
  44. insert: sinon.stub().callsArg(1)
  45. findAndModify: sinon.stub().callsArg(1)
  46. describe "with no last update", ->
  47. beforeEach ->
  48. @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4)
  49. @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback
  50. describe "for a small update", ->
  51. it "should insert the update into a new pack", ->
  52. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates, true).should.equal true
  53. it "should call the callback", ->
  54. @callback.called.should.equal true
  55. describe "for many small updates", ->
  56. beforeEach ->
  57. @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048])
  58. @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, false, @callback
  59. it "should append the initial updates to the existing pack", ->
  60. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[0...512], false).should.equal true
  61. it "should insert the first set remaining updates as a new pack", ->
  62. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[512...1024], false).should.equal true
  63. it "should insert the second set of remaining updates as a new pack", ->
  64. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1024...1536], false).should.equal true
  65. it "should insert the third set of remaining updates as a new pack", ->
  66. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1536...2048], false).should.equal true
  67. it "should insert the final set of remaining updates as a new pack", ->
  68. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2048..2048], false).should.equal true
  69. it "should call the callback", ->
  70. @callback.called.should.equal true
  71. describe "with an existing pack as the last update", ->
  72. beforeEach ->
  73. @PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5)
  74. @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4)
  75. @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback
  76. describe "for a small update", ->
  77. it "should append the update to the existing pack", ->
  78. @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates, false).should.equal true
  79. it "should not insert any new packs", ->
  80. @PackManager.insertUpdatesIntoNewPack.called.should.equal false
  81. it "should call the callback", ->
  82. @callback.called.should.equal true
  83. describe "for many small updates", ->
  84. beforeEach ->
  85. @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048])
  86. @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback
  87. it "should append the initial updates to the existing pack", ->
  88. @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0...510], false).should.equal true
  89. it "should insert the first set remaining updates as a new pack", ->
  90. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[510...1022], false).should.equal true
  91. it "should insert the second set of remaining updates as a new pack", ->
  92. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1022...1534], false).should.equal true
  93. it "should insert the third set of remaining updates as a new pack", ->
  94. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1534...2046], false).should.equal true
  95. it "should insert the final set of remaining updates as a new pack", ->
  96. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2046..2048], false).should.equal true
  97. it "should call the callback", ->
  98. @callback.called.should.equal true
  99. describe "for many big updates", ->
  100. beforeEach ->
  101. longString = ("a" for [0 .. (0.75*@PackManager.MAX_SIZE)]).join("")
  102. @newUpdates = ({ op: "op-#{i}-#{longString}", meta: "meta-#{i}", v: i} for i in [0..4])
  103. @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback
  104. it "should append the initial updates to the existing pack", ->
  105. @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0..0], false).should.equal true
  106. it "should insert the first set remaining updates as a new pack", ->
  107. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1..1], false).should.equal true
  108. it "should insert the second set of remaining updates as a new pack", ->
  109. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2..2], false).should.equal true
  110. it "should insert the third set of remaining updates as a new pack", ->
  111. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[3..3], false).should.equal true
  112. it "should insert the final set of remaining updates as a new pack", ->
  113. @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[4..4], false).should.equal true
  114. it "should call the callback", ->
  115. @callback.called.should.equal true
  116. describe "flushCompressedUpdates", ->
  117. describe "when there is no previous update", ->
  118. beforeEach ->
  119. @PackManager.flushCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback
  120. describe "for a small update that will expire", ->
  121. it "should insert the update into mongo", ->
  122. @db.docHistory.save.calledWithMatch({
  123. pack: @newUpdates,
  124. project_id: ObjectId(@project_id),
  125. doc_id: ObjectId(@doc_id)
  126. n: @newUpdates.length
  127. v: @newUpdates[0].v
  128. v_end: @newUpdates[@newUpdates.length-1].v
  129. }).should.equal true
  130. it "should set an expiry time in the future", ->
  131. @db.docHistory.save.calledWithMatch({
  132. expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)
  133. }).should.equal true
  134. it "should call the callback", ->
  135. @callback.called.should.equal true
  136. describe "when there is a recent previous update in mongo", ->
  137. beforeEach ->
  138. @lastUpdate = {
  139. _id: "12345"
  140. pack: [
  141. { op: "op-1", meta: "meta-1", v: 1},
  142. { op: "op-2", meta: "meta-2", v: 2}
  143. ]
  144. n : 2
  145. sz : 100
  146. expiresAt: new Date(Date.now())
  147. }
  148. @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback
  149. describe "for a small update that will expire", ->
  150. it "should append the update in mongo", ->
  151. @db.docHistory.findAndModify.calledWithMatch({
  152. query: {_id: @lastUpdate._id}
  153. update: { $push: {"pack" : {$each: @newUpdates}}, $set: {v_end: @newUpdates[@newUpdates.length-1].v}}
  154. }).should.equal true
  155. it "should set an expiry time in the future", ->
  156. @db.docHistory.findAndModify.calledWithMatch({
  157. update: {$set: {expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)}}
  158. }).should.equal true
  159. it "should call the callback", ->
  160. @callback.called.should.equal true
  161. describe "when there is an old previous update in mongo", ->
  162. beforeEach ->
  163. @lastUpdate = {
  164. _id: "12345"
  165. pack: [
  166. { op: "op-1", meta: "meta-1", v: 1},
  167. { op: "op-2", meta: "meta-2", v: 2}
  168. ]
  169. n : 2
  170. sz : 100
  171. meta: {start_ts: Date.now() - 30 * 24 * 3600 * 1000}
  172. expiresAt: new Date(Date.now() - 30 * 24 * 3600 * 1000)
  173. }
  174. @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback
  175. describe "for a small update that will expire", ->
  176. it "should insert the update into mongo", ->
  177. @db.docHistory.save.calledWithMatch({
  178. pack: @newUpdates,
  179. project_id: ObjectId(@project_id),
  180. doc_id: ObjectId(@doc_id)
  181. n: @newUpdates.length
  182. v: @newUpdates[0].v
  183. v_end: @newUpdates[@newUpdates.length-1].v
  184. }).should.equal true
  185. it "should set an expiry time in the future", ->
  186. @db.docHistory.save.calledWithMatch({
  187. expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)
  188. }).should.equal true
  189. it "should call the callback", ->
  190. @callback.called.should.equal true