| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- sinon = require('sinon')
- chai = require('chai')
- assert = require('chai').assert
- 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()
- _ = require("underscore")
- tk = require "timekeeper"
- describe "PackManager", ->
- beforeEach ->
- tk.freeze(new Date())
- @PackManager = SandboxedModule.require modulePath, requires:
- "./mongojs" : { db: @db = {}, ObjectId: ObjectId, BSON: BSON }
- "./LockManager" : {}
- "./MongoAWS": {}
- "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() }
- 'metrics-sharelatex': {inc: ()->}
- "./ProjectIterator": require("../../../../app/js/ProjectIterator.js") # Cache for speed
- "settings-sharelatex":
- redis: lock: key_schema: {}
- @callback = sinon.stub()
- @doc_id = ObjectId().toString()
- @project_id = ObjectId().toString()
- @PackManager.MAX_COUNT = 512
- 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 =
- save: sinon.stub().callsArg(1)
- 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.save.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.save.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 that expires", ->
- 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() - 6 * 3600 * 1000}
- 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 a recent previous update in mongo that expires", ->
- beforeEach ->
- @PackManager.updateIndex = sinon.stub().callsArg(2)
- @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() - 6 * 3600 * 1000}
- expiresAt: new Date(Date.now())
- }
- @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback
- describe "for a small update that will not expire", ->
- it "should insert the update into mongo", ->
- @db.docHistory.save.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 not set any expiry time", ->
- @db.docHistory.save.neverCalledWithMatch(sinon.match.has("expiresAt")).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.save.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.save.calledWithMatch({
- expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)
- }).should.equal true
- it "should call the callback", ->
- @callback.called.should.equal true
- describe "getOpsByVersionRange", ->
- describe "loadPacksByVersionRange", ->
- describe "fetchPacksIfNeeded", ->
- describe "makeProjectIterator", ->
- describe "getPackById", ->
- describe "increaseTTL", ->
- describe "getIndex", ->
- describe "getPackFromIndex", ->
- # getLastPackFromIndex:
- # getIndexWithKeys
- # initialiseIndex
- # updateIndex
- # findCompletedPacks
- # findUnindexedPacks
- # insertPacksIntoIndexWithLock
- # _insertPacksIntoIndex
- # archivePack
- # checkArchivedPack
- # processOldPack
- # updateIndexIfNeeded
- # findUnarchivedPacks
- describe "checkArchiveNotInProgress", ->
- describe "when an archive is in progress", ->
- beforeEach ->
- @db.docHistoryIndex =
- findOne: sinon.stub().callsArgWith(2, null, {inS3:false})
- @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback
- it "should call the callback", ->
- @callback.called.should.equal true
- it "should return an error", ->
- @callback.calledWith(sinon.match.has('message')).should.equal true
- describe "when an archive is completed", ->
- beforeEach ->
- @db.docHistoryIndex =
- findOne: sinon.stub().callsArgWith(2, null, {inS3:true})
- @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback
- it "should call the callback", ->
- @callback.called.should.equal true
- it "should return an error", ->
- @callback.calledWith(sinon.match.has('message')).should.equal true
- describe "when the archive has not started or completed", ->
- beforeEach ->
- @db.docHistoryIndex =
- findOne: sinon.stub().callsArgWith(2, null, {})
- @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback
- it "should call the callback with no error", ->
- @callback.called.should.equal true
- it "should return with no error", ->
- (typeof @callback.lastCall.args[0]).should.equal 'undefined'
- # describe "setTTLOnArchivedPack", ->
- # beforeEach ->
- # @pack_id = "somepackid"
- # @onedayinms = 86400000
- # @db.docHistory =
- # findAndModify : sinon.stub().callsArgWith(1)
- # it "should set expires to 1 day", (done)->
- # #@PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms)
- # @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, =>
- # args = @db.docHistory.findAndModify.args[0][0]
- # args.query._id.should.equal @pack_id
- # args.update['$set'].expiresAt.should.equal @onedayinms
- # done()
- # describe "_getOneDayInFutureWithRandomDelay", ->
- # beforeEach ->
- # @onedayinms = 86400000
- # @thirtyMins = 1000 * 60 * 30
- # it "should give 1 day + 30 mins random time", (done)->
- # loops = 10000
- # while --loops > 0
- # randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms)
- # randomDelay.should.be.above(0)
- # randomDelay.should.be.below(@thirtyMins + 1)
- # done()
|