mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
92f62f91c1
[clsi] Add endpoints to get zip of output files GitOrigin-RevId: a1a935e8170ab5a8d40baa6d96f8e42fe22c2e8c
234 lines
6.5 KiB
JavaScript
234 lines
6.5 KiB
JavaScript
const SandboxedModule = require('sandboxed-module')
|
|
const sinon = require('sinon')
|
|
const { assert, expect } = require('chai')
|
|
|
|
const MODULE_PATH = require('path').join(
|
|
__dirname,
|
|
'../../../app/js/OutputFileArchiveManager'
|
|
)
|
|
|
|
describe('OutputFileArchiveManager', function () {
|
|
const userId = 'user-id-123'
|
|
const projectId = 'project-id-123'
|
|
const buildId = 'build-id-123'
|
|
|
|
afterEach(function () {
|
|
sinon.restore()
|
|
})
|
|
|
|
beforeEach(function () {
|
|
this.OutputFileFinder = {
|
|
promises: {
|
|
findOutputFiles: sinon.stub().resolves({ outputFiles: [] }),
|
|
},
|
|
}
|
|
|
|
this.OutputCacheManger = {
|
|
path: sinon.stub().callsFake((build, path) => {
|
|
return `${build}/${path}`
|
|
}),
|
|
}
|
|
|
|
this.archive = {
|
|
append: sinon.stub(),
|
|
finalize: sinon.stub(),
|
|
}
|
|
|
|
this.archiver = sinon.stub().returns(this.archive)
|
|
|
|
this.outputDir = '/output/dir'
|
|
|
|
this.fs = {
|
|
open: sinon.stub().callsFake(file => ({
|
|
createReadStream: sinon.stub().returns(`handle: ${file}`),
|
|
})),
|
|
}
|
|
|
|
this.OutputFileArchiveManager = SandboxedModule.require(MODULE_PATH, {
|
|
requires: {
|
|
'./OutputFileFinder': this.OutputFileFinder,
|
|
'./OutputCacheManager': this.OutputCacheManger,
|
|
archiver: this.archiver,
|
|
'node:fs/promises': this.fs,
|
|
'node:path': {
|
|
basename: sinon.stub().callsFake(path => path.split('/').pop()),
|
|
},
|
|
'@overleaf/settings': {
|
|
path: {
|
|
outputDir: this.outputDir,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
})
|
|
|
|
describe('when called with no files', function () {
|
|
beforeEach(async function () {
|
|
this.OutputFileFinder.promises.findOutputFiles.resolves({
|
|
outputFiles: [
|
|
{ path: 'file_1' },
|
|
{ path: 'file_2' },
|
|
{ path: 'file_3' },
|
|
{ path: 'file_4' },
|
|
],
|
|
})
|
|
await this.OutputFileArchiveManager.archiveFilesForBuild(
|
|
projectId,
|
|
userId,
|
|
buildId
|
|
)
|
|
})
|
|
|
|
it('creates a zip archive', function () {
|
|
sinon.assert.calledWith(this.archiver, 'zip')
|
|
})
|
|
|
|
it('adds all the output files to the archive', function () {
|
|
expect(this.archive.append.callCount).to.equal(4)
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
`handle: ${this.outputDir}/${projectId}-${userId}/${buildId}/file_1`,
|
|
sinon.match({ name: 'file_1' })
|
|
)
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
`handle: ${this.outputDir}/${projectId}-${userId}/${buildId}/file_2`,
|
|
sinon.match({ name: 'file_2' })
|
|
)
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
`handle: ${this.outputDir}/${projectId}-${userId}/${buildId}/file_3`,
|
|
sinon.match({ name: 'file_3' })
|
|
)
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
`handle: ${this.outputDir}/${projectId}-${userId}/${buildId}/file_4`,
|
|
sinon.match({ name: 'file_4' })
|
|
)
|
|
})
|
|
|
|
it('finalizes the archive after all files are appended', function () {
|
|
sinon.assert.called(this.archive.finalize)
|
|
expect(this.archive.finalize.calledBefore(this.archive.append)).to.be
|
|
.false
|
|
})
|
|
})
|
|
|
|
describe('when called with a list of files that all are in the output directory', function () {
|
|
beforeEach(async function () {
|
|
this.OutputFileFinder.promises.findOutputFiles.resolves({
|
|
outputFiles: [
|
|
{ path: 'file_1' },
|
|
{ path: 'file_2' },
|
|
{ path: 'file_3' },
|
|
{ path: 'file_4' },
|
|
],
|
|
})
|
|
await this.OutputFileArchiveManager.archiveFilesForBuild(
|
|
projectId,
|
|
userId,
|
|
buildId,
|
|
['file_1', 'file_4']
|
|
)
|
|
})
|
|
|
|
it('creates a zip archive', function () {
|
|
sinon.assert.calledWith(this.archiver, 'zip')
|
|
})
|
|
|
|
it('adds only output files from the list of files to the archive', function () {
|
|
expect(this.archive.append.callCount).to.equal(2)
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
`handle: ${this.outputDir}/${projectId}-${userId}/${buildId}/file_1`,
|
|
sinon.match({
|
|
name: 'file_1',
|
|
})
|
|
)
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
`handle: ${this.outputDir}/${projectId}-${userId}/${buildId}/file_4`,
|
|
sinon.match({
|
|
name: 'file_4',
|
|
})
|
|
)
|
|
})
|
|
|
|
it('finalizes the archive after all files are appended', function () {
|
|
sinon.assert.called(this.archive.finalize)
|
|
expect(this.archive.finalize.calledBefore(this.archive.append)).to.be
|
|
.false
|
|
})
|
|
})
|
|
|
|
describe('when called with a list of files and one of the files is missing from the output directory', function () {
|
|
beforeEach(async function () {
|
|
this.OutputFileFinder.promises.findOutputFiles.resolves({
|
|
outputFiles: [
|
|
{ path: 'file_1' },
|
|
{ path: 'file_2' },
|
|
{ path: 'file_3' },
|
|
],
|
|
})
|
|
await this.OutputFileArchiveManager.archiveFilesForBuild(
|
|
projectId,
|
|
userId,
|
|
buildId,
|
|
['file_1', 'file_4']
|
|
)
|
|
})
|
|
|
|
it('creates a zip archive', function () {
|
|
sinon.assert.calledWith(this.archiver, 'zip')
|
|
})
|
|
|
|
it('adds the files that were found to the archive', function () {
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
`handle: ${this.outputDir}/${projectId}-${userId}/${buildId}/file_1`,
|
|
sinon.match({ name: 'file_1' })
|
|
)
|
|
})
|
|
|
|
it('adds a file listing any missing files', function () {
|
|
sinon.assert.calledWith(
|
|
this.archive.append,
|
|
'file_4',
|
|
sinon.match({
|
|
name: 'missing_files.txt',
|
|
})
|
|
)
|
|
})
|
|
|
|
it('finalizes the archive after all files are appended', function () {
|
|
sinon.assert.called(this.archive.finalize)
|
|
expect(this.archive.finalize.calledBefore(this.archive.append)).to.be
|
|
.false
|
|
})
|
|
})
|
|
|
|
describe('when the output directory cannot be accessed', function () {
|
|
beforeEach(async function () {
|
|
this.OutputFileFinder.promises.findOutputFiles.rejects({
|
|
code: 'ENOENT',
|
|
})
|
|
})
|
|
|
|
it('rejects with a NotFoundError', async function () {
|
|
try {
|
|
await this.OutputFileArchiveManager.archiveFilesForBuild(
|
|
projectId,
|
|
userId,
|
|
buildId
|
|
)
|
|
assert.fail('should have thrown a NotFoundError')
|
|
} catch (err) {
|
|
expect(err).to.haveOwnProperty('name', 'NotFoundError')
|
|
}
|
|
})
|
|
|
|
it('does not create an archive', function () {
|
|
expect(this.archiver.called).to.be.false
|
|
})
|
|
})
|
|
})
|