|
|
@@ -0,0 +1,232 @@
|
|
|
+sinon = require('sinon')
|
|
|
+chai = require('chai')
|
|
|
+should = chai.should()
|
|
|
+expect = chai.expect
|
|
|
+modulePath = "../../../../app/js/PackManager.js"
|
|
|
+SandboxedModule = require('sandboxed-module')
|
|
|
+{ObjectId} = require("mongojs")
|
|
|
+bson = require("bson")
|
|
|
+BSON = new bson.BSONPure()
|
|
|
+
|
|
|
+tk = require "timekeeper"
|
|
|
+
|
|
|
+describe "PackManager", ->
|
|
|
+ beforeEach ->
|
|
|
+ tk.freeze(new Date())
|
|
|
+ @PackManager = SandboxedModule.require modulePath, requires:
|
|
|
+ "./mongojs" : { db: @db = {}, ObjectId: ObjectId, BSON: BSON }
|
|
|
+ "./LockManager" : {}
|
|
|
+ "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() }
|
|
|
+ @callback = sinon.stub()
|
|
|
+ @doc_id = ObjectId().toString()
|
|
|
+ @project_id = ObjectId().toString()
|
|
|
+
|
|
|
+ afterEach ->
|
|
|
+ tk.reset()
|
|
|
+
|
|
|
+ describe "insertCompressedUpdates", ->
|
|
|
+ beforeEach ->
|
|
|
+ @lastUpdate = {
|
|
|
+ _id: "12345"
|
|
|
+ pack: [
|
|
|
+ { op: "op-1", meta: "meta-1", v: 1},
|
|
|
+ { op: "op-2", meta: "meta-2", v: 2}
|
|
|
+ ]
|
|
|
+ n : 2
|
|
|
+ sz : 100
|
|
|
+ }
|
|
|
+ @newUpdates = [
|
|
|
+ { op: "op-3", meta: "meta-3", v: 3},
|
|
|
+ { op: "op-4", meta: "meta-4", v: 4}
|
|
|
+ ]
|
|
|
+ @db.docHistory =
|
|
|
+ insert: sinon.stub().callsArg(1)
|
|
|
+ findAndModify: sinon.stub().callsArg(1)
|
|
|
+
|
|
|
+ describe "with no last update", ->
|
|
|
+ beforeEach ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4)
|
|
|
+ @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback
|
|
|
+
|
|
|
+ describe "for a small update", ->
|
|
|
+ it "should insert the update into a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates, true).should.equal true
|
|
|
+
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|
|
|
+
|
|
|
+ describe "for many small updates", ->
|
|
|
+ beforeEach ->
|
|
|
+ @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048])
|
|
|
+ @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, false, @callback
|
|
|
+
|
|
|
+ it "should append the initial updates to the existing pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[0...512], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the first set remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[512...1024], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the second set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1024...1536], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the third set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1536...2048], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the final set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2048..2048], false).should.equal true
|
|
|
+
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ describe "with an existing pack as the last update", ->
|
|
|
+ beforeEach ->
|
|
|
+ @PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5)
|
|
|
+ @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4)
|
|
|
+ @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback
|
|
|
+
|
|
|
+ describe "for a small update", ->
|
|
|
+ it "should append the update to the existing pack", ->
|
|
|
+ @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates, false).should.equal true
|
|
|
+ it "should not insert any new packs", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.called.should.equal false
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|
|
|
+
|
|
|
+ describe "for many small updates", ->
|
|
|
+ beforeEach ->
|
|
|
+ @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048])
|
|
|
+ @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback
|
|
|
+
|
|
|
+ it "should append the initial updates to the existing pack", ->
|
|
|
+ @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0...510], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the first set remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[510...1022], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the second set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1022...1534], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the third set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1534...2046], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the final set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2046..2048], false).should.equal true
|
|
|
+
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|
|
|
+
|
|
|
+ describe "for many big updates", ->
|
|
|
+ beforeEach ->
|
|
|
+ longString = ("a" for [0 .. (0.75*@PackManager.MAX_SIZE)]).join("")
|
|
|
+ @newUpdates = ({ op: "op-#{i}-#{longString}", meta: "meta-#{i}", v: i} for i in [0..4])
|
|
|
+ @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback
|
|
|
+
|
|
|
+ it "should append the initial updates to the existing pack", ->
|
|
|
+ @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0..0], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the first set remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1..1], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the second set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2..2], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the third set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[3..3], false).should.equal true
|
|
|
+
|
|
|
+ it "should insert the final set of remaining updates as a new pack", ->
|
|
|
+ @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[4..4], false).should.equal true
|
|
|
+
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|
|
|
+
|
|
|
+ describe "flushCompressedUpdates", ->
|
|
|
+ describe "when there is no previous update", ->
|
|
|
+ beforeEach ->
|
|
|
+ @PackManager.flushCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback
|
|
|
+
|
|
|
+ describe "for a small update that will expire", ->
|
|
|
+ it "should insert the update into mongo", ->
|
|
|
+ @db.docHistory.insert.calledWithMatch({
|
|
|
+ pack: @newUpdates,
|
|
|
+ project_id: ObjectId(@project_id),
|
|
|
+ doc_id: ObjectId(@doc_id)
|
|
|
+ n: @newUpdates.length
|
|
|
+ v: @newUpdates[0].v
|
|
|
+ v_end: @newUpdates[@newUpdates.length-1].v
|
|
|
+ }).should.equal true
|
|
|
+
|
|
|
+ it "should set an expiry time in the future", ->
|
|
|
+ @db.docHistory.insert.calledWithMatch({
|
|
|
+ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)
|
|
|
+ }).should.equal true
|
|
|
+
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|
|
|
+
|
|
|
+ describe "when there is a recent previous update in mongo", ->
|
|
|
+ beforeEach ->
|
|
|
+ @lastUpdate = {
|
|
|
+ _id: "12345"
|
|
|
+ pack: [
|
|
|
+ { op: "op-1", meta: "meta-1", v: 1},
|
|
|
+ { op: "op-2", meta: "meta-2", v: 2}
|
|
|
+ ]
|
|
|
+ n : 2
|
|
|
+ sz : 100
|
|
|
+ expiresAt: new Date(Date.now())
|
|
|
+ }
|
|
|
+
|
|
|
+ @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback
|
|
|
+
|
|
|
+ describe "for a small update that will expire", ->
|
|
|
+ it "should append the update in mongo", ->
|
|
|
+ @db.docHistory.findAndModify.calledWithMatch({
|
|
|
+ query: {_id: @lastUpdate._id}
|
|
|
+ update: { $push: {"pack" : {$each: @newUpdates}}, $set: {v_end: @newUpdates[@newUpdates.length-1].v}}
|
|
|
+ }).should.equal true
|
|
|
+
|
|
|
+ it "should set an expiry time in the future", ->
|
|
|
+ @db.docHistory.findAndModify.calledWithMatch({
|
|
|
+ update: {$set: {expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)}}
|
|
|
+ }).should.equal true
|
|
|
+
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|
|
|
+
|
|
|
+
|
|
|
+ describe "when there is an old previous update in mongo", ->
|
|
|
+ beforeEach ->
|
|
|
+ @lastUpdate = {
|
|
|
+ _id: "12345"
|
|
|
+ pack: [
|
|
|
+ { op: "op-1", meta: "meta-1", v: 1},
|
|
|
+ { op: "op-2", meta: "meta-2", v: 2}
|
|
|
+ ]
|
|
|
+ n : 2
|
|
|
+ sz : 100
|
|
|
+ meta: {start_ts: Date.now() - 30 * 24 * 3600 * 1000}
|
|
|
+ expiresAt: new Date(Date.now() - 30 * 24 * 3600 * 1000)
|
|
|
+ }
|
|
|
+
|
|
|
+ @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback
|
|
|
+
|
|
|
+ describe "for a small update that will expire", ->
|
|
|
+ it "should insert the update into mongo", ->
|
|
|
+ @db.docHistory.insert.calledWithMatch({
|
|
|
+ pack: @newUpdates,
|
|
|
+ project_id: ObjectId(@project_id),
|
|
|
+ doc_id: ObjectId(@doc_id)
|
|
|
+ n: @newUpdates.length
|
|
|
+ v: @newUpdates[0].v
|
|
|
+ v_end: @newUpdates[@newUpdates.length-1].v
|
|
|
+ }).should.equal true
|
|
|
+
|
|
|
+ it "should set an expiry time in the future", ->
|
|
|
+ @db.docHistory.insert.calledWithMatch({
|
|
|
+ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)
|
|
|
+ }).should.equal true
|
|
|
+
|
|
|
+ it "should call the callback", ->
|
|
|
+ @callback.called.should.equal true
|