diff --git a/services/web/app/src/Features/Uploads/ArchiveManager.js b/services/web/app/src/Features/Uploads/ArchiveManager.js index ad5819fe71..0c12c4a802 100644 --- a/services/web/app/src/Features/Uploads/ArchiveManager.js +++ b/services/web/app/src/Features/Uploads/ArchiveManager.js @@ -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 diff --git a/services/web/app/src/Features/Uploads/ProjectUploadManager.js b/services/web/app/src/Features/Uploads/ProjectUploadManager.js index 3a22f62627..4c2fd6e407 100644 --- a/services/web/app/src/Features/Uploads/ProjectUploadManager.js +++ b/services/web/app/src/Features/Uploads/ProjectUploadManager.js @@ -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 +} diff --git a/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js b/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js index ab5b997a22..bca4338410 100644 --- a/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js +++ b/services/web/test/unit/src/Uploads/ProjectUploadManagerTests.js @@ -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 + ) + }) }) }) })