mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-05 09:00:07 +00:00
Merge pull request #2745 from overleaf/em-promisify-project-upload-manager
Finish promisification of ProjectUploadManager GitOrigin-RevId: a8550d9decf25af35a2d2f82770cff336a4153cc
This commit is contained in:
parent
c494526b55
commit
d063ab2bc5
3 changed files with 337 additions and 569 deletions
|
@ -15,7 +15,6 @@
|
|||
const logger = require('logger-sharelatex')
|
||||
const metrics = require('metrics-sharelatex')
|
||||
const fs = require('fs')
|
||||
const { promisify } = require('util')
|
||||
const Path = require('path')
|
||||
const fse = require('fs-extra')
|
||||
const yauzl = require('yauzl')
|
||||
|
@ -26,6 +25,7 @@ const {
|
|||
ZipContentsTooLargeError
|
||||
} = require('./ArchiveErrors')
|
||||
const _ = require('underscore')
|
||||
const { promisifyAll } = require('../../util/promises')
|
||||
|
||||
const ONE_MEG = 1024 * 1024
|
||||
|
||||
|
@ -257,10 +257,5 @@ const ArchiveManager = {
|
|||
}
|
||||
}
|
||||
|
||||
const promises = {
|
||||
extractZipArchive: promisify(ArchiveManager.extractZipArchive)
|
||||
}
|
||||
|
||||
ArchiveManager.promises = promises
|
||||
|
||||
ArchiveManager.promises = promisifyAll(ArchiveManager)
|
||||
module.exports = ArchiveManager
|
||||
|
|
|
@ -1,233 +1,139 @@
|
|||
/* eslint-disable
|
||||
camelcase,
|
||||
handle-callback-err,
|
||||
max-len,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const path = require('path')
|
||||
const rimraf = require('rimraf')
|
||||
const { promisify, callbackify } = require('util')
|
||||
const fs = require('fs-extra')
|
||||
const { callbackify } = require('util')
|
||||
const ArchiveManager = require('./ArchiveManager')
|
||||
const FileSystemImportManager = require('./FileSystemImportManager')
|
||||
const ProjectCreationHandler = require('../Project/ProjectCreationHandler')
|
||||
const ProjectRootDocManager = require('../Project/ProjectRootDocManager')
|
||||
const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler')
|
||||
const ProjectDeleter = require('../Project/ProjectDeleter').promises
|
||||
const ProjectDeleter = require('../Project/ProjectDeleter')
|
||||
const DocumentHelper = require('../Documents/DocumentHelper')
|
||||
const logger = require('logger-sharelatex')
|
||||
|
||||
const ProjectUploadManager = {
|
||||
createProjectFromZipArchive(ownerId, defaultName, zipPath, callback) {
|
||||
callbackify(ProjectUploadManager.promises.createProjectFromZipArchive)(
|
||||
ownerId,
|
||||
defaultName,
|
||||
zipPath,
|
||||
callback
|
||||
)
|
||||
},
|
||||
|
||||
createProjectFromZipArchiveWithName(
|
||||
ownerId,
|
||||
proposedName,
|
||||
zipPath,
|
||||
attributes,
|
||||
callback
|
||||
) {
|
||||
if (callback == null) {
|
||||
callback = function(error, project) {}
|
||||
}
|
||||
if (arguments.length === 4) {
|
||||
callback = attributes
|
||||
attributes = {}
|
||||
}
|
||||
|
||||
callbackify(
|
||||
ProjectUploadManager.promises.createProjectFromZipArchiveWithName
|
||||
)(ownerId, proposedName, zipPath, attributes, callback)
|
||||
},
|
||||
|
||||
insertZipArchiveIntoFolder(
|
||||
owner_id,
|
||||
project_id,
|
||||
folder_id,
|
||||
zipPath,
|
||||
callback
|
||||
) {
|
||||
if (callback == null) {
|
||||
callback = function(error) {}
|
||||
}
|
||||
const destination = ProjectUploadManager._getDestinationDirectory(zipPath)
|
||||
return ArchiveManager.extractZipArchive(zipPath, destination, error => {
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
|
||||
return ProjectUploadManager._insertZipContentsIntoFolder(
|
||||
owner_id,
|
||||
project_id,
|
||||
folder_id,
|
||||
destination,
|
||||
callback
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
_insertZipContentsIntoFolder(
|
||||
owner_id,
|
||||
project_id,
|
||||
folder_id,
|
||||
destination,
|
||||
callback
|
||||
) {
|
||||
if (callback == null) {
|
||||
callback = function(error) {}
|
||||
}
|
||||
return ArchiveManager.findTopLevelDirectory(destination, function(
|
||||
error,
|
||||
topLevelDestination
|
||||
) {
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
return FileSystemImportManager.addFolderContents(
|
||||
owner_id,
|
||||
project_id,
|
||||
folder_id,
|
||||
topLevelDestination,
|
||||
false,
|
||||
function(error) {
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
return rimraf(destination, callback)
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
_getDestinationDirectory(source) {
|
||||
return path.join(
|
||||
path.dirname(source),
|
||||
`${path.basename(source, '.zip')}-${Date.now()}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const promises = {
|
||||
async createProjectFromZipArchive(ownerId, defaultName, zipPath) {
|
||||
const destination = ProjectUploadManager._getDestinationDirectory(zipPath)
|
||||
await ArchiveManager.promises.extractZipArchive(zipPath, destination)
|
||||
|
||||
const {
|
||||
path,
|
||||
content
|
||||
} = await ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
destination
|
||||
)
|
||||
|
||||
const projectName =
|
||||
DocumentHelper.getTitleFromTexContent(content || '') || defaultName
|
||||
const proposedName = ProjectDetailsHandler.fixProjectName(projectName)
|
||||
const uniqueName = await ProjectDetailsHandler.promises.generateUniqueName(
|
||||
ownerId,
|
||||
proposedName
|
||||
)
|
||||
|
||||
const project = await ProjectCreationHandler.promises.createBlankProject(
|
||||
ownerId,
|
||||
uniqueName
|
||||
)
|
||||
try {
|
||||
await ProjectUploadManager.promises._insertZipContentsIntoFolder(
|
||||
ownerId,
|
||||
project._id,
|
||||
project.rootFolder[0]._id,
|
||||
destination
|
||||
)
|
||||
|
||||
if (path) {
|
||||
await ProjectRootDocManager.promises.setRootDocFromName(
|
||||
project._id,
|
||||
path
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
// no need to wait for the cleanup here
|
||||
ProjectDeleter.deleteProject(project._id).catch(err =>
|
||||
logger.error(
|
||||
{ err, projectId: project._id },
|
||||
'there was an error cleaning up project after importing a zip failed'
|
||||
)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
return project
|
||||
},
|
||||
|
||||
async createProjectFromZipArchiveWithName(
|
||||
ownerId,
|
||||
proposedName,
|
||||
zipPath,
|
||||
attributes
|
||||
) {
|
||||
attributes = attributes || {}
|
||||
|
||||
const fixedProjectName = ProjectDetailsHandler.fixProjectName(proposedName)
|
||||
const projectName = await ProjectDetailsHandler.promises.generateUniqueName(
|
||||
ownerId,
|
||||
fixedProjectName
|
||||
)
|
||||
|
||||
const project = await ProjectCreationHandler.promises.createBlankProject(
|
||||
ownerId,
|
||||
projectName,
|
||||
attributes
|
||||
)
|
||||
|
||||
try {
|
||||
await ProjectUploadManager.promises.insertZipArchiveIntoFolder(
|
||||
ownerId,
|
||||
project._id,
|
||||
project.rootFolder[0]._id,
|
||||
zipPath
|
||||
)
|
||||
await ProjectRootDocManager.promises.setRootDocAutomatically(project._id)
|
||||
} catch (err) {
|
||||
// no need to wait for the cleanup here
|
||||
ProjectDeleter.deleteProject(project._id).catch(err =>
|
||||
logger.error(
|
||||
{ err, projectId: project._id },
|
||||
'there was an error cleaning up project after importing a zip failed'
|
||||
)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
|
||||
return project
|
||||
},
|
||||
|
||||
_insertZipContentsIntoFolder: promisify(
|
||||
ProjectUploadManager._insertZipContentsIntoFolder
|
||||
module.exports = {
|
||||
createProjectFromZipArchive: callbackify(createProjectFromZipArchive),
|
||||
createProjectFromZipArchiveWithName: callbackify(
|
||||
createProjectFromZipArchiveWithName
|
||||
),
|
||||
|
||||
insertZipArchiveIntoFolder(ownerId, projectId, folderId, zipPath) {
|
||||
return promisify(ProjectUploadManager.insertZipArchiveIntoFolder)(
|
||||
ownerId,
|
||||
projectId,
|
||||
folderId,
|
||||
zipPath
|
||||
)
|
||||
promises: {
|
||||
createProjectFromZipArchive,
|
||||
createProjectFromZipArchiveWithName
|
||||
}
|
||||
}
|
||||
|
||||
ProjectUploadManager.promises = promises
|
||||
async function createProjectFromZipArchive(ownerId, defaultName, zipPath) {
|
||||
const extractionPath = await _extractZip(zipPath)
|
||||
const {
|
||||
path,
|
||||
content
|
||||
} = await ProjectRootDocManager.promises.findRootDocFileFromDirectory(
|
||||
extractionPath
|
||||
)
|
||||
|
||||
module.exports = ProjectUploadManager
|
||||
const projectName =
|
||||
DocumentHelper.getTitleFromTexContent(content || '') || defaultName
|
||||
const uniqueName = await _generateUniqueName(ownerId, projectName)
|
||||
const project = await ProjectCreationHandler.promises.createBlankProject(
|
||||
ownerId,
|
||||
uniqueName
|
||||
)
|
||||
try {
|
||||
await _insertZipContentsIntoFolder(
|
||||
ownerId,
|
||||
project._id,
|
||||
project.rootFolder[0]._id,
|
||||
extractionPath
|
||||
)
|
||||
|
||||
if (path) {
|
||||
await ProjectRootDocManager.promises.setRootDocFromName(project._id, path)
|
||||
}
|
||||
} catch (err) {
|
||||
// no need to wait for the cleanup here
|
||||
ProjectDeleter.promises
|
||||
.deleteProject(project._id)
|
||||
.catch(err =>
|
||||
logger.error(
|
||||
{ err, projectId: project._id },
|
||||
'there was an error cleaning up project after importing a zip failed'
|
||||
)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
return project
|
||||
}
|
||||
|
||||
async function createProjectFromZipArchiveWithName(
|
||||
ownerId,
|
||||
proposedName,
|
||||
zipPath,
|
||||
attributes = {}
|
||||
) {
|
||||
const extractionPath = await _extractZip(zipPath)
|
||||
const uniqueName = await _generateUniqueName(ownerId, proposedName)
|
||||
const project = await ProjectCreationHandler.promises.createBlankProject(
|
||||
ownerId,
|
||||
uniqueName,
|
||||
attributes
|
||||
)
|
||||
|
||||
try {
|
||||
await _insertZipContentsIntoFolder(
|
||||
ownerId,
|
||||
project._id,
|
||||
project.rootFolder[0]._id,
|
||||
extractionPath
|
||||
)
|
||||
await ProjectRootDocManager.promises.setRootDocAutomatically(project._id)
|
||||
} catch (err) {
|
||||
// no need to wait for the cleanup here
|
||||
ProjectDeleter.promises
|
||||
.deleteProject(project._id)
|
||||
.catch(err =>
|
||||
logger.error(
|
||||
{ err, projectId: project._id },
|
||||
'there was an error cleaning up project after importing a zip failed'
|
||||
)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
|
||||
return project
|
||||
}
|
||||
|
||||
async function _insertZipContentsIntoFolder(
|
||||
ownerId,
|
||||
projectId,
|
||||
folderId,
|
||||
destination
|
||||
) {
|
||||
const topLevelDestination = await ArchiveManager.promises.findTopLevelDirectory(
|
||||
destination
|
||||
)
|
||||
await FileSystemImportManager.promises.addFolderContents(
|
||||
ownerId,
|
||||
projectId,
|
||||
folderId,
|
||||
topLevelDestination,
|
||||
false
|
||||
)
|
||||
await fs.remove(destination)
|
||||
}
|
||||
|
||||
async function _extractZip(zipPath) {
|
||||
const destination = path.join(
|
||||
path.dirname(zipPath),
|
||||
`${path.basename(zipPath, '.zip')}-${Date.now()}`
|
||||
)
|
||||
await ArchiveManager.promises.extractZipArchive(zipPath, destination)
|
||||
return destination
|
||||
}
|
||||
|
||||
async function _generateUniqueName(ownerId, originalName) {
|
||||
const fixedName = ProjectDetailsHandler.fixProjectName(originalName)
|
||||
const uniqueName = await ProjectDetailsHandler.promises.generateUniqueName(
|
||||
ownerId,
|
||||
fixedName
|
||||
)
|
||||
return uniqueName
|
||||
}
|
||||
|
|
|
@ -1,32 +1,20 @@
|
|||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require('sinon')
|
||||
const chai = require('chai')
|
||||
const should = chai.should()
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/Uploads/ProjectUploadManager.js'
|
||||
const { expect } = require('chai')
|
||||
const timekeeper = require('timekeeper')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
const promiseStub = val => new Promise(resolve => resolve(val))
|
||||
const failedPromiseStub = err => new Promise((resolve, reject) => reject(err))
|
||||
const MODULE_PATH =
|
||||
'../../../../app/src/Features/Uploads/ProjectUploadManager.js'
|
||||
|
||||
describe('ProjectUploadManager', function() {
|
||||
beforeEach(function() {
|
||||
this.now = Date.now()
|
||||
timekeeper.freeze(this.now)
|
||||
this.project_id = 'project-id-123'
|
||||
this.folder_id = 'folder-id-123'
|
||||
this.owner_id = 'owner-id-123'
|
||||
this.callback = sinon.stub()
|
||||
this.source = '/path/to/zip/file-name.zip'
|
||||
this.destination = '/path/to/zile/file-extracted'
|
||||
this.destination = `/path/to/zip/file-name-${this.now}`
|
||||
this.root_folder_id = this.folder_id
|
||||
this.owner_id = 'owner-id-123'
|
||||
this.name = 'Project name'
|
||||
|
@ -35,365 +23,244 @@ describe('ProjectUploadManager', function() {
|
|||
_id: this.project_id,
|
||||
rootFolder: [{ _id: this.root_folder_id }]
|
||||
}
|
||||
this.ProjectUploadManager = SandboxedModule.require(modulePath, {
|
||||
this.topLevelDestination = '/path/to/zip/file-extracted/nested'
|
||||
|
||||
this.fs = {
|
||||
remove: sinon.stub().resolves()
|
||||
}
|
||||
this.ArchiveManager = {
|
||||
promises: {
|
||||
extractZipArchive: sinon.stub().resolves(),
|
||||
findTopLevelDirectory: sinon.stub().resolves(this.topLevelDestination)
|
||||
}
|
||||
}
|
||||
this.FileSystemImportManager = {
|
||||
promises: {
|
||||
addFolderContents: sinon.stub().resolves()
|
||||
}
|
||||
}
|
||||
this.ProjectCreationHandler = {
|
||||
promises: {
|
||||
createBlankProject: sinon.stub().resolves(this.project)
|
||||
}
|
||||
}
|
||||
this.ProjectRootDocManager = {
|
||||
promises: {
|
||||
setRootDocAutomatically: sinon.stub().resolves(),
|
||||
findRootDocFileFromDirectory: sinon
|
||||
.stub()
|
||||
.resolves({ path: 'main.tex', content: this.othername }),
|
||||
setRootDocFromName: sinon.stub().resolves()
|
||||
}
|
||||
}
|
||||
this.ProjectDetailsHandler = {
|
||||
fixProjectName: sinon.stub().returnsArg(0),
|
||||
promises: {
|
||||
generateUniqueName: sinon.stub().resolves(this.othername)
|
||||
}
|
||||
}
|
||||
this.ProjectDeleter = {
|
||||
promises: {
|
||||
deleteProject: sinon.stub().resolves()
|
||||
}
|
||||
}
|
||||
this.DocumentHelper = {
|
||||
getTitleFromTexContent: sinon.stub().returns(this.othername)
|
||||
}
|
||||
|
||||
this.ProjectUploadManager = SandboxedModule.require(MODULE_PATH, {
|
||||
globals: {
|
||||
console: console
|
||||
},
|
||||
requires: {
|
||||
'./FileSystemImportManager': (this.FileSystemImportManager = {}),
|
||||
'./ArchiveManager': (this.ArchiveManager = { promises: {} }),
|
||||
'../Project/ProjectCreationHandler': (this.ProjectCreationHandler = {
|
||||
promises: {}
|
||||
}),
|
||||
'../Project/ProjectRootDocManager': (this.ProjectRootDocManager = {
|
||||
promises: {}
|
||||
}),
|
||||
'../Project/ProjectDetailsHandler': (this.ProjectDetailsHandler = {
|
||||
promises: {}
|
||||
}),
|
||||
'../Project/ProjectDeleter': (this.ProjectDeleter = {
|
||||
promises: {}
|
||||
}),
|
||||
'../Documents/DocumentHelper': (this.DocumentHelper = {}),
|
||||
rimraf: (this.rimraf = sinon.stub().callsArg(1))
|
||||
'fs-extra': this.fs,
|
||||
'./FileSystemImportManager': this.FileSystemImportManager,
|
||||
'./ArchiveManager': this.ArchiveManager,
|
||||
'../Project/ProjectCreationHandler': this.ProjectCreationHandler,
|
||||
'../Project/ProjectRootDocManager': this.ProjectRootDocManager,
|
||||
'../Project/ProjectDetailsHandler': this.ProjectDetailsHandler,
|
||||
'../Project/ProjectDeleter': this.ProjectDeleter,
|
||||
'../Documents/DocumentHelper': this.DocumentHelper
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.ArchiveManager.extractZipArchive = sinon.stub().callsArg(2)
|
||||
this.ArchiveManager.promises.extractZipArchive = sinon
|
||||
.stub()
|
||||
.returns(promiseStub())
|
||||
this.ArchiveManager.findTopLevelDirectory = sinon
|
||||
.stub()
|
||||
.callsArgWith(
|
||||
1,
|
||||
null,
|
||||
(this.topLevelDestination = '/path/to/zip/file-extracted/nested')
|
||||
)
|
||||
this.ProjectCreationHandler.promises.createBlankProject = sinon
|
||||
.stub()
|
||||
.returns(promiseStub(this.project))
|
||||
this.ProjectRootDocManager.promises.setRootDocAutomatically = sinon
|
||||
.stub()
|
||||
.returns(promiseStub())
|
||||
this.FileSystemImportManager.addFolderContents = sinon.stub().callsArg(5)
|
||||
this.ProjectRootDocManager.promises.findRootDocFileFromDirectory = sinon
|
||||
.stub()
|
||||
.returns(promiseStub({ path: 'main.tex', content: this.othername }))
|
||||
this.ProjectRootDocManager.promises.setRootDocFromName = sinon
|
||||
.stub()
|
||||
.returns(promiseStub())
|
||||
this.DocumentHelper.getTitleFromTexContent = sinon
|
||||
.stub()
|
||||
.returns(this.othername)
|
||||
return (this.ProjectDetailsHandler.fixProjectName = sinon
|
||||
.stub()
|
||||
.returnsArg(0))
|
||||
afterEach(function() {
|
||||
timekeeper.reset()
|
||||
})
|
||||
|
||||
describe('createProjectFromZipArchive', function() {
|
||||
describe('when the title can be read from the root document', function() {
|
||||
beforeEach(function(done) {
|
||||
this.ProjectUploadManager._getDestinationDirectory = sinon
|
||||
.stub()
|
||||
.returns(this.destination)
|
||||
this.ProjectDetailsHandler.promises.generateUniqueName = sinon
|
||||
.stub()
|
||||
.returns(promiseStub(this.othername))
|
||||
return this.ProjectUploadManager.createProjectFromZipArchive(
|
||||
beforeEach(async function() {
|
||||
await this.ProjectUploadManager.promises.createProjectFromZipArchive(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source,
|
||||
(err, project) => {
|
||||
this.callback(err, project)
|
||||
return done()
|
||||
}
|
||||
this.source
|
||||
)
|
||||
})
|
||||
|
||||
it('should set up the directory to extract the archive to', function() {
|
||||
this.ProjectUploadManager._getDestinationDirectory
|
||||
.calledWith(this.source)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should extract the archive', function() {
|
||||
this.ArchiveManager.promises.extractZipArchive
|
||||
.calledWith(this.source, this.destination)
|
||||
.should.equal(true)
|
||||
this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith(
|
||||
this.source,
|
||||
this.destination
|
||||
)
|
||||
})
|
||||
|
||||
it('should find the top level directory', function() {
|
||||
this.ArchiveManager.findTopLevelDirectory
|
||||
.calledWith(this.destination)
|
||||
.should.equal(true)
|
||||
this.ArchiveManager.promises.findTopLevelDirectory.should.have.been.calledWith(
|
||||
this.destination
|
||||
)
|
||||
})
|
||||
|
||||
it('should insert the extracted archive into the folder', function() {
|
||||
this.FileSystemImportManager.addFolderContents
|
||||
.calledWith(
|
||||
this.owner_id,
|
||||
this.project_id,
|
||||
this.folder_id,
|
||||
this.topLevelDestination,
|
||||
false
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should create a project owned by the owner_id', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject
|
||||
.calledWith(this.owner_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should create a project with the correct name', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject
|
||||
.calledWith(sinon.match.any, this.othername)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should read the title from the tex contents', function() {
|
||||
this.DocumentHelper.getTitleFromTexContent.called.should.equal(true)
|
||||
})
|
||||
|
||||
it('should set the root document', function() {
|
||||
this.ProjectRootDocManager.promises.setRootDocFromName
|
||||
.calledWith(this.project_id, 'main.tex')
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call the callback', function() {
|
||||
this.callback
|
||||
.calledWith(sinon.match.falsy, this.project)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should ensure the name is valid', function() {
|
||||
return this.ProjectDetailsHandler.fixProjectName.called.should.equal(
|
||||
true
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("when the root document can't be determined", function() {
|
||||
beforeEach(function(done) {
|
||||
this.ProjectRootDocManager.promises.findRootDocFileFromDirectory = sinon
|
||||
.stub()
|
||||
.returns(promiseStub())
|
||||
this.ProjectUploadManager._getDestinationDirectory = sinon
|
||||
.stub()
|
||||
.returns(this.destination)
|
||||
this.ProjectDetailsHandler.promises.generateUniqueName = sinon
|
||||
.stub()
|
||||
.returns(promiseStub(this.name))
|
||||
|
||||
return this.ProjectUploadManager.createProjectFromZipArchive(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source,
|
||||
(err, project) => {
|
||||
this.callback(err, project)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should not try to set the root doc', function() {
|
||||
this.ProjectRootDocManager.promises.setRootDocFromName.called.should.equal(
|
||||
false
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('createProjectFromZipArchiveWithName', function() {
|
||||
beforeEach(function(done) {
|
||||
this.ProjectDetailsHandler.promises.generateUniqueName = sinon
|
||||
.stub()
|
||||
.returns(promiseStub(this.name))
|
||||
this.ProjectCreationHandler.promises.createBlankProject = sinon
|
||||
.stub()
|
||||
.returns(promiseStub(this.project))
|
||||
this.ProjectUploadManager.promises.insertZipArchiveIntoFolder = sinon
|
||||
.stub()
|
||||
.returns(promiseStub())
|
||||
this.ProjectUploadManager.createProjectFromZipArchiveWithName(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source,
|
||||
(err, project) => {
|
||||
this.callback(err, project)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should create a project owned by the owner_id', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject
|
||||
.calledWith(this.owner_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should create a project with the correct name', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject
|
||||
.calledWith(sinon.match.any, this.name)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should insert the zip file contents into the root folder', function() {
|
||||
this.ProjectUploadManager.promises.insertZipArchiveIntoFolder
|
||||
.calledWith(
|
||||
this.owner_id,
|
||||
this.project_id,
|
||||
this.root_folder_id,
|
||||
this.source
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should automatically set the root doc', function() {
|
||||
this.ProjectRootDocManager.promises.setRootDocAutomatically
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call the callback', function() {
|
||||
return this.callback
|
||||
.calledWith(sinon.match.falsy, this.project)
|
||||
.should.equal(true)
|
||||
})
|
||||
describe('when inserting the zip file contents into the root folder fails', function() {
|
||||
beforeEach(function(done) {
|
||||
this.callback = sinon.stub()
|
||||
this.ProjectUploadManager.promises.insertZipArchiveIntoFolder = sinon
|
||||
.stub()
|
||||
.returns(failedPromiseStub('insert-zip-error'))
|
||||
this.ProjectDeleter.promises.deleteProject = sinon
|
||||
.stub()
|
||||
.returns(promiseStub())
|
||||
this.ProjectUploadManager.createProjectFromZipArchiveWithName(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source,
|
||||
(err, project) => {
|
||||
this.callback(err, project)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should pass an error to the callback', function() {
|
||||
return this.callback
|
||||
.calledWith('insert-zip-error', sinon.match.falsy)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should cleanup the blank project created', function() {
|
||||
return this.ProjectDeleter.promises.deleteProject
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when setting automatically the root doc fails', function() {
|
||||
beforeEach(function(done) {
|
||||
this.callback = sinon.stub()
|
||||
this.ProjectRootDocManager.promises.setRootDocAutomatically = sinon
|
||||
.stub()
|
||||
.returns(failedPromiseStub('set-root-auto-error'))
|
||||
this.ProjectDeleter.promises.deleteProject = sinon
|
||||
.stub()
|
||||
.returns(promiseStub())
|
||||
this.ProjectUploadManager.createProjectFromZipArchiveWithName(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source,
|
||||
(err, project) => {
|
||||
this.callback(err, project)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should pass an error to the callback', function() {
|
||||
return this.callback
|
||||
.calledWith('set-root-auto-error', sinon.match.falsy)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should cleanup the blank project created', function() {
|
||||
return this.ProjectDeleter.promises.deleteProject
|
||||
.calledWith(this.project_id)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('insertZipArchiveIntoFolder', function() {
|
||||
beforeEach(function(done) {
|
||||
this.ProjectUploadManager._getDestinationDirectory = sinon
|
||||
.stub()
|
||||
.returns(this.destination)
|
||||
return this.ProjectUploadManager.insertZipArchiveIntoFolder(
|
||||
this.owner_id,
|
||||
this.project_id,
|
||||
this.folder_id,
|
||||
this.source,
|
||||
err => {
|
||||
this.callback(err)
|
||||
return done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should set up the directory to extract the archive to', function() {
|
||||
this.ProjectUploadManager._getDestinationDirectory
|
||||
.calledWith(this.source)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should extract the archive', function() {
|
||||
this.ArchiveManager.extractZipArchive
|
||||
.calledWith(this.source, this.destination)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should find the top level directory', function() {
|
||||
this.ArchiveManager.findTopLevelDirectory
|
||||
.calledWith(this.destination)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should insert the extracted archive into the folder', function() {
|
||||
this.FileSystemImportManager.addFolderContents
|
||||
.calledWith(
|
||||
this.FileSystemImportManager.promises.addFolderContents.should.have.been.calledWith(
|
||||
this.owner_id,
|
||||
this.project_id,
|
||||
this.folder_id,
|
||||
this.topLevelDestination,
|
||||
false
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should create a project owned by the owner_id', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
|
||||
this.owner_id
|
||||
)
|
||||
})
|
||||
|
||||
it('should create a project with the correct name', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
|
||||
sinon.match.any,
|
||||
this.othername
|
||||
)
|
||||
})
|
||||
|
||||
it('should read the title from the tex contents', function() {
|
||||
this.DocumentHelper.getTitleFromTexContent.should.have.been.called
|
||||
})
|
||||
|
||||
it('should set the root document', function() {
|
||||
this.ProjectRootDocManager.promises.setRootDocFromName.should.have.been.calledWith(
|
||||
this.project_id,
|
||||
'main.tex'
|
||||
)
|
||||
})
|
||||
|
||||
it('should ensure the name is valid', function() {
|
||||
this.ProjectDetailsHandler.fixProjectName.should.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the callback', function() {
|
||||
this.callback.called.should.equal(true)
|
||||
})
|
||||
describe("when the root document can't be determined", function() {
|
||||
beforeEach(async function() {
|
||||
this.ProjectRootDocManager.promises.findRootDocFileFromDirectory.resolves(
|
||||
{}
|
||||
)
|
||||
await this.ProjectUploadManager.promises.createProjectFromZipArchive(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source
|
||||
)
|
||||
})
|
||||
|
||||
it('should remove the desintation directory afterwards', function() {
|
||||
this.rimraf.calledWith(this.destination).should.equal(true)
|
||||
it('should not try to set the root doc', function() {
|
||||
this.ProjectRootDocManager.promises.setRootDocFromName.should.not.have
|
||||
.been.called
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('_getDestinationDirectory', function() {
|
||||
it('should return the path with the time appended', function() {
|
||||
const date = Date.now()
|
||||
sinon.stub(Date, 'now').returns(date)
|
||||
this.ProjectUploadManager._getDestinationDirectory(
|
||||
'/path/to/zip/file.zip'
|
||||
).should.equal(`/path/to/zip/file-${date}`)
|
||||
Date.now.restore()
|
||||
describe('createProjectFromZipArchiveWithName', function() {
|
||||
beforeEach(async function() {
|
||||
await this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source
|
||||
)
|
||||
})
|
||||
|
||||
it('should create a project owned by the owner_id', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
|
||||
this.owner_id
|
||||
)
|
||||
})
|
||||
|
||||
it('should create a project with the correct name', function() {
|
||||
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
|
||||
sinon.match.any,
|
||||
this.othername
|
||||
)
|
||||
})
|
||||
|
||||
it('should automatically set the root doc', function() {
|
||||
this.ProjectRootDocManager.promises.setRootDocAutomatically.should.have.been.calledWith(
|
||||
this.project_id
|
||||
)
|
||||
})
|
||||
|
||||
it('should extract the archive', function() {
|
||||
this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith(
|
||||
this.source,
|
||||
this.destination
|
||||
)
|
||||
})
|
||||
|
||||
it('should find the top level directory', function() {
|
||||
this.ArchiveManager.promises.findTopLevelDirectory.should.have.been.calledWith(
|
||||
this.destination
|
||||
)
|
||||
})
|
||||
|
||||
it('should insert the extracted archive into the folder', function() {
|
||||
this.FileSystemImportManager.promises.addFolderContents.should.have.been.calledWith(
|
||||
this.owner_id,
|
||||
this.project_id,
|
||||
this.folder_id,
|
||||
this.topLevelDestination,
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('should remove the destination directory afterwards', function() {
|
||||
this.fs.remove.should.have.been.calledWith(this.destination)
|
||||
})
|
||||
|
||||
describe('when inserting the zip file contents into the root folder fails', function() {
|
||||
beforeEach(async function() {
|
||||
this.FileSystemImportManager.promises.addFolderContents.rejects()
|
||||
await expect(
|
||||
this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
|
||||
this.owner_id,
|
||||
this.name,
|
||||
this.source
|
||||
)
|
||||
).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.owner_id,
|
||||
this.name,
|
||||
this.source
|
||||
)
|
||||
).to.be.rejected
|
||||
})
|
||||
|
||||
it('should cleanup the blank project created', function() {
|
||||
this.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
|
||||
this.project_id
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue