mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-03 21:19:44 -05:00
1be43911b4
Set Prettier's "trailingComma" setting to "es5" GitOrigin-RevId: 9f14150511929a855b27467ad17be6ab262fe5d5
353 lines
11 KiB
JavaScript
353 lines
11 KiB
JavaScript
const sinon = require('sinon')
|
|
const { expect } = require('chai')
|
|
const timekeeper = require('timekeeper')
|
|
const SandboxedModule = require('sandboxed-module')
|
|
const { ObjectId } = require('mongodb')
|
|
|
|
const MODULE_PATH =
|
|
'../../../../app/src/Features/Uploads/ProjectUploadManager.js'
|
|
|
|
describe('ProjectUploadManager', function () {
|
|
beforeEach(function () {
|
|
this.now = Date.now()
|
|
timekeeper.freeze(this.now)
|
|
this.rootFolderId = new ObjectId()
|
|
this.ownerId = new ObjectId()
|
|
this.zipPath = '/path/to/zip/file-name.zip'
|
|
this.extractedZipPath = `/path/to/zip/file-name-${this.now}`
|
|
this.mainContent = 'Contents of main.tex'
|
|
this.projectName = 'My project*'
|
|
this.fixedProjectName = 'My project'
|
|
this.uniqueProjectName = 'My project (1)'
|
|
this.project = {
|
|
_id: new ObjectId(),
|
|
rootFolder: [{ _id: this.rootFolderId }],
|
|
overleaf: { history: { id: 12345 } },
|
|
}
|
|
this.doc = {
|
|
_id: new ObjectId(),
|
|
name: 'main.tex',
|
|
}
|
|
this.docFsPath = '/path/to/doc'
|
|
this.docLines = ['My thesis', 'by A. U. Thor']
|
|
this.file = {
|
|
_id: new ObjectId(),
|
|
name: 'image.png',
|
|
}
|
|
this.fileFsPath = '/path/to/file'
|
|
|
|
this.topLevelDestination = '/path/to/zip/file-extracted/nested'
|
|
this.newProjectVersion = 123
|
|
this.importEntries = [
|
|
{
|
|
type: 'doc',
|
|
projectPath: '/main.tex',
|
|
lines: this.docLines,
|
|
},
|
|
{
|
|
type: 'file',
|
|
projectPath: `/${this.file.name}`,
|
|
fsPath: this.fileFsPath,
|
|
},
|
|
]
|
|
this.docEntries = [
|
|
{
|
|
doc: this.doc,
|
|
path: `/${this.doc.name}`,
|
|
docLines: this.docLines.join('\n'),
|
|
},
|
|
]
|
|
this.fileEntries = [
|
|
{ file: this.file, path: `/${this.file.name}`, url: this.fileStoreUrl },
|
|
]
|
|
|
|
this.fs = {
|
|
remove: sinon.stub().resolves(),
|
|
}
|
|
this.ArchiveManager = {
|
|
promises: {
|
|
extractZipArchive: sinon.stub().resolves(),
|
|
findTopLevelDirectory: sinon
|
|
.stub()
|
|
.withArgs(this.extractedZipPath)
|
|
.resolves(this.topLevelDestination),
|
|
},
|
|
}
|
|
this.Doc = sinon.stub().returns(this.doc)
|
|
this.DocstoreManager = {
|
|
promises: {
|
|
updateDoc: sinon.stub().resolves(),
|
|
},
|
|
}
|
|
this.DocumentHelper = {
|
|
getTitleFromTexContent: sinon
|
|
.stub()
|
|
.withArgs(this.mainContent)
|
|
.returns(this.projectName),
|
|
}
|
|
this.DocumentUpdaterHandler = {
|
|
promises: {
|
|
updateProjectStructure: sinon.stub().resolves(),
|
|
},
|
|
}
|
|
this.FileStoreHandler = {
|
|
promises: {
|
|
uploadFileFromDisk: sinon
|
|
.stub()
|
|
.resolves({ fileRef: this.file, url: this.fileStoreUrl }),
|
|
},
|
|
}
|
|
this.FileSystemImportManager = {
|
|
promises: {
|
|
importDir: sinon
|
|
.stub()
|
|
.withArgs(this.topLevelDestination)
|
|
.resolves(this.importEntries),
|
|
},
|
|
}
|
|
this.ProjectCreationHandler = {
|
|
promises: {
|
|
createBlankProject: sinon.stub().resolves(this.project),
|
|
},
|
|
}
|
|
this.ProjectEntityMongoUpdateHandler = {
|
|
promises: {
|
|
createNewFolderStructure: sinon.stub().resolves(this.newProjectVersion),
|
|
},
|
|
}
|
|
this.ProjectRootDocManager = {
|
|
promises: {
|
|
setRootDocAutomatically: sinon.stub().resolves(),
|
|
findRootDocFileFromDirectory: sinon
|
|
.stub()
|
|
.resolves({ path: 'main.tex', content: this.mainContent }),
|
|
setRootDocFromName: sinon.stub().resolves(),
|
|
},
|
|
}
|
|
this.ProjectDetailsHandler = {
|
|
fixProjectName: sinon
|
|
.stub()
|
|
.withArgs(this.projectName)
|
|
.returns(this.fixedProjectName),
|
|
promises: {
|
|
generateUniqueName: sinon.stub().resolves(this.uniqueProjectName),
|
|
},
|
|
}
|
|
this.ProjectDeleter = {
|
|
promises: {
|
|
deleteProject: sinon.stub().resolves(),
|
|
},
|
|
}
|
|
this.TpdsProjectFlusher = {
|
|
promises: {
|
|
flushProjectToTpds: sinon.stub().resolves(),
|
|
},
|
|
}
|
|
|
|
this.ProjectUploadManager = SandboxedModule.require(MODULE_PATH, {
|
|
requires: {
|
|
'fs-extra': this.fs,
|
|
'./ArchiveManager': this.ArchiveManager,
|
|
'../../models/Doc': { Doc: this.Doc },
|
|
'../Docstore/DocstoreManager': this.DocstoreManager,
|
|
'../Documents/DocumentHelper': this.DocumentHelper,
|
|
'../DocumentUpdater/DocumentUpdaterHandler': this
|
|
.DocumentUpdaterHandler,
|
|
'../FileStore/FileStoreHandler': this.FileStoreHandler,
|
|
'./FileSystemImportManager': this.FileSystemImportManager,
|
|
'../Project/ProjectCreationHandler': this.ProjectCreationHandler,
|
|
'../Project/ProjectEntityMongoUpdateHandler': this
|
|
.ProjectEntityMongoUpdateHandler,
|
|
'../Project/ProjectRootDocManager': this.ProjectRootDocManager,
|
|
'../Project/ProjectDetailsHandler': this.ProjectDetailsHandler,
|
|
'../Project/ProjectDeleter': this.ProjectDeleter,
|
|
'../ThirdPartyDataStore/TpdsProjectFlusher': this.TpdsProjectFlusher,
|
|
},
|
|
})
|
|
})
|
|
|
|
afterEach(function () {
|
|
timekeeper.reset()
|
|
})
|
|
|
|
describe('createProjectFromZipArchive', function () {
|
|
describe('when the title can be read from the root document', function () {
|
|
beforeEach(async function () {
|
|
await this.ProjectUploadManager.promises.createProjectFromZipArchive(
|
|
this.ownerId,
|
|
this.projectName,
|
|
this.zipPath
|
|
)
|
|
})
|
|
|
|
it('should extract the archive', function () {
|
|
this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith(
|
|
this.zipPath,
|
|
this.extractedZipPath
|
|
)
|
|
})
|
|
|
|
it('should create a project', function () {
|
|
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
|
|
this.ownerId,
|
|
this.uniqueProjectName
|
|
)
|
|
})
|
|
|
|
it('should initialize the file tree', function () {
|
|
this.ProjectEntityMongoUpdateHandler.promises.createNewFolderStructure.should.have.been.calledWith(
|
|
this.project._id,
|
|
this.docEntries,
|
|
this.fileEntries
|
|
)
|
|
})
|
|
|
|
it('should notify document updater', function () {
|
|
this.DocumentUpdaterHandler.promises.updateProjectStructure.should.have.been.calledWith(
|
|
this.project._id,
|
|
this.project.overleaf.history.id,
|
|
this.ownerId,
|
|
{
|
|
newDocs: this.docEntries,
|
|
newFiles: this.fileEntries,
|
|
newProject: { version: this.newProjectVersion },
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should flush the project to TPDS', function () {
|
|
this.TpdsProjectFlusher.promises.flushProjectToTpds.should.have.been.calledWith(
|
|
this.project._id
|
|
)
|
|
})
|
|
|
|
it('should set the root document', function () {
|
|
this.ProjectRootDocManager.promises.setRootDocFromName.should.have.been.calledWith(
|
|
this.project._id,
|
|
'main.tex'
|
|
)
|
|
})
|
|
|
|
it('should remove the destination directory afterwards', function () {
|
|
this.fs.remove.should.have.been.calledWith(this.extractedZipPath)
|
|
})
|
|
})
|
|
|
|
describe("when the root document can't be determined", function () {
|
|
beforeEach(async function () {
|
|
this.ProjectRootDocManager.promises.findRootDocFileFromDirectory.resolves(
|
|
{}
|
|
)
|
|
await this.ProjectUploadManager.promises.createProjectFromZipArchive(
|
|
this.ownerId,
|
|
this.projectName,
|
|
this.zipPath
|
|
)
|
|
})
|
|
|
|
it('should not try to set the root doc', function () {
|
|
this.ProjectRootDocManager.promises.setRootDocFromName.should.not.have
|
|
.been.called
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('createProjectFromZipArchiveWithName', function () {
|
|
beforeEach(async function () {
|
|
await this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
|
|
this.ownerId,
|
|
this.projectName,
|
|
this.zipPath
|
|
)
|
|
})
|
|
|
|
it('should extract the archive', function () {
|
|
this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith(
|
|
this.zipPath,
|
|
this.extractedZipPath
|
|
)
|
|
})
|
|
|
|
it('should create a project owned by the owner_id', function () {
|
|
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
|
|
this.ownerId,
|
|
this.uniqueProjectName
|
|
)
|
|
})
|
|
|
|
it('should automatically set the root doc', function () {
|
|
this.ProjectRootDocManager.promises.setRootDocAutomatically.should.have.been.calledWith(
|
|
this.project._id
|
|
)
|
|
})
|
|
|
|
it('should initialize the file tree', function () {
|
|
this.ProjectEntityMongoUpdateHandler.promises.createNewFolderStructure.should.have.been.calledWith(
|
|
this.project._id,
|
|
this.docEntries,
|
|
this.fileEntries
|
|
)
|
|
})
|
|
|
|
it('should notify document updater', function () {
|
|
this.DocumentUpdaterHandler.promises.updateProjectStructure.should.have.been.calledWith(
|
|
this.project._id,
|
|
this.project.overleaf.history.id,
|
|
this.ownerId,
|
|
{
|
|
newDocs: this.docEntries,
|
|
newFiles: this.fileEntries,
|
|
newProject: { version: this.newProjectVersion },
|
|
}
|
|
)
|
|
})
|
|
|
|
it('should flush the project to TPDS', function () {
|
|
this.TpdsProjectFlusher.promises.flushProjectToTpds.should.have.been.calledWith(
|
|
this.project._id
|
|
)
|
|
})
|
|
|
|
it('should remove the destination directory afterwards', function () {
|
|
this.fs.remove.should.have.been.calledWith(this.extractedZipPath)
|
|
})
|
|
|
|
describe('when initializing the folder structure fails', function () {
|
|
beforeEach(async function () {
|
|
this.ProjectEntityMongoUpdateHandler.promises.createNewFolderStructure.rejects()
|
|
await expect(
|
|
this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
|
|
this.ownerId,
|
|
this.projectName,
|
|
this.zipPath
|
|
)
|
|
).to.be.rejected
|
|
})
|
|
|
|
it('should cleanup the blank project created', async function () {
|
|
this.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
|
|
this.project._id
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('when setting automatically the root doc fails', function () {
|
|
beforeEach(async function () {
|
|
this.ProjectRootDocManager.promises.setRootDocAutomatically.rejects()
|
|
await expect(
|
|
this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
|
|
this.ownerId,
|
|
this.projectName,
|
|
this.zipPath
|
|
)
|
|
).to.be.rejected
|
|
})
|
|
|
|
it('should cleanup the blank project created', function () {
|
|
this.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
|
|
this.project._id
|
|
)
|
|
})
|
|
})
|
|
})
|
|
})
|