overleaf/services/web/test/unit/src/Project/ProjectEntityMongoUpdateHandlerTests.js
Eric Mc Sween 2627595040 Merge pull request #2639 from overleaf/em-convert-doc-to-file
Endpoint for converting a doc to a file

GitOrigin-RevId: 0a3bd46a7a78537b0f64dc577402277cbe81fecb
2020-03-05 04:16:49 +00:00

1115 lines
31 KiB
JavaScript

const { expect } = require('chai')
const sinon = require('sinon')
const tk = require('timekeeper')
const Errors = require('../../../../app/src/Features/Errors/Errors')
const { ObjectId } = require('mongoose').Types
const SandboxedModule = require('sandboxed-module')
const { Project } = require('../helpers/models/Project')
const MODULE_PATH =
'../../../../app/src/Features/Project/ProjectEntityMongoUpdateHandler'
describe('ProjectEntityMongoUpdateHandler', function() {
beforeEach(function() {
this.doc = {
_id: ObjectId(),
name: 'test-doc.txt',
lines: ['hello', 'world'],
rev: 1234
}
this.docPath = {
mongo: 'rootFolder.0.docs.0',
fileSystem: '/test-doc.txt'
}
this.file = {
_id: ObjectId(),
name: 'something.jpg',
linkedFileData: { provider: 'url' },
hash: 'some-hash'
}
this.filePath = {
fileSystem: '/something.png',
mongo: 'rootFolder.0.fileRefs.0'
}
this.subfolder = { _id: ObjectId(), name: 'test-subfolder' }
this.subfolderPath = {
fileSystem: '/test-folder/test-subfolder',
mongo: 'rootFolder.0.folders.0.folders.0'
}
this.folder = {
_id: ObjectId(),
name: 'test-folder',
folders: [this.subfolder]
}
this.folderPath = {
fileSystem: '/test-folder',
mongo: 'rootFolder.0.folders.0'
}
this.rootFolder = {
_id: ObjectId(),
folders: [this.folder],
docs: [this.doc],
fileRefs: [this.file]
}
this.rootFolderPath = {
fileSystem: '/',
mongo: 'rootFolder.0'
}
this.project = {
_id: ObjectId(),
name: 'project name',
rootFolder: [this.rootFolder]
}
this.logger = {
log: sinon.stub(),
warn: sinon.stub(),
error: sinon.stub(),
err() {}
}
this.Settings = { maxEntitiesPerProject: 100 }
this.CooldownManager = {}
this.LockManager = {
promises: {
runWithLock: sinon.spy((namespace, id, runner) => runner())
}
}
this.FolderModel = sinon.stub()
this.ProjectMock = sinon.mock(Project)
this.ProjectEntityHandler = {
promises: {
getAllEntitiesFromProject: sinon.stub()
}
}
this.ProjectLocator = {
promises: {
findElement: sinon.stub().rejects(new Error('not found')),
findElementByPath: sinon.stub().rejects(new Error('not found'))
}
}
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.rootFolder._id,
type: 'folder'
})
.resolves({
element: this.rootFolder,
path: this.rootFolderPath
})
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.folder._id,
type: 'folder'
})
.resolves({
element: this.folder,
path: this.folderPath,
folder: this.rootFolder
})
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.subfolder._id,
type: 'folder'
})
.resolves({
element: this.subfolder,
path: this.subfolderPath,
folder: this.folder
})
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.file._id,
type: 'file'
})
.resolves({
element: this.file,
path: this.filePath,
folder: this.rootFolder
})
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.doc._id,
type: 'doc'
})
.resolves({
element: this.doc,
path: this.docPath,
folder: this.rootFolder
})
this.ProjectLocator.promises.findElementByPath
.withArgs(
sinon.match({
project: this.project,
path: '/'
})
)
.resolves({ element: this.rootFolder, type: 'folder' })
this.ProjectLocator.promises.findElementByPath
.withArgs(
sinon.match({
project: this.project,
path: '/test-folder'
})
)
.resolves({ element: this.folder, type: 'folder' })
this.ProjectLocator.promises.findElementByPath
.withArgs(
sinon.match({
project: this.project,
path: '/test-folder/test-subfolder'
})
)
.resolves({ element: this.subfolder, type: 'folder' })
this.ProjectGetter = {
promises: {
getProjectWithoutLock: sinon
.stub()
.withArgs(this.project._id)
.resolves(this.project),
getProjectWithOnlyFolders: sinon.stub().resolves(this.project)
}
}
this.FolderStructureBuilder = {
buildFolderStructure: sinon.stub()
}
this.subject = SandboxedModule.require(MODULE_PATH, {
globals: {
console: console
},
requires: {
'logger-sharelatex': this.logger,
'settings-sharelatex': this.Settings,
'../Cooldown/CooldownManager': this.CooldownManager,
'../../models/Folder': { Folder: this.FolderModel },
'../../infrastructure/LockManager': this.LockManager,
'../../models/Project': { Project },
'./ProjectEntityHandler': this.ProjectEntityHandler,
'./ProjectLocator': this.ProjectLocator,
'./ProjectGetter': this.ProjectGetter,
'./FolderStructureBuilder': this.FolderStructureBuilder,
// We need to provide Errors here to make instance check work
'../Errors/Errors': Errors
}
})
})
afterEach(function() {
this.ProjectMock.restore()
})
beforeEach(function() {
tk.freeze(Date.now())
})
afterEach(function() {
tk.reset()
})
describe('addDoc', function() {
beforeEach(async function() {
const doc = { _id: ObjectId(), name: 'other.txt' }
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: { 'rootFolder.0.folders.0.docs': doc },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
this.result = await this.subject.promises.addDoc(
this.project._id,
this.folder._id,
doc
)
})
it('adds the document in Mongo', function() {
this.ProjectMock.verify()
})
it('returns path info and the project', function() {
expect(this.result).to.deep.equal({
result: {
path: {
mongo: 'rootFolder.0.folders.0',
fileSystem: '/test-folder/other.txt'
}
},
project: this.project
})
})
})
describe('addFile', function() {
beforeEach(function() {
this.newFile = { _id: ObjectId(), name: 'picture.jpg' }
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: { 'rootFolder.0.folders.0.fileRefs': this.newFile },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
})
describe('happy path', function() {
beforeEach(async function() {
this.result = await this.subject.promises.addFile(
this.project._id,
this.folder._id,
this.newFile
)
})
it('adds the file in Mongo', function() {
this.ProjectMock.verify()
})
it('returns path info and the project', function() {
expect(this.result).to.deep.equal({
result: {
path: {
mongo: 'rootFolder.0.folders.0',
fileSystem: '/test-folder/picture.jpg'
}
},
project: this.project
})
})
})
describe('when entity limit is reached', function() {
beforeEach(function() {
this.savedMaxEntities = this.Settings.maxEntitiesPerProject
this.Settings.maxEntitiesPerProject = 3
})
afterEach(function() {
this.Settings.maxEntitiesPerProject = this.savedMaxEntities
})
it('should throw an error', async function() {
await expect(
this.subject.promises.addFile(
this.project._id,
this.folder._id,
this.newFile
)
).to.be.rejected
})
})
})
describe('addFolder', function() {
beforeEach(async function() {
const folderName = 'New folder'
this.FolderModel.withArgs({ name: folderName }).returns({
_id: ObjectId(),
name: folderName
})
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: {
'rootFolder.0.folders.0.folders': sinon.match({
name: folderName
})
},
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
await this.subject.promises.addFolder(
this.project._id,
this.folder._id,
folderName
)
})
it('adds the folder in Mongo', function() {
this.ProjectMock.verify()
})
})
describe('replaceFileWithNew', function() {
beforeEach(async function() {
const newFile = {
_id: ObjectId(),
name: 'some-other-file.png',
linkedFileData: { some: 'data' },
hash: 'some-hash'
}
// Add a deleted file record
this.ProjectMock.expects('updateOne')
.withArgs(
{ _id: this.project._id },
{
$push: {
deletedFiles: {
_id: this.file._id,
name: this.file.name,
linkedFileData: this.file.linkedFileData,
hash: this.file.hash,
deletedAt: sinon.match.date
}
}
}
)
.chain('exec')
.resolves()
// Update the file in place
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$set: {
'rootFolder.0.fileRefs.0._id': newFile._id,
'rootFolder.0.fileRefs.0.created': sinon.match.date,
'rootFolder.0.fileRefs.0.linkedFileData': newFile.linkedFileData,
'rootFolder.0.fileRefs.0.hash': newFile.hash
},
$inc: {
version: 1,
'rootFolder.0.fileRefs.0.rev': 1
}
}
)
.chain('exec')
.resolves(this.project)
await this.subject.promises.replaceFileWithNew(
this.project._id,
this.file._id,
newFile
)
})
it('updates the database', function() {
this.ProjectMock.verify()
})
})
describe('mkdirp', function() {
describe('when the path is just a slash', function() {
beforeEach(async function() {
this.result = await this.subject.promises.mkdirp(this.project._id, '/')
})
it('should return the root folder', function() {
expect(this.result.folder).to.deep.equal(this.rootFolder)
})
it('should not report a parent folder', function() {
expect(this.result.folder.parentFolder_id).not.to.exist
})
it('should not return new folders', function() {
expect(this.result.newFolders).to.have.length(0)
})
})
describe('when the folder already exists', function() {
beforeEach(async function() {
this.result = await this.subject.promises.mkdirp(
this.project._id,
'/test-folder'
)
})
it('should return the existing folder', function() {
expect(this.result.folder).to.deep.equal(this.folder)
})
it('should report the parent folder', function() {
expect(this.result.folder.parentFolder_id).not.equal(
this.rootFolder._id
)
})
it('should not return new folders', function() {
expect(this.result.newFolders).to.have.length(0)
})
})
describe('when the path is a new folder at the top level', function() {
beforeEach(async function() {
this.newFolder = { _id: ObjectId(), name: 'new-folder' }
this.FolderModel.returns(this.newFolder)
this.exactCaseMatch = false
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: { 'rootFolder.0.folders': this.newFolder },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
this.result = await this.subject.promises.mkdirp(
this.project._id,
'/new-folder/',
{ exactCaseMatch: this.exactCaseMatch }
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
it('should make just one folder', function() {
expect(this.result.newFolders).to.have.length(1)
})
it('should return the new folder', function() {
expect(this.result.folder.name).to.equal('new-folder')
})
it('should return the parent folder', function() {
expect(this.result.folder.parentFolder_id).to.equal(this.rootFolder._id)
})
it('should pass the exactCaseMatch option to ProjectLocator', function() {
expect(
this.ProjectLocator.promises.findElementByPath
).to.have.been.calledWithMatch({ exactCaseMatch: this.exactCaseMatch })
})
})
describe('adding a subfolder', function() {
beforeEach(async function() {
this.newFolder = { _id: ObjectId(), name: 'new-folder' }
this.FolderModel.returns(this.newFolder)
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: {
'rootFolder.0.folders.0.folders': sinon.match({
name: 'new-folder'
})
},
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
this.result = await this.subject.promises.mkdirp(
this.project._id,
'/test-folder/new-folder'
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
it('should create one folder', function() {
expect(this.result.newFolders).to.have.length(1)
})
it('should return the new folder', function() {
expect(this.result.folder.name).to.equal('new-folder')
})
it('should return the parent folder', function() {
expect(this.result.folder.parentFolder_id).to.equal(this.folder._id)
})
})
describe('when mutliple folders are missing', async function() {
beforeEach(function() {
this.folder1 = { _id: ObjectId(), name: 'folder1' }
this.folder1Path = {
fileSystem: '/test-folder/folder1',
mongo: 'rootFolder.0.folders.0.folders.0'
}
this.folder2 = { _id: ObjectId(), name: 'folder2' }
this.folder2Path = {
fileSystem: '/test-folder/folder1/folder2',
mongo: 'rootFolder.0.folders.0.folders.0.folders.0'
}
this.FolderModel.onFirstCall().returns(this.folder1)
this.FolderModel.onSecondCall().returns(this.folder2)
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.folder1._id,
type: 'folder'
})
.resolves({
element: this.folder1,
path: this.folder1Path
})
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.folder2._id,
type: 'folder'
})
.resolves({
element: this.folder2,
path: this.folder2Path
})
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: {
'rootFolder.0.folders.0.folders': sinon.match({
name: 'folder1'
})
},
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: {
'rootFolder.0.folders.0.folders.0.folders': sinon.match({
name: 'folder2'
})
},
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
})
;[
{
description: 'without a trailing slash',
path: '/test-folder/folder1/folder2'
},
{
description: 'with a trailing slash',
path: '/test-folder/folder1/folder2/'
}
].forEach(({ description, path }) => {
describe(description, function() {
beforeEach(async function() {
this.result = await this.subject.promises.mkdirp(
this.project._id,
path
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
it('should add multiple folders', function() {
const newFolders = this.result.newFolders
expect(newFolders).to.have.length(2)
expect(newFolders[0].name).to.equal('folder1')
expect(newFolders[1].name).to.equal('folder2')
})
it('should return the last folder', function() {
expect(this.result.folder.name).to.equal('folder2')
})
it('should return the parent folder', function() {
expect(this.result.folder.parentFolder_id).to.equal(
this.folder1._id
)
})
})
})
})
})
describe('moveEntity', function() {
describe('moving a doc into a different folder', function() {
beforeEach(async function() {
this.pathAfterMove = {
fileSystem: '/somewhere/else.txt'
}
this.oldDocs = ['old-doc']
this.oldFiles = ['old-file']
this.newDocs = ['new-doc']
this.newFiles = ['new-file']
this.ProjectEntityHandler.promises.getAllEntitiesFromProject
.onFirstCall()
.resolves({ docs: this.oldDocs, files: this.oldFiles })
this.ProjectEntityHandler.promises.getAllEntitiesFromProject
.onSecondCall()
.resolves({ docs: this.newDocs, files: this.newFiles })
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: { 'rootFolder.0.folders.0.docs': this.doc },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$pull: { 'rootFolder.0.docs': { _id: this.doc._id } },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
this.result = await this.subject.promises.moveEntity(
this.project._id,
this.doc._id,
this.folder._id,
'doc'
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
it('should report what changed', function() {
expect(this.result).to.deep.equal({
project: this.project,
startPath: '/test-doc.txt',
endPath: '/test-folder/test-doc.txt',
rev: this.doc.rev,
changes: {
oldDocs: this.oldDocs,
newDocs: this.newDocs,
oldFiles: this.oldFiles,
newFiles: this.newFiles,
newProject: this.project
}
})
})
})
describe('when moving a folder inside itself', function() {
it('throws an error', async function() {
await expect(
this.subject.promises.moveEntity(
this.project._id,
this.folder._id,
this.folder._id,
'folder'
)
).to.be.rejectedWith(Errors.InvalidNameError)
})
})
describe('when moving a folder to a subfolder of itself', function() {
it('throws an error', async function() {
await expect(
this.subject.promises.moveEntity(
this.project._id,
this.folder._id,
this.subfolder._id,
'folder'
)
).to.be.rejectedWith(Errors.InvalidNameError)
})
})
})
describe('deleteEntity', function() {
beforeEach(async function() {
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$pull: { 'rootFolder.0.docs': { _id: this.doc._id } },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
await this.subject.promises.deleteEntity(
this.project._id,
this.doc._id,
'doc'
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
})
describe('renameEntity', function() {
describe('happy path', function() {
beforeEach(async function() {
this.newName = 'new.tex'
this.oldDocs = ['old-doc']
this.oldFiles = ['old-file']
this.newDocs = ['new-doc']
this.newFiles = ['new-file']
this.ProjectEntityHandler.promises.getAllEntitiesFromProject
.onFirstCall()
.resolves({ docs: this.oldDocs, files: this.oldFiles })
this.ProjectEntityHandler.promises.getAllEntitiesFromProject
.onSecondCall()
.resolves({ docs: this.newDocs, files: this.newFiles })
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$set: { 'rootFolder.0.docs.0.name': this.newName },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
this.result = await this.subject.promises.renameEntity(
this.project._id,
this.doc._id,
'doc',
this.newName
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
it('returns info', function() {
expect(this.result).to.deep.equal({
project: this.project,
startPath: '/test-doc.txt',
endPath: '/new.tex',
rev: this.doc.rev,
changes: {
oldDocs: this.oldDocs,
newDocs: this.newDocs,
oldFiles: this.oldFiles,
newFiles: this.newFiles,
newProject: this.project
}
})
})
})
describe('name already exists', function() {
it('should throw an error', async function() {
await expect(
this.subject.promises.renameEntity(
this.project._id,
this.doc._id,
'doc',
this.folder.name
)
).to.be.rejectedWith(Errors.InvalidNameError)
})
})
})
describe('_putElement', function() {
describe('updating the project', function() {
describe('when the parent folder is given', function() {
beforeEach(function() {
this.newFile = { _id: ObjectId(), name: 'new file.png' }
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: { 'rootFolder.0.folders.0.fileRefs': this.newFile },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
})
it('should update the database', async function() {
await this.subject.promises._putElement(
this.project,
this.folder._id,
this.newFile,
'files'
)
this.ProjectMock.verify()
})
it('should add an s onto the type if not included', async function() {
await this.subject.promises._putElement(
this.project,
this.folder._id,
this.newFile,
'file'
)
this.ProjectMock.verify()
})
})
describe('error cases', function() {
it('should throw an error if element is null', async function() {
await expect(
this.subject.promises._putElement(
this.project,
this.folder._id,
null,
'file'
)
).to.be.rejected
})
it('should error if the element has no _id', async function() {
const file = { name: 'something' }
await expect(
this.subject.promises._putElement(
this.project,
this.folder._id,
file,
'file'
)
).to.be.rejected
})
it('should error if element name contains invalid characters', async function() {
const file = { _id: ObjectId(), name: 'something*bad' }
await expect(
this.subject.promises._putElement(
this.project,
this.folder._id,
file,
'file'
)
).to.be.rejected
})
it('should error if element name is too long', async function() {
const file = {
_id: ObjectId(),
name: 'long-'.repeat(1000) + 'something'
}
await expect(
this.subject.promises._putElement(
this.project,
this.folder._id,
file,
'file'
)
).to.be.rejectedWith(Errors.InvalidNameError)
})
it('should error if the folder name is too long', async function() {
const file = {
_id: ObjectId(),
name: 'something'
}
this.ProjectLocator.promises.findElement
.withArgs({
project: this.project,
element_id: this.folder._id,
type: 'folder'
})
.resolves({
element: this.folder,
path: { fileSystem: 'subdir/'.repeat(1000) + 'foo' }
})
await expect(
this.subject.promises._putElement(
this.project,
this.folder._id,
file,
'file'
)
).to.be.rejectedWith(Errors.InvalidNameError)
})
;['file', 'doc', 'folder'].forEach(entityType => {
it(`should error if a ${entityType} already exists with the same name`, async function() {
const file = {
_id: ObjectId(),
name: this[entityType].name
}
await expect(
this.subject.promises._putElement(
this.project,
null,
file,
'file'
)
).to.be.rejectedWith(Errors.InvalidNameError)
})
})
})
})
describe('when the parent folder is not given', function() {
it('should default to root folder insert', async function() {
this.newFile = { _id: ObjectId(), name: 'new file.png' }
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$push: { 'rootFolder.0.fileRefs': this.newFile },
$inc: { version: 1 }
}
)
.chain('exec')
.resolves(this.project)
await this.subject.promises._putElement(
this.project,
this.rootFolder._id,
this.newFile,
'file'
)
})
})
})
describe('_insertDeletedDocReference', function() {
beforeEach(async function() {
this.ProjectMock.expects('updateOne')
.withArgs(
{ _id: this.project._id },
{
$push: {
deletedDocs: {
_id: this.doc._id,
name: this.doc.name,
deletedAt: sinon.match.date
}
}
}
)
.chain('exec')
.resolves()
await this.subject.promises._insertDeletedDocReference(
this.project._id,
this.doc
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
})
describe('_insertDeletedFileReference', function() {
beforeEach(async function() {
this.ProjectMock.expects('updateOne')
.withArgs(
{ _id: this.project._id },
{
$push: {
deletedFiles: {
_id: this.file._id,
name: this.file.name,
linkedFileData: this.file.linkedFileData,
hash: this.file.hash,
deletedAt: sinon.match.date
}
}
}
)
.chain('exec')
.resolves()
await this.subject.promises._insertDeletedFileReference(
this.project._id,
this.file
)
})
it('should update the database', function() {
this.ProjectMock.verify()
})
})
describe('createNewFolderStructure', function() {
beforeEach(function() {
this.mockRootFolder = 'MOCK_ROOT_FOLDER'
this.docUploads = ['MOCK_DOC_UPLOAD']
this.fileUploads = ['MOCK_FILE_UPLOAD']
this.FolderStructureBuilder.buildFolderStructure
.withArgs(this.docUploads, this.fileUploads)
.returns(this.mockRootFolder)
this.updateExpectation = this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.project._id,
'rootFolder.0.folders.0': { $exists: false },
'rootFolder.0.docs.0': { $exists: false },
'rootFolder.0.files.0': { $exists: false }
},
{ $set: { rootFolder: [this.mockRootFolder] }, $inc: { version: 1 } }
)
.chain('exec')
})
describe('happy path', function() {
beforeEach(async function() {
this.updateExpectation.resolves({ n: 1 })
await this.subject.promises.createNewFolderStructure(
this.project._id,
this.docUploads,
this.fileUploads
)
})
it('updates the database', function() {
this.ProjectMock.verify()
})
})
describe("when the update doesn't find a matching document", function() {
beforeEach(async function() {
this.updateExpectation.resolves({ n: 0 })
})
it('throws an error', async function() {
await expect(
this.subject.promises.createNewFolderStructure(
this.project._id,
this.docUploads,
this.fileUploads
)
).to.be.rejected
})
})
})
describe('replaceDocWithFile', function() {
it('should simultaneously remove the doc and add the file', async function() {
this.ProjectMock.expects('findOneAndUpdate')
.withArgs(
{ _id: this.project._id },
{
$pull: { 'rootFolder.0.docs': { _id: this.doc._id } },
$push: { 'rootFolder.0.fileRefs': this.file },
$inc: { version: 1 }
},
{ new: true }
)
.chain('exec')
.resolves(this.project)
await this.subject.promises.replaceDocWithFile(
this.project._id,
this.doc._id,
this.file
)
this.ProjectMock.verify()
})
})
})