| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089 |
- const { expect } = require('chai')
- const sinon = require('sinon')
- const tk = require('timekeeper')
- const Errors = require('../../../../app/src/Features/Errors/Errors')
- const { ObjectId } = require('mongodb')
- const SandboxedModule = require('sandboxed-module')
- const { DeletedFile } = require('../helpers/models/DeletedFile')
- 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.Settings = { maxEntitiesPerProject: 100 }
- this.CooldownManager = {}
- this.LockManager = {
- promises: {
- runWithLock: sinon.spy((namespace, id, runner) => runner()),
- },
- }
- this.FolderModel = sinon.stub()
- this.DeletedFileMock = sinon.mock(DeletedFile)
- this.ProjectMock = sinon.mock(Project)
- this.ProjectEntityHandler = {
- 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, {
- requires: {
- mongodb: { ObjectId },
- '@overleaf/settings': this.Settings,
- '../Cooldown/CooldownManager': this.CooldownManager,
- '../../models/Folder': { Folder: this.FolderModel },
- '../../infrastructure/LockManager': this.LockManager,
- '../../models/DeletedFile': { DeletedFile },
- '../../models/Project': { Project },
- './ProjectEntityHandler': this.ProjectEntityHandler,
- './ProjectLocator': this.ProjectLocator,
- './ProjectGetter': this.ProjectGetter,
- './FolderStructureBuilder': this.FolderStructureBuilder,
- },
- })
- })
- afterEach(function () {
- this.DeletedFileMock.restore()
- 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.DeletedFileMock.expects('create')
- .withArgs({
- projectId: this.project._id,
- _id: this.file._id,
- name: this.file.name,
- linkedFileData: this.file.linkedFileData,
- hash: this.file.hash,
- deletedAt: sinon.match.date,
- })
- .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.DeletedFileMock.verify()
- 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.getAllEntitiesFromProject
- .onFirstCall()
- .returns({ docs: this.oldDocs, files: this.oldFiles })
- this.ProjectEntityHandler.getAllEntitiesFromProject
- .onSecondCall()
- .returns({ 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.getAllEntitiesFromProject
- .onFirstCall()
- .returns({ docs: this.oldDocs, files: this.oldFiles })
- this.ProjectEntityHandler.getAllEntitiesFromProject
- .onSecondCall()
- .returns({ 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('_insertDeletedFileReference', function () {
- beforeEach(async function () {
- this.DeletedFileMock.expects('create')
- .withArgs({
- projectId: this.project._id,
- _id: this.file._id,
- name: this.file.name,
- linkedFileData: this.file.linkedFileData,
- hash: this.file.hash,
- deletedAt: sinon.match.date,
- })
- .resolves()
- await this.subject.promises._insertDeletedFileReference(
- this.project._id,
- this.file
- )
- })
- it('should update the database', function () {
- this.DeletedFileMock.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('findOneAndUpdate')
- .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 } },
- { new: true, lean: true, fields: { version: 1 } }
- )
- .chain('exec')
- })
- describe('happy path', function () {
- beforeEach(async function () {
- this.updateExpectation.resolves({ version: 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(null)
- })
- 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()
- })
- })
- describe('replaceFileWithDoc', function () {
- it('should simultaneously remove the file and add the doc', async function () {
- this.ProjectMock.expects('findOneAndUpdate')
- .withArgs(
- { _id: this.project._id },
- {
- $pull: { 'rootFolder.0.fileRefs': { _id: this.file._id } },
- $push: { 'rootFolder.0.docs': this.doc },
- $inc: { version: 1 },
- },
- { new: true }
- )
- .chain('exec')
- .resolves(this.project)
- await this.subject.promises.replaceFileWithDoc(
- this.project._id,
- this.file._id,
- this.doc
- )
- this.ProjectMock.verify()
- })
- })
- })
|