| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- 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,
- '@overleaf/settings': {
- path: {
- outputDir: this.outputDir,
- },
- },
- },
- })
- })
- describe('when the output cache directory contains only exportable 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 the directory includes files ignored by web', function () {
- beforeEach(async function () {
- this.OutputFileFinder.promises.findOutputFiles.resolves({
- outputFiles: [
- { path: 'file_1' },
- { path: 'file_2' },
- { path: 'file_3' },
- { path: 'file_4' },
- { path: 'output.pdf' },
- ],
- })
- await this.OutputFileArchiveManager.archiveFilesForBuild(
- projectId,
- userId,
- buildId
- )
- })
- it('only includes the non-ignored files in 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' })
- )
- })
- })
- describe('when one of the files is called output.pdf', function () {
- beforeEach(async function () {
- this.OutputFileFinder.promises.findOutputFiles.resolves({
- outputFiles: [
- { path: 'file_1' },
- { path: 'file_2' },
- { path: 'file_3' },
- { path: 'file_4' },
- { path: 'output.pdf' },
- ],
- })
- await this.OutputFileArchiveManager.archiveFilesForBuild(
- projectId,
- userId,
- buildId
- )
- })
- it('does not include that file in 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' })
- )
- })
- })
- 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
- })
- })
- })
|