diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 1077199c2c..3bcf50af2a 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -122,7 +122,7 @@ module.exports = MongoManager = db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } # For finding all packs that affect a project (use a sparse index so only packs are included) db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}, { background: true, sparse: true } - # For finding updates that dont yet have a project_id and need it inserting + # For finding updates that don't yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee new file mode 100644 index 0000000000..c76afab988 --- /dev/null +++ b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee @@ -0,0 +1,44 @@ +chai = require('chai') +sinon = require("sinon") +should = chai.should() +modulePath = "../../../../app/js/DocArchiveManager.js" +SandboxedModule = require('sandboxed-module') +ObjectId = require("mongojs").ObjectId + +describe "DocArchiveManager", -> + beforeEach -> + @DocArchiveManager = SandboxedModule.require modulePath, requires: + "./MongoManager" : @MongoManager = sinon.stub() + "./MongoAWS" : @MongoAWS = sinon.stub() + "./LockManager" : @LockManager = sinon.stub() + "./DocstoreHandler" : @DocstoreHandler = sinon.stub() + "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} + "settings-sharelatex": @settings = + filestore: + backend: 's3' + + @mongoDocs = [{ + _id: ObjectId() + }, { + _id: ObjectId() + }, { + _id: ObjectId() + }] + + @project_id = "project-id-123" + @doc_id = "doc-id-123" + @callback = sinon.stub() + + describe "archiveAllDocsChanges", -> + + it "should archive all project docs change", (done)-> + + @DocstoreHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @mongoDocs) + @DocArchiveManager.archiveDocChangesWithLock = sinon.stub().callsArgWith(2, null) + + @DocArchiveManager.archiveAllDocsChanges @project_id, (err)=> + @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[0]._id).should.equal true + @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[1]._id).should.equal true + @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[2]._id).should.equal true + should.not.exist err + done() diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee new file mode 100644 index 0000000000..279f2a979b --- /dev/null +++ b/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee @@ -0,0 +1,55 @@ +chai = require('chai') +chai.should() +sinon = require("sinon") +modulePath = "../../../../app/js/DocstoreHandler.js" +SandboxedModule = require('sandboxed-module') + +describe "DocstoreHandler", -> + beforeEach -> + @requestDefaults = sinon.stub().returns(@request = sinon.stub()) + @DocstoreHandler = SandboxedModule.require modulePath, requires: + "request" : defaults: @requestDefaults + "settings-sharelatex": @settings = + apis: + docstore: + url: "docstore.sharelatex.com" + "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} + + @requestDefaults.calledWith(jar: false).should.equal true + + @project_id = "project-id-123" + @doc_id = "doc-id-123" + @callback = sinon.stub() + + describe "getAllDocs", -> + describe "with a successful response code", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, statusCode: 204, @docs = [{ _id: "mock-doc-id" }]) + @DocstoreHandler.getAllDocs @project_id, @callback + + it "should get all the project docs in the docstore api", -> + @request.get + .calledWith({ + url: "#{@settings.apis.docstore.url}/project/#{@project_id}/doc" + json: true + }) + .should.equal true + + it "should call the callback with the docs", -> + @callback.calledWith(null, @docs).should.equal true + + describe "with a failed response code", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, statusCode: 500, "") + @DocstoreHandler.getAllDocs @project_id, @callback + + it "should call the callback with an error", -> + @callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true + + it "should log the error", -> + @logger.error + .calledWith({ + err: new Error("docstore api responded with a non-success code: 500") + project_id: @project_id + }, "error getting all docs from docstore") + .should.equal true \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee new file mode 100644 index 0000000000..88c9f4b7da --- /dev/null +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -0,0 +1,78 @@ +chai = require('chai') +chai.should() +sinon = require("sinon") +modulePath = "../../../../app/js/MongoAWS.js" +SandboxedModule = require('sandboxed-module') +{ObjectId} = require("mongojs") + +describe "MongoAWS", -> + beforeEach -> + @MongoAWS = SandboxedModule.require modulePath, requires: + "settings-sharelatex": @settings = + filestore: + s3: + secret: "s3-secret" + key: "s3-key" + stores: + user_files: "s3-bucket" + "child_process": @child_process = {} + "mongo-uri": @mongouri = {} + "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} + "aws-sdk": @awssdk = {} + "fs": @fs = {} + "s3-streams": @s3streams = {} + "./mongojs" : { db: @db = {}, ObjectId: ObjectId } + "JSONStream": @JSONStream = {} + "readline-stream": @readline = sinon.stub() + + @bulkLimit = @MongoAWS.bulkLimit + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @callback = sinon.stub() + + describe "archiveDocHistory", -> + + beforeEach -> + @awssdk.config = { update: sinon.stub() } + @awssdk.S3 = sinon.stub() + @s3streams.WriteStream = sinon.stub() + @db.docHistory = {} + @db.docHistory.pipe = sinon.stub() + @db.docHistory.find = sinon.stub().returns @db.docHistory + @db.docHistory.pipe.returns + pipe:-> + on: (type, cb)-> + if type == "finish" + cb() + @JSONStream.stringify = sinon.stub() + + @MongoAWS.archiveDocHistory @project_id, @doc_id, @callback + + it "should call the callback", -> + @callback.calledWith(null).should.equal true + + describe "unArchiveDocHistory", -> + + beforeEach -> + @awssdk.config = { update: sinon.stub() } + @awssdk.S3 = sinon.stub() + @s3streams.ReadStream = sinon.stub() + + @s3streams.ReadStream.returns + #describe on 'open' behavior + on: (type, cb)-> + pipe:-> + #describe on 'data' behavior + on: (type, cb)-> + cb([]) + #describe on 'end' behavior + on: (type, cb)-> + cb() + #describe on 'error' behavior + on: sinon.stub() + + @MongoAWS.handleBulk = sinon.stub() + @MongoAWS.unArchiveDocHistory @project_id, @doc_id, @callback + + it "should call handleBulk", -> + @MongoAWS.handleBulk.calledWith([],@callback).should.equal true diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 7c559c0937..e9533664e3 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -13,6 +13,7 @@ describe "HttpController", -> "./DiffManager": @DiffManager = {} "./RestoreManager": @RestoreManager = {} "./PackManager": @PackManager = {} + "./DocArchiveManager": @DocArchiveManager = {} @doc_id = "doc-id-123" @project_id = "project-id-123" @next = sinon.stub() @@ -130,3 +131,39 @@ describe "HttpController", -> it "should return a success code", -> @res.send.calledWith(204).should.equal true + + describe "archiveProject", -> + beforeEach -> + @req = + params: + project_id: @project_id + @res = + send: sinon.stub() + @DocArchiveManager.archiveAllDocsChanges = sinon.stub().callsArg(1) + @HttpController.archiveProject @req, @res, @next + + it "should process archive doc changes", -> + @DocArchiveManager.archiveAllDocsChanges + .calledWith(@project_id) + .should.equal true + + it "should return a success code", -> + @res.send.calledWith(204).should.equal true + + describe "unArchiveProject", -> + beforeEach -> + @req = + params: + project_id: @project_id + @res = + send: sinon.stub() + @DocArchiveManager.unArchiveAllDocsChanges = sinon.stub().callsArg(1) + @HttpController.unArchiveProject @req, @res, @next + + it "should process unarchive doc changes", -> + @DocArchiveManager.unArchiveAllDocsChanges + .calledWith(@project_id) + .should.equal true + + it "should return a success code", -> + @res.send.calledWith(204).should.equal true diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 24588de6ea..8db054b40f 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -398,3 +398,89 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true + + describe "getDocChangesCount", -> + beforeEach -> + @db.docHistory = + count: sinon.stub().callsArg(2) + @MongoManager.getDocChangesCount @doc_id, @callback + + it "should return if there is any doc changes", -> + @db.docHistory.count + .calledWith({ + doc_id: ObjectId(@doc_id) + inS3 : { $exists : false } + }, { + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "getArchivedDocChanges", -> + beforeEach -> + @db.docHistory = + count: sinon.stub().callsArg(2) + @MongoManager.getArchivedDocChanges @doc_id, @callback + + it "should return if there is any archived doc changes", -> + @db.docHistory.count + .calledWith({ + doc_id: ObjectId(@doc_id) + inS3 : true + }, { + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "markDocHistoryAsArchived", -> + beforeEach -> + @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} + @db.docHistory = + update: sinon.stub().callsArg(2) + remove: sinon.stub().callsArg(1) + @MongoManager.markDocHistoryAsArchived @doc_id, @update, @callback + + it "should update last doc change with inS3 flag", -> + @db.docHistory.update + .calledWith({ + _id: ObjectId(@update._id) + },{ + $set : { inS3 : true } + }) + .should.equal true + + it "should remove any other doc changes before last update", -> + @db.docHistory.remove + .calledWith({ + doc_id: ObjectId(@doc_id) + inS3 : { $exists : false } + v: { $lt : @update.v } + expiresAt: {$exists : false} + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "markDocHistoryAsUnarchived", -> + beforeEach -> + @db.docHistory = + update: sinon.stub().callsArg(3) + @MongoManager.markDocHistoryAsUnarchived @doc_id, @callback + + it "should remove any doc changes inS3 flag", -> + @db.docHistory.update + .calledWith({ + doc_id: ObjectId(@doc_id) + },{ + $unset : { inS3 : true } + },{ + multi: true + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true \ No newline at end of file