2020-02-16 09:02:49 -05:00
|
|
|
/* eslint-disable
|
|
|
|
no-dupe-keys,
|
|
|
|
no-return-assign,
|
|
|
|
no-unused-vars,
|
|
|
|
*/
|
|
|
|
// TODO: This file was created by bulk-decaffeinate.
|
|
|
|
// Fix any style issues and re-enable lint.
|
2020-02-16 09:02:48 -05:00
|
|
|
/*
|
|
|
|
* decaffeinate suggestions:
|
|
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
|
|
*/
|
2020-02-16 09:02:55 -05:00
|
|
|
const SandboxedModule = require('sandboxed-module')
|
|
|
|
const sinon = require('sinon')
|
2021-03-12 17:16:01 -05:00
|
|
|
const { assert, expect } = require('chai')
|
2020-02-16 09:02:55 -05:00
|
|
|
const modulePath = require('path').join(__dirname, '../../../app/js/DocManager')
|
2020-08-21 12:50:01 -04:00
|
|
|
const { ObjectId } = require('mongodb')
|
2020-02-16 09:02:55 -05:00
|
|
|
const Errors = require('../../../app/js/Errors')
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('DocManager', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager = SandboxedModule.require(modulePath, {
|
|
|
|
requires: {
|
|
|
|
'./MongoManager': (this.MongoManager = {}),
|
|
|
|
'./DocArchiveManager': (this.DocArchiveManager = {}),
|
|
|
|
'./RangeManager': (this.RangeManager = {
|
|
|
|
jsonRangesToMongo(r) {
|
|
|
|
return r
|
|
|
|
},
|
2021-07-13 07:04:48 -04:00
|
|
|
shouldUpdateRanges: sinon.stub().returns(false),
|
2020-02-16 09:02:55 -05:00
|
|
|
}),
|
2021-07-12 12:47:20 -04:00
|
|
|
'@overleaf/settings': (this.settings = { docstore: {} }),
|
2021-07-13 07:04:48 -04:00
|
|
|
'./Errors': Errors,
|
|
|
|
},
|
2020-02-16 09:02:55 -05:00
|
|
|
})
|
|
|
|
this.doc_id = ObjectId().toString()
|
|
|
|
this.project_id = ObjectId().toString()
|
|
|
|
this.another_project_id = ObjectId().toString()
|
|
|
|
this.callback = sinon.stub()
|
|
|
|
return (this.stubbedError = new Error('blew up'))
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('getFullDoc', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub()
|
|
|
|
return (this.doc = {
|
|
|
|
_id: this.doc_id,
|
2021-07-13 07:04:48 -04:00
|
|
|
lines: ['2134'],
|
2020-02-16 09:02:55 -05:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should call get doc with a quick filter', function (done) {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc.callsArgWith(3, null, this.doc)
|
|
|
|
return this.DocManager.getFullDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
(err, doc) => {
|
2021-10-27 05:49:18 -04:00
|
|
|
if (err) return done(err)
|
2020-02-16 09:02:55 -05:00
|
|
|
doc.should.equal(this.doc)
|
|
|
|
this.DocManager._getDoc
|
|
|
|
.calledWith(this.project_id, this.doc_id, {
|
|
|
|
lines: true,
|
|
|
|
rev: true,
|
|
|
|
deleted: true,
|
|
|
|
version: true,
|
|
|
|
ranges: true,
|
2021-07-13 07:04:48 -04:00
|
|
|
inS3: true,
|
2020-02-16 09:02:55 -05:00
|
|
|
})
|
|
|
|
.should.equal(true)
|
|
|
|
return done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return error when get doc errors', function (done) {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc.callsArgWith(3, 'error')
|
|
|
|
return this.DocManager.getFullDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
(err, exist) => {
|
|
|
|
err.should.equal('error')
|
|
|
|
return done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('getRawDoc', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub()
|
|
|
|
return (this.doc = { lines: ['2134'] })
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should call get doc with a quick filter', function (done) {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc.callsArgWith(3, null, this.doc)
|
|
|
|
return this.DocManager.getDocLines(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
(err, doc) => {
|
2021-10-27 05:49:18 -04:00
|
|
|
if (err) return done(err)
|
2020-02-16 09:02:55 -05:00
|
|
|
doc.should.equal(this.doc)
|
|
|
|
this.DocManager._getDoc
|
|
|
|
.calledWith(this.project_id, this.doc_id, {
|
|
|
|
lines: true,
|
2021-07-13 07:04:48 -04:00
|
|
|
inS3: true,
|
2020-02-16 09:02:55 -05:00
|
|
|
})
|
|
|
|
.should.equal(true)
|
|
|
|
return done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return error when get doc errors', function (done) {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc.callsArgWith(3, 'error')
|
|
|
|
return this.DocManager.getDocLines(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
(err, exist) => {
|
|
|
|
err.should.equal('error')
|
|
|
|
return done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('getDoc', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.project = { name: 'mock-project' }
|
|
|
|
this.doc = {
|
|
|
|
_id: this.doc_id,
|
|
|
|
project_id: this.project_id,
|
2021-07-13 07:04:48 -04:00
|
|
|
lines: ['mock-lines'],
|
2020-02-16 09:02:55 -05:00
|
|
|
}
|
|
|
|
this.version = 42
|
|
|
|
this.MongoManager.findDoc = sinon.stub()
|
|
|
|
return (this.MongoManager.getDocVersion = sinon
|
|
|
|
.stub()
|
|
|
|
.yields(null, this.version))
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when using a filter', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.findDoc.yields(null, this.doc)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should error if inS3 is not set to true', function (done) {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{ inS3: false },
|
2021-07-13 07:04:48 -04:00
|
|
|
err => {
|
2020-02-16 09:02:55 -05:00
|
|
|
expect(err).to.exist
|
|
|
|
return done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should always get inS3 even when no filter is passed', function (done) {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
undefined,
|
2021-07-13 07:04:48 -04:00
|
|
|
err => {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.MongoManager.findDoc.called.should.equal(false)
|
|
|
|
expect(err).to.exist
|
|
|
|
return done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should not error if inS3 is set to true', function (done) {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{ inS3: true },
|
2021-07-13 07:04:48 -04:00
|
|
|
err => {
|
2020-02-16 09:02:55 -05:00
|
|
|
expect(err).to.not.exist
|
|
|
|
return done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the doc is in the doc collection', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.MongoManager.findDoc.yields(null, this.doc)
|
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{ version: true, inS3: true },
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should get the doc from the doc collection', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.findDoc
|
|
|
|
.calledWith(this.project_id, this.doc_id)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should get the doc version from the docOps collection', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.getDocVersion
|
|
|
|
.calledWith(this.doc_id)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the doc with the version', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.callback.called.should.equal(true)
|
|
|
|
const doc = this.callback.args[0][1]
|
|
|
|
doc.lines.should.equal(this.doc.lines)
|
|
|
|
return doc.version.should.equal(this.version)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('without the version filter', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.MongoManager.findDoc.yields(null, this.doc)
|
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{ version: false, inS3: true },
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should not get the doc version from the docOps collection', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.getDocVersion.called.should.equal(false)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when MongoManager.findDoc errors', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.MongoManager.findDoc.yields(this.stubbedError)
|
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{ version: true, inS3: true },
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the error', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback.calledWith(this.stubbedError).should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the doc is archived', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.doc = {
|
|
|
|
_id: this.doc_id,
|
|
|
|
project_id: this.project_id,
|
|
|
|
lines: ['mock-lines'],
|
2021-07-13 07:04:48 -04:00
|
|
|
inS3: true,
|
2020-02-16 09:02:55 -05:00
|
|
|
}
|
|
|
|
this.MongoManager.findDoc.yields(null, this.doc)
|
2023-03-16 07:58:12 -04:00
|
|
|
this.DocArchiveManager.unarchiveDoc = (projectId, docId, callback) => {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.doc.inS3 = false
|
|
|
|
return callback()
|
|
|
|
}
|
|
|
|
sinon.spy(this.DocArchiveManager, 'unarchiveDoc')
|
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{ version: true, inS3: true },
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should call the DocArchive to unarchive the doc', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocArchiveManager.unarchiveDoc
|
|
|
|
.calledWith(this.project_id, this.doc_id)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should look up the doc twice', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.findDoc.calledTwice.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the doc', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback.calledWith(null, this.doc).should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return describe('when the doc does not exist in the docs collection', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.MongoManager.findDoc = sinon.stub().yields(null, null)
|
|
|
|
return this.DocManager._getDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{ version: true, inS3: true },
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return a NotFoundError', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(
|
|
|
|
sinon.match.has(
|
|
|
|
'message',
|
|
|
|
`No such doc: ${this.doc_id} in project ${this.project_id}`
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('getAllNonDeletedDocs', function () {
|
|
|
|
describe('when the project exists', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.docs = [
|
|
|
|
{
|
|
|
|
_id: this.doc_id,
|
|
|
|
project_id: this.project_id,
|
2021-07-13 07:04:48 -04:00
|
|
|
lines: ['mock-lines'],
|
|
|
|
},
|
2020-02-16 09:02:55 -05:00
|
|
|
]
|
|
|
|
this.MongoManager.getProjectsDocs = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(3, null, this.docs)
|
|
|
|
this.DocArchiveManager.unArchiveAllDocs = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(1, null, this.docs)
|
|
|
|
this.filter = { lines: true }
|
|
|
|
return this.DocManager.getAllNonDeletedDocs(
|
|
|
|
this.project_id,
|
|
|
|
this.filter,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should get the project from the database', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.getProjectsDocs
|
|
|
|
.calledWith(this.project_id, { include_deleted: false }, this.filter)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the docs', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback.calledWith(null, this.docs).should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return describe('when there are no docs for the project', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.MongoManager.getProjectsDocs = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(3, null, null)
|
|
|
|
this.DocArchiveManager.unArchiveAllDocs = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(1, null)
|
|
|
|
return this.DocManager.getAllNonDeletedDocs(
|
|
|
|
this.project_id,
|
|
|
|
this.filter,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return a NotFoundError', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(
|
|
|
|
sinon.match.has('message', `No docs for project ${this.project_id}`)
|
|
|
|
)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2021-02-15 08:13:48 -05:00
|
|
|
describe('patchDoc', function () {
|
|
|
|
describe('when the doc exists', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
this.lines = ['mock', 'doc', 'lines']
|
|
|
|
this.rev = 77
|
|
|
|
this.MongoManager.findDoc = sinon
|
|
|
|
.stub()
|
|
|
|
.yields(null, { _id: ObjectId(this.doc_id) })
|
|
|
|
this.MongoManager.patchDoc = sinon.stub().yields(null)
|
2022-05-17 07:49:13 -04:00
|
|
|
this.DocArchiveManager.archiveDoc = sinon.stub().yields(null)
|
2021-02-15 08:13:48 -05:00
|
|
|
this.meta = {}
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('standard path', function () {
|
|
|
|
beforeEach(function (done) {
|
|
|
|
this.callback = sinon.stub().callsFake(done)
|
|
|
|
this.DocManager.patchDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.meta,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should get the doc', function () {
|
|
|
|
expect(this.MongoManager.findDoc).to.have.been.calledWith(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should persist the meta', function () {
|
|
|
|
expect(this.MongoManager.patchDoc).to.have.been.calledWith(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.meta
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should return the callback', function () {
|
|
|
|
expect(this.callback).to.have.been.calledWith(null)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('background flush disabled and deleting a doc', function () {
|
|
|
|
beforeEach(function (done) {
|
|
|
|
this.settings.docstore.archiveOnSoftDelete = false
|
|
|
|
this.meta.deleted = true
|
|
|
|
|
|
|
|
this.callback = sinon.stub().callsFake(done)
|
|
|
|
this.DocManager.patchDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.meta,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not flush the doc out of mongo', function () {
|
2022-05-17 07:49:13 -04:00
|
|
|
expect(this.DocArchiveManager.archiveDoc).to.not.have.been.called
|
2021-02-15 08:13:48 -05:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('background flush enabled and not deleting a doc', function () {
|
|
|
|
beforeEach(function (done) {
|
|
|
|
this.settings.docstore.archiveOnSoftDelete = false
|
|
|
|
this.meta.deleted = false
|
|
|
|
this.callback = sinon.stub().callsFake(done)
|
|
|
|
this.DocManager.patchDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.meta,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not flush the doc out of mongo', function () {
|
2022-05-17 07:49:13 -04:00
|
|
|
expect(this.DocArchiveManager.archiveDoc).to.not.have.been.called
|
2021-02-15 08:13:48 -05:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('background flush enabled and deleting a doc', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
this.settings.docstore.archiveOnSoftDelete = true
|
|
|
|
this.meta.deleted = true
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('when the background flush succeeds', function () {
|
|
|
|
beforeEach(function (done) {
|
2022-05-17 07:49:13 -04:00
|
|
|
this.DocArchiveManager.archiveDoc = sinon.stub().yields(null)
|
2021-02-15 08:13:48 -05:00
|
|
|
this.callback = sinon.stub().callsFake(done)
|
|
|
|
this.DocManager.patchDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.meta,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not log a warning', function () {
|
|
|
|
expect(this.logger.warn).to.not.have.been.called
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should flush the doc out of mongo', function () {
|
2022-05-17 07:49:13 -04:00
|
|
|
expect(this.DocArchiveManager.archiveDoc).to.have.been.calledWith(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id
|
|
|
|
)
|
2021-02-15 08:13:48 -05:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('when the background flush fails', function () {
|
|
|
|
beforeEach(function (done) {
|
|
|
|
this.err = new Error('foo')
|
2022-05-17 07:49:13 -04:00
|
|
|
this.DocArchiveManager.archiveDoc = sinon.stub().yields(this.err)
|
2021-02-15 08:13:48 -05:00
|
|
|
this.callback = sinon.stub().callsFake(done)
|
|
|
|
this.DocManager.patchDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.meta,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should log a warning', function () {
|
|
|
|
expect(this.logger.warn).to.have.been.calledWith(
|
|
|
|
sinon.match({
|
2023-03-16 07:58:12 -04:00
|
|
|
projectId: this.project_id,
|
|
|
|
docId: this.doc_id,
|
2021-07-13 07:04:48 -04:00
|
|
|
err: this.err,
|
2021-02-15 08:13:48 -05:00
|
|
|
}),
|
|
|
|
'archiving a single doc in the background failed'
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not fail the delete process', function () {
|
|
|
|
expect(this.callback).to.have.been.calledWith(null)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('when the doc does not exist', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
this.MongoManager.findDoc = sinon.stub().yields(null)
|
|
|
|
this.DocManager.patchDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
{},
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should return a NotFoundError', function () {
|
|
|
|
expect(this.callback).to.have.been.calledWith(
|
|
|
|
sinon.match.has(
|
|
|
|
'message',
|
|
|
|
`No such project/doc to delete: ${this.project_id}/${this.doc_id}`
|
|
|
|
)
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return describe('updateDoc', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.oldDocLines = ['old', 'doc', 'lines']
|
|
|
|
this.newDocLines = ['new', 'doc', 'lines']
|
|
|
|
this.originalRanges = {
|
|
|
|
changes: [
|
|
|
|
{
|
|
|
|
id: ObjectId().toString(),
|
|
|
|
op: { i: 'foo', p: 3 },
|
|
|
|
meta: {
|
|
|
|
user_id: ObjectId().toString(),
|
2021-07-13 07:04:48 -04:00
|
|
|
ts: new Date().toString(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
2020-02-16 09:02:55 -05:00
|
|
|
}
|
|
|
|
this.newRanges = {
|
|
|
|
changes: [
|
|
|
|
{
|
|
|
|
id: ObjectId().toString(),
|
|
|
|
op: { i: 'bar', p: 6 },
|
|
|
|
meta: {
|
|
|
|
user_id: ObjectId().toString(),
|
2021-07-13 07:04:48 -04:00
|
|
|
ts: new Date().toString(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
2020-02-16 09:02:55 -05:00
|
|
|
}
|
|
|
|
this.version = 42
|
|
|
|
this.doc = {
|
|
|
|
_id: this.doc_id,
|
|
|
|
project_id: this.project_id,
|
|
|
|
lines: this.oldDocLines,
|
|
|
|
rev: (this.rev = 5),
|
|
|
|
version: this.version,
|
2021-07-13 07:04:48 -04:00
|
|
|
ranges: this.originalRanges,
|
2020-02-16 09:02:55 -05:00
|
|
|
}
|
|
|
|
|
2023-03-22 09:08:01 -04:00
|
|
|
this.MongoManager.upsertIntoDocCollection = sinon.stub().yields()
|
2020-02-16 09:02:55 -05:00
|
|
|
this.MongoManager.setDocVersion = sinon.stub().yields()
|
|
|
|
return (this.DocManager._getDoc = sinon.stub())
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when only the doc lines have changed', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
|
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.newDocLines,
|
|
|
|
this.version,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should get the existing doc', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocManager._getDoc
|
|
|
|
.calledWith(this.project_id, this.doc_id, {
|
|
|
|
version: true,
|
|
|
|
rev: true,
|
|
|
|
lines: true,
|
|
|
|
version: true,
|
|
|
|
ranges: true,
|
2021-07-13 07:04:48 -04:00
|
|
|
inS3: true,
|
2020-02-16 09:02:55 -05:00
|
|
|
})
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should upsert the document to the doc collection', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.upsertIntoDocCollection
|
2023-03-22 09:08:01 -04:00
|
|
|
.calledWith(this.project_id, this.doc_id, this.rev, {
|
|
|
|
lines: this.newDocLines,
|
|
|
|
})
|
2020-02-16 09:02:55 -05:00
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should not update the version', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.setDocVersion.called.should.equal(false)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the new rev', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(null, true, this.rev + 1)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the doc ranges have changed', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
|
|
|
|
this.RangeManager.shouldUpdateRanges.returns(true)
|
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.oldDocLines,
|
|
|
|
this.version,
|
|
|
|
this.newRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should upsert the ranges', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.upsertIntoDocCollection
|
2023-03-22 09:08:01 -04:00
|
|
|
.calledWith(this.project_id, this.doc_id, this.rev, {
|
|
|
|
ranges: this.newRanges,
|
|
|
|
})
|
2020-02-16 09:02:55 -05:00
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should not update the version', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.setDocVersion.called.should.equal(false)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the new rev', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(null, true, this.rev + 1)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when only the version has changed', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
|
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.oldDocLines,
|
|
|
|
this.version + 1,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should not change the lines or ranges', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.upsertIntoDocCollection.called.should.equal(
|
|
|
|
false
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should update the version', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.setDocVersion
|
|
|
|
.calledWith(this.doc_id, this.version + 1)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the old rev', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback.calledWith(null, true, this.rev).should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the doc has not changed at all', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
|
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.oldDocLines,
|
|
|
|
this.version,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should not update the ranges or lines', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.upsertIntoDocCollection.called.should.equal(
|
|
|
|
false
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should not update the version', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.setDocVersion.called.should.equal(false)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the old rev and modified == false', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(null, false, this.rev)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the version is null', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.newDocLines,
|
|
|
|
null,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return an error', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(
|
|
|
|
sinon.match.has('message', 'no lines, version or ranges provided')
|
|
|
|
)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the lines are null', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
null,
|
|
|
|
this.version,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return an error', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(
|
|
|
|
sinon.match.has('message', 'no lines, version or ranges provided')
|
|
|
|
)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the ranges are null', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.newDocLines,
|
|
|
|
this.version,
|
|
|
|
null,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return an error', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(
|
|
|
|
sinon.match.has('message', 'no lines, version or ranges provided')
|
|
|
|
)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when there is a generic error getting the doc', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.error = new Error('doc could not be found')
|
|
|
|
this.DocManager._getDoc = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(3, this.error, null, null)
|
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.newDocLines,
|
|
|
|
this.version,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should not upsert the document to the doc collection', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.upsertIntoDocCollection.called.should.equal(
|
|
|
|
false
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the error', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback.calledWith(this.error).should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-03-21 09:18:16 -04:00
|
|
|
describe('when the version was decremented', function () {
|
|
|
|
beforeEach(function () {
|
|
|
|
this.DocManager._getDoc = sinon.stub().yields(null, this.doc)
|
|
|
|
this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.newDocLines,
|
|
|
|
this.version - 1,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should return an error', function () {
|
|
|
|
this.callback.should.have.been.calledWith(
|
|
|
|
sinon.match.instanceOf(Errors.DocVersionDecrementedError)
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
describe('when the doc lines have not changed', function () {
|
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
|
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.oldDocLines.slice(),
|
|
|
|
this.version,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should not update the doc', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.upsertIntoDocCollection.called.should.equal(
|
|
|
|
false
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the existing rev', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback
|
|
|
|
.calledWith(null, false, this.rev)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-03-22 09:08:01 -04:00
|
|
|
describe('when the doc does not exist', function () {
|
2020-05-28 09:20:54 -04:00
|
|
|
beforeEach(function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, null, null)
|
|
|
|
return this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.newDocLines,
|
|
|
|
this.version,
|
|
|
|
this.originalRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should upsert the document to the doc collection', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.upsertIntoDocCollection
|
2023-03-22 09:08:01 -04:00
|
|
|
.calledWith(this.project_id, this.doc_id, undefined, {
|
2020-02-16 09:02:55 -05:00
|
|
|
lines: this.newDocLines,
|
2021-07-13 07:04:48 -04:00
|
|
|
ranges: this.originalRanges,
|
2020-02-16 09:02:55 -05:00
|
|
|
})
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
it('should set the version', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.MongoManager.setDocVersion
|
|
|
|
.calledWith(this.doc_id, this.version)
|
|
|
|
.should.equal(true)
|
|
|
|
})
|
|
|
|
|
2020-05-28 09:20:54 -04:00
|
|
|
return it('should return the callback with the new rev', function () {
|
2020-02-16 09:02:55 -05:00
|
|
|
return this.callback.calledWith(null, true, 1).should.equal(true)
|
|
|
|
})
|
|
|
|
})
|
2023-03-22 09:08:01 -04:00
|
|
|
|
|
|
|
describe('when another update is racing', function () {
|
|
|
|
beforeEach(function (done) {
|
|
|
|
this.DocManager._getDoc = sinon.stub().yields(null, this.doc)
|
|
|
|
this.MongoManager.upsertIntoDocCollection
|
|
|
|
.onFirstCall()
|
|
|
|
.yields(new Errors.DocRevValueError())
|
|
|
|
this.MongoManager.upsertIntoDocCollection.onSecondCall().yields(null)
|
|
|
|
this.RangeManager.shouldUpdateRanges.returns(true)
|
|
|
|
this.callback.callsFake(done)
|
|
|
|
this.DocManager.updateDoc(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.newDocLines,
|
|
|
|
this.version + 1,
|
|
|
|
this.newRanges,
|
|
|
|
this.callback
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should upsert the doc twice', function () {
|
|
|
|
this.MongoManager.upsertIntoDocCollection.should.have.been.calledWith(
|
|
|
|
this.project_id,
|
|
|
|
this.doc_id,
|
|
|
|
this.rev,
|
|
|
|
{
|
|
|
|
ranges: this.newRanges,
|
|
|
|
lines: this.newDocLines,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
this.MongoManager.upsertIntoDocCollection.should.have.been.calledTwice
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should update the version once', function () {
|
|
|
|
this.MongoManager.setDocVersion.should.have.been.calledOnce
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should return the callback with the new rev', function () {
|
|
|
|
this.callback.should.have.been.calledWith(null, true, this.rev + 1)
|
|
|
|
})
|
|
|
|
})
|
2020-02-16 09:02:55 -05:00
|
|
|
})
|
|
|
|
})
|