Brian Gough 10 лет назад
Родитель
Сommit
ae61d1261e

+ 232 - 0
services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee

@@ -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

+ 2 - 1
services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee

@@ -67,6 +67,7 @@ describe "UpdatesManager", ->
 				@MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v)
 				@MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2)
 				@MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4)
+				@PackManager.insertCompressedUpdates = sinon.stub().callsArg(5)
 				@UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates)
 
 			describe "when the raw ops start where the existing history ends", ->
@@ -109,7 +110,7 @@ describe "UpdatesManager", ->
 						.calledWith(@doc_id)
 						.should.equal true
 
-				it "should defer the compression of raw ops to PackManager", ->
+				it "should defer the compression of raw ops until they are written in a new pack", ->
 					@UpdateCompressor.compressRawUpdates
 						.should.not.be.called