Files
overleaf-cep/services/web/test/unit/src/Project/ProjectLocatorTests.js
T
Andrew Rumble 2ad9f36706 Promisify tests
GitOrigin-RevId: 6f413f4c5ef8d034b4e94afacdf2d7b43c3a8830
2025-04-29 08:05:18 +00:00

511 lines
16 KiB
JavaScript

const { expect } = require('chai')
const modulePath = '../../../../app/src/Features/Project/ProjectLocator'
const SandboxedModule = require('sandboxed-module')
const sinon = require('sinon')
const Errors = require('../../../../app/src/Features/Errors/Errors')
const project = { _id: '1234566', rootFolder: [] }
const rootDoc = { name: 'rootDoc', _id: 'das239djd' }
const doc1 = { name: 'otherDoc.txt', _id: 'dsad2ddd' }
const doc2 = { name: 'docname.txt', _id: 'dsad2ddddd' }
const file1 = { name: 'file1', _id: 'dsa9lkdsad' }
const subSubFile = { name: 'subSubFile', _id: 'd1d2dk' }
const subSubDoc = { name: 'subdoc.txt', _id: '321dmdwi' }
const secondSubFolder = {
name: 'secondSubFolder',
_id: 'dsa3e23',
docs: [subSubDoc],
fileRefs: [subSubFile],
folders: [],
}
const subFolder = {
name: 'subFolder',
_id: 'dsadsa93',
folders: [secondSubFolder, null],
docs: [],
fileRefs: [],
}
const subFolder1 = { name: 'subFolder1', _id: '123asdjoij' }
const rootFolder = {
_id: '123sdskd',
docs: [doc1, doc2, null, rootDoc],
fileRefs: [file1],
folders: [subFolder1, subFolder],
}
project.rootFolder[0] = rootFolder
project.rootDoc_id = rootDoc._id
describe('ProjectLocator', function () {
beforeEach(function () {
this.ProjectGetter = {
getProject: sinon.stub().callsArgWith(2, null, project),
}
this.ProjectHelper = {
isArchived: sinon.stub(),
isTrashed: sinon.stub(),
isArchivedOrTrashed: sinon.stub(),
}
this.locator = SandboxedModule.require(modulePath, {
requires: {
'../../models/User': { User: this.User },
'./ProjectGetter': this.ProjectGetter,
'./ProjectHelper': this.ProjectHelper,
},
})
})
describe('finding a doc', function () {
it('finds one at the root level', async function () {
const { element, path, folder } = await this.locator.promises.findElement(
{
project_id: project._id,
element_id: doc2._id,
type: 'docs',
}
)
element._id.should.equal(doc2._id)
path.fileSystem.should.equal(`/${doc2.name}`)
folder._id.should.equal(project.rootFolder[0]._id)
path.mongo.should.equal('rootFolder.0.docs.1')
})
it('when it is nested', async function () {
const { element, path, folder } = await this.locator.promises.findElement(
{
project_id: project._id,
element_id: subSubDoc._id,
type: 'doc',
}
)
expect(element._id).to.equal(subSubDoc._id)
path.fileSystem.should.equal(
`/${subFolder.name}/${secondSubFolder.name}/${subSubDoc.name}`
)
folder._id.should.equal(secondSubFolder._id)
path.mongo.should.equal('rootFolder.0.folders.1.folders.0.docs.0')
})
it('should give error if element could not be found', async function () {
await expect(
this.locator.promises.findElement({
project_id: project._id,
element_id: 'ddsd432nj42',
type: 'docs',
})
)
.to.eventually.be.rejectedWith(Errors.NotFoundError)
.and.eventually.have.property('message', 'entity not found')
})
})
describe('finding a folder', function () {
it('should return root folder when looking for root folder', async function () {
const { element: foundElement } = await this.locator.promises.findElement(
{
project_id: project._id,
element_id: rootFolder._id,
type: 'folder',
}
)
foundElement._id.should.equal(rootFolder._id)
})
it('when at root', async function () {
const {
element: foundElement,
path,
folder: parentFolder,
} = await this.locator.promises.findElement({
project_id: project._id,
element_id: subFolder._id,
type: 'folder',
})
foundElement._id.should.equal(subFolder._id)
path.fileSystem.should.equal(`/${subFolder.name}`)
parentFolder._id.should.equal(rootFolder._id)
path.mongo.should.equal('rootFolder.0.folders.1')
})
it('when deeply nested', async function () {
const {
element: foundElement,
path,
folder: parentFolder,
} = await this.locator.promises.findElement({
project_id: project._id,
element_id: secondSubFolder._id,
type: 'folder',
})
foundElement._id.should.equal(secondSubFolder._id)
path.fileSystem.should.equal(`/${subFolder.name}/${secondSubFolder.name}`)
parentFolder._id.should.equal(subFolder._id)
path.mongo.should.equal('rootFolder.0.folders.1.folders.0')
})
})
describe('finding a file', function () {
it('when at root', async function () {
const {
element: foundElement,
path,
folder: parentFolder,
} = await this.locator.promises.findElement({
project_id: project._id,
element_id: file1._id,
type: 'fileRefs',
})
foundElement._id.should.equal(file1._id)
path.fileSystem.should.equal(`/${file1.name}`)
parentFolder._id.should.equal(rootFolder._id)
path.mongo.should.equal('rootFolder.0.fileRefs.0')
})
it('when deeply nested', async function () {
const {
element: foundElement,
path,
folder: parentFolder,
} = await this.locator.promises.findElement({
project_id: project._id,
element_id: subSubFile._id,
type: 'fileRefs',
})
foundElement._id.should.equal(subSubFile._id)
path.fileSystem.should.equal(
`/${subFolder.name}/${secondSubFolder.name}/${subSubFile.name}`
)
parentFolder._id.should.equal(secondSubFolder._id)
path.mongo.should.equal('rootFolder.0.folders.1.folders.0.fileRefs.0')
})
})
describe('finding an element with wrong element type', function () {
it('should add an s onto the element type', async function () {
const { element: foundElement } = await this.locator.promises.findElement(
{
project_id: project._id,
element_id: subSubDoc._id,
type: 'doc',
}
)
foundElement._id.should.equal(subSubDoc._id)
})
it('should convert file to fileRefs', async function () {
const { element: foundElement } = await this.locator.promises.findElement(
{
project_id: project._id,
element_id: file1._id,
type: 'fileRefs',
}
)
foundElement._id.should.equal(file1._id)
})
})
describe('should be able to take actual project as well as id', function () {
const doc3 = {
_id: '123dsdj3',
name: 'doc3',
}
const rootFolder2 = {
_id: '123sddedskd',
docs: [doc3],
}
const project2 = {
_id: '1234566',
rootFolder: [rootFolder2],
}
it('should find doc in project', async function () {
const {
element: foundElement,
path,
folder: parentFolder,
} = await this.locator.promises.findElement({
project: project2,
element_id: doc3._id,
type: 'docs',
})
foundElement._id.should.equal(doc3._id)
path.fileSystem.should.equal(`/${doc3.name}`)
parentFolder._id.should.equal(project2.rootFolder[0]._id)
path.mongo.should.equal('rootFolder.0.docs.0')
})
})
describe('finding root doc', function () {
it('should return root doc when passed project', async function () {
const { element: doc } = await this.locator.promises.findRootDoc(project)
doc._id.should.equal(rootDoc._id)
})
it('should return root doc when passed project_id', async function () {
const { element: doc } = await this.locator.promises.findRootDoc(
project._id
)
doc._id.should.equal(rootDoc._id)
})
it('should return null when the project has no rootDoc', async function () {
project.rootDoc_id = null
const { element: rootDoc } =
await this.locator.promises.findRootDoc(project)
expect(rootDoc).to.equal(null)
})
it('should return null when the rootDoc_id no longer exists', async function () {
project.rootDoc_id = 'doesntexist'
const { element: rootDoc } =
await this.locator.promises.findRootDoc(project)
expect(rootDoc).to.equal(null)
})
})
describe('findElementByPath', function () {
it('should take a doc path and return the element for a root level document', async function () {
const path = `${doc1.name}`
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(doc1)
expect(type).to.equal('doc')
expect(folder).to.equal(rootFolder)
})
it('should take a doc path and return the element for a root level document with a starting slash', async function () {
const path = `/${doc1.name}`
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(doc1)
expect(type).to.equal('doc')
expect(folder).to.equal(rootFolder)
})
it('should take a doc path and return the element for a nested document', async function () {
const path = `${subFolder.name}/${secondSubFolder.name}/${subSubDoc.name}`
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(subSubDoc)
expect(type).to.equal('doc')
expect(folder).to.equal(secondSubFolder)
})
it('should take a file path and return the element for a root level document', async function () {
const path = `${file1.name}`
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(file1)
expect(type).to.equal('file')
expect(folder).to.equal(rootFolder)
})
it('should take a file path and return the element for a nested document', async function () {
const path = `${subFolder.name}/${secondSubFolder.name}/${subSubFile.name}`
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(subSubFile)
expect(type).to.equal('file')
expect(folder).to.equal(secondSubFolder)
})
it('should take a file path and return the element for a nested document case insenstive', async function () {
const path = `${subFolder.name.toUpperCase()}/${secondSubFolder.name.toUpperCase()}/${subSubFile.name.toUpperCase()}`
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(subSubFile)
expect(type).to.equal('file')
expect(folder).to.equal(secondSubFolder)
})
it('should not return elements with a case-insensitive match when exactCaseMatch is true', async function () {
const path = `${subFolder.name.toUpperCase()}/${secondSubFolder.name.toUpperCase()}/${subSubFile.name.toUpperCase()}`
await expect(
this.locator.promises.findElementByPath({
project,
path,
exactCaseMatch: true,
})
).to.eventually.be.rejected
})
it('should take a file path and return the element for a nested folder', async function () {
const path = `${subFolder.name}/${secondSubFolder.name}`
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(secondSubFolder)
expect(type).to.equal('folder')
expect(folder).to.equal(subFolder)
})
it('should take a file path and return the root folder', async function () {
const path = '/'
const { element, type, folder } =
await this.locator.promises.findElementByPath({
project,
path,
})
element.should.deep.equal(rootFolder)
expect(type).to.equal('folder')
expect(folder).to.equal(null)
})
it('should return an error if the file can not be found inside know folder', async function () {
const path = `${subFolder.name}/${secondSubFolder.name}/exist.txt`
await expect(this.locator.promises.findElementByPath({ project, path }))
.to.eventually.be.rejected
})
it('should return an error if the file can not be found inside unknown folder', async function () {
const path = 'this/does/not/exist.txt'
await expect(
this.locator.promises.findElementByPath({
project,
path,
})
).to.eventually.be.rejected
})
describe('where duplicate folder exists', function () {
beforeEach(function () {
this.duplicateFolder = {
name: 'duplicate1',
_id: '1234',
folders: [
{
name: '1',
docs: [{ name: 'main.tex', _id: '456' }],
folders: [],
fileRefs: [],
},
],
docs: [(this.doc = { name: 'main.tex', _id: '456' })],
fileRefs: [],
}
this.project = {
rootFolder: [
{
folders: [this.duplicateFolder, this.duplicateFolder],
fileRefs: [],
docs: [],
},
],
}
})
it('should not call the callback more than once', async function () {
const path = `${this.duplicateFolder.name}/${this.doc.name}`
await this.locator.promises.findElementByPath({
project: this.project,
path,
})
}) // mocha will throw exception if done called multiple times
it('should not call the callback more than once when the path is longer than 1 level below the duplicate level', async function () {
const path = `${this.duplicateFolder.name}/1/main.tex`
await this.locator.promises.findElementByPath({
project: this.project,
path,
})
})
}) // mocha will throw exception if done called multiple times
describe('with a null doc', function () {
beforeEach(function () {
this.project = {
rootFolder: [
{
folders: [],
fileRefs: [],
docs: [{ name: 'main.tex' }, null, { name: 'other.tex' }],
},
],
}
})
it('should not crash with a null', async function () {
const path = '/other.tex'
const { element } = await this.locator.promises.findElementByPath({
project: this.project,
path,
})
element.name.should.equal('other.tex')
})
})
describe('with a null project', function () {
beforeEach(function () {
this.ProjectGetter = { getProject: sinon.stub().callsArg(2) }
})
it('should not crash with a null', async function () {
const path = '/other.tex'
await expect(
this.locator.promises.findElementByPath({
project_id: project._id,
path,
})
).to.be.rejected
})
})
describe('with a project_id', function () {
it('should take a doc path and return the element for a root level document', async function () {
const path = `${doc1.name}`
const { element, type } = await this.locator.promises.findElementByPath(
{
project_id: project._id,
path,
}
)
this.ProjectGetter.getProject
.calledWith(project._id, { rootFolder: true, rootDoc_id: true })
.should.equal(true)
element.should.deep.equal(doc1)
expect(type).to.equal('doc')
})
})
})
describe('findElementByMongoPath', function () {
it('traverses the file tree like Mongo would do', function () {
const element = this.locator.findElementByMongoPath(
project,
'rootFolder.0.folders.1.folders.0.fileRefs.0'
)
expect(element).to.equal(subSubFile)
})
it('throws an error if no element is found', function () {
expect(() =>
this.locator.findElementByMongoPath(
project,
'rootolder.0.folders.0.folders.0.fileRefs.0'
)
).to.throw
})
})
})