| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- assert = require("chai").assert
- sinon = require('sinon')
- chai = require('chai')
- should = chai.should()
- expect = chai.expect
- modulePath = "../../../app/js/DocArchiveManager.js"
- SandboxedModule = require('sandboxed-module')
- ObjectId = require("mongojs").ObjectId
- Errors = require "../../../app/js/Errors"
- crypto = require("crypto")
- describe "DocArchiveManager", ->
- beforeEach ->
- @settings =
- docstore:
- s3:
- secret: "secret"
- key: "this_key"
- bucket:"doc-archive-unit-test"
- @request =
- put: {}
- get: {}
- del: {}
- @archivedDocs = [{
- _id: ObjectId()
- inS3:true
- rev: 2
- }, {
- _id: ObjectId()
- inS3:true
- rev: 4
- }, {
- _id: ObjectId()
- inS3:true
- rev: 6
- }]
- @mongoDocs = [{
- _id: ObjectId()
- lines: ["one", "two", "three"]
- rev: 2
- }, {
- _id: ObjectId()
- lines: ["aaa", "bbb", "ccc"]
- rev: 4
- }, {
- _id: ObjectId()
- inS3: true
- rev: 6
- }, {
- _id: ObjectId()
- inS3: true
- rev: 6
- }, {
- _id: ObjectId()
- lines: ["111", "222", "333"]
- rev: 6
- }]
- @unarchivedDocs = [{
- _id: ObjectId()
- lines: ["wombat", "potato", "banana"]
- rev: 2
- }, {
- _id: ObjectId()
- lines: ["llama", "turnip", "apple"]
- rev: 4
- }, {
- _id: ObjectId()
- lines: ["elephant", "swede", "nectarine"]
- rev: 6
- }]
- @mixedDocs = @archivedDocs.concat(@unarchivedDocs)
- @MongoManager =
- markDocAsArchived: sinon.stub().callsArgWith(2, null)
- upsertIntoDocCollection: sinon.stub().callsArgWith(3, null)
- getProjectsDocs: sinon.stub().callsArgWith(3, null, @mongoDocs)
- getArchivedProjectDocs: sinon.stub().callsArgWith(2, null, @mongoDocs)
- @requires =
- "settings-sharelatex": @settings
- "./MongoManager": @MongoManager
- "request": @request
- "./RangeManager": @RangeManager = {}
- "logger-sharelatex":
- log:->
- err:->
- @globals =
- JSON: JSON
- @error = "my errror"
- @project_id = ObjectId().toString()
- @stubbedError = new Errors.NotFoundError("Error in S3 request")
- @DocArchiveManager = SandboxedModule.require modulePath, requires: @requires, globals: @globals
- describe "archiveDoc", ->
- it "should use correct options", (done)->
- @request.put = sinon.stub().callsArgWith(1, null, {statusCode:200,headers:{etag:""}})
- @DocArchiveManager.archiveDoc @project_id, @mongoDocs[0], (err)=>
- opts = @request.put.args[0][0]
- assert.deepEqual(opts.aws, {key:@settings.docstore.s3.key, secret:@settings.docstore.s3.secret, bucket:@settings.docstore.s3.bucket})
- opts.body.should.equal JSON.stringify(
- lines: @mongoDocs[0].lines
- ranges: @mongoDocs[0].ranges
- schema_v: 1
- )
- opts.timeout.should.equal (30*1000)
- opts.uri.should.equal "https://#{@settings.docstore.s3.bucket}.s3.amazonaws.com/#{@project_id}/#{@mongoDocs[0]._id}"
- done()
- it "should return no md5 error", (done)->
- data = JSON.stringify(
- lines: @mongoDocs[0].lines
- ranges: @mongoDocs[0].ranges
- schema_v: 1
- )
- @md5 = crypto.createHash("md5").update(data).digest("hex")
- @request.put = sinon.stub().callsArgWith(1, null, {statusCode:200,headers:{etag:@md5}})
- @DocArchiveManager.archiveDoc @project_id, @mongoDocs[0], (err)=>
- should.not.exist err
- done()
- it "should return the error", (done)->
- @request.put = sinon.stub().callsArgWith(1, @stubbedError, {statusCode:400,headers:{etag:""}})
- @DocArchiveManager.archiveDoc @project_id, @mongoDocs[0], (err)=>
- should.exist err
- done()
- describe "unarchiveDoc", ->
- it "should use correct options", (done)->
- @request.get = sinon.stub().callsArgWith(1, null, statusCode:200, @mongoDocs[0].lines)
- @request.del = sinon.stub().callsArgWith(1, null, statusCode:204, {})
- @DocArchiveManager.unarchiveDoc @project_id, @mongoDocs[0]._id, (err)=>
- opts = @request.get.args[0][0]
- assert.deepEqual(opts.aws, {key:@settings.docstore.s3.key, secret:@settings.docstore.s3.secret, bucket:@settings.docstore.s3.bucket})
- opts.json.should.equal true
- opts.timeout.should.equal (30*1000)
- opts.uri.should.equal "https://#{@settings.docstore.s3.bucket}.s3.amazonaws.com/#{@project_id}/#{@mongoDocs[0]._id}"
- done()
- it "should return the error", (done)->
- @request.get = sinon.stub().callsArgWith(1, @stubbedError, {}, {})
- @DocArchiveManager.unarchiveDoc @project_id, @mongoDocs[0], (err)=>
- should.exist err
- done()
- it "should error if the doc lines are a string not an array", (done)->
- @request.get = sinon.stub().callsArgWith(1, null, statusCode:200, "this is a string")
- @request.del = sinon.stub()
- @DocArchiveManager.unarchiveDoc @project_id, @mongoDocs[0], (err)=>
- should.exist err
- @request.del.called.should.equal false
- done()
- describe "archiveAllDocs", ->
- it "should archive all project docs which are not in s3", (done)->
- @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, @mongoDocs)
- @DocArchiveManager.archiveDoc = sinon.stub().callsArgWith(2, null)
- @DocArchiveManager.archiveAllDocs @project_id, (err)=>
- @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[0]).should.equal true
- @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[1]).should.equal true
- @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[4]).should.equal true
- @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[2]).should.equal false
- @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[3]).should.equal false
-
- should.not.exist err
- done()
- it "should return error if have no docs", (done)->
- @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, null)
- @DocArchiveManager.archiveAllDocs @project_id, (err)=>
- should.exist err
- done()
- it "should return the error", (done)->
- @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, @error, null)
- @DocArchiveManager.archiveAllDocs @project_id, (err)=>
- err.should.equal @error
- done()
- describe "when most have been already put in s3", ->
- beforeEach ->
- numberOfDocs = 10 * 1000
- @mongoDocs = []
- while --numberOfDocs != 0
- @mongoDocs.push({inS3:true, _id: ObjectId()})
- @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, @mongoDocs)
- @DocArchiveManager.archiveDoc = sinon.stub().callsArgWith(2, null)
- it "should not throw and error", (done)->
- @DocArchiveManager.archiveAllDocs @project_id, (err)=>
- should.not.exist err
- done()
- describe "unArchiveAllDocs", ->
- it "should unarchive all inS3 docs", (done)->
- @MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, null, @archivedDocs)
- @DocArchiveManager.unarchiveDoc = sinon.stub().callsArgWith(2, null)
- @DocArchiveManager.unArchiveAllDocs @project_id, (err)=>
- for doc in @archivedDocs
- @DocArchiveManager.unarchiveDoc.calledWith(@project_id, doc._id).should.equal true
- should.not.exist err
- done()
- it "should return error if have no docs", (done)->
- @MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, null, null)
- @DocArchiveManager.unArchiveAllDocs @project_id, (err)=>
- should.exist err
- done()
- it "should return the error", (done)->
- @MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, @error, null)
- @DocArchiveManager.unArchiveAllDocs @project_id, (err)=>
- err.should.equal @error
- done()
- describe "destroyAllDocs", ->
- beforeEach ->
- @request.del = sinon.stub().callsArgWith(1, null, statusCode:204, {})
- @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, @mixedDocs)
- @MongoManager.findDoc = sinon.stub().callsArgWith(3, null, null)
- @MongoManager.destroyDoc = sinon.stub().yields()
- for doc in @mixedDocs
- @MongoManager.findDoc.withArgs(@project_id, doc._id).callsArgWith(3, null, doc)
- it "should destroy all the docs", (done)->
- @DocArchiveManager.destroyDoc = sinon.stub().callsArgWith(2, null)
- @DocArchiveManager.destroyAllDocs @project_id, (err)=>
- for doc in @mixedDocs
- @DocArchiveManager.destroyDoc.calledWith(@project_id, doc._id).should.equal true
- should.not.exist err
- done()
- it "should only the s3 docs from s3", (done)->
- docOpts = (doc) =>
- JSON.parse(JSON.stringify({
- aws: {key:@settings.docstore.s3.key, secret:@settings.docstore.s3.secret, bucket:@settings.docstore.s3.bucket},
- json: true,
- timeout: 30 * 1000
- uri:"https://#{@settings.docstore.s3.bucket}.s3.amazonaws.com/#{@project_id}/#{doc._id}"
- }))
- @DocArchiveManager.destroyAllDocs @project_id, (err)=>
- expect(err).not.to.exist
- for doc in @archivedDocs
- sinon.assert.calledWith(@request.del, docOpts(doc))
- for doc in @unarchivedDocs
- expect(@request.del.calledWith(docOpts(doc))).to.equal false # no notCalledWith
- done()
- it "should remove the docs from mongo", (done)->
- @DocArchiveManager.destroyAllDocs @project_id, (err)=>
- expect(err).not.to.exist
- for doc in @mixedDocs
- sinon.assert.calledWith(@MongoManager.destroyDoc, doc._id)
- done()
-
- describe "_s3DocToMongoDoc", ->
- describe "with the old schema", ->
- it "should return the docs lines", (done) ->
- @DocArchiveManager._s3DocToMongoDoc ["doc", "lines"], (error, doc) ->
- expect(doc).to.deep.equal {
- lines: ["doc", "lines"]
- }
- done()
-
- describe "with the new schema", ->
- it "should return the doc lines and ranges", (done) ->
- @RangeManager.jsonRangesToMongo = sinon.stub().returns {"mongo": "ranges"}
- @DocArchiveManager._s3DocToMongoDoc {
- lines: ["doc", "lines"]
- ranges: {"json": "ranges"}
- schema_v: 1
- }, (error, doc) ->
- expect(doc).to.deep.equal {
- lines: ["doc", "lines"]
- ranges: {"mongo": "ranges"}
- }
- done()
-
- it "should return just the doc lines when there are no ranges", (done) ->
- @DocArchiveManager._s3DocToMongoDoc {
- lines: ["doc", "lines"]
- schema_v: 1
- }, (error, doc) ->
- expect(doc).to.deep.equal {
- lines: ["doc", "lines"]
- }
- done()
-
- describe "with an unrecognised schema", ->
- it "should return an error", (done) ->
- @DocArchiveManager._s3DocToMongoDoc {
- schema_v: 2
- }, (error, doc) ->
- expect(error).to.exist
- done()
-
- describe "_mongoDocToS3Doc", ->
- describe "with a valid doc", ->
- it "should return the json version", (done) ->
- @DocArchiveManager._mongoDocToS3Doc doc = {
- lines: ["doc", "lines"]
- ranges: { "mock": "ranges" }
- }, (err, s3_doc) ->
- expect(s3_doc).to.equal JSON.stringify({
- lines: ["doc", "lines"]
- ranges: { "mock": "ranges" }
- schema_v: 1
- })
- done()
-
- describe "with null bytes in the result", ->
- beforeEach ->
- @_stringify = JSON.stringify
- JSON.stringify = sinon.stub().returns '{"bad": "\u0000"}'
-
- afterEach ->
- JSON.stringify = @_stringify
-
- it "should return an error", (done) ->
- @DocArchiveManager._mongoDocToS3Doc {
- lines: ["doc", "lines"]
- ranges: { "mock": "ranges" }
- }, (err, s3_doc) ->
- expect(err).to.exist
- done()
-
- describe "without doc lines", ->
- it "should return an error", (done) ->
- @DocArchiveManager._mongoDocToS3Doc {}, (err, s3_doc) ->
- expect(err).to.exist
- done()
-
-
|