diff --git a/libraries/object-persistor/package.json b/libraries/object-persistor/package.json index df7c5ea46d..2378878bd1 100644 --- a/libraries/object-persistor/package.json +++ b/libraries/object-persistor/package.json @@ -38,7 +38,6 @@ "mocha": "^11.1.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", - "mock-fs": "^5.2.0", "mongodb": "6.12.0", "sandboxed-module": "^2.0.4", "sinon": "^9.2.4", diff --git a/libraries/object-persistor/src/FSPersistor.js b/libraries/object-persistor/src/FSPersistor.js index df0cca68a4..289a0d7568 100644 --- a/libraries/object-persistor/src/FSPersistor.js +++ b/libraries/object-persistor/src/FSPersistor.js @@ -3,9 +3,12 @@ const fs = require('node:fs') const fsPromises = require('node:fs/promises') const { glob } = require('glob') const Path = require('node:path') +const { promisify } = require('node:util') const { PassThrough } = require('node:stream') const { pipeline } = require('node:stream/promises') +const openCb = promisify(fs.open) + const AbstractPersistor = require('./AbstractPersistor') const { ReadError, WriteError, NotImplementedError } = require('./Errors') const PersistorHelper = require('./PersistorHelper') @@ -85,8 +88,9 @@ module.exports = class FSPersistor extends AbstractPersistor { }) const fsPath = this._getFsPath(location, name, opts.useSubdirectories) + let fd try { - opts.fd = await fsPromises.open(fsPath, 'r') + fd = await openCb(fsPath, 'r') } catch (err) { throw PersistorHelper.wrapError( err, @@ -96,7 +100,7 @@ module.exports = class FSPersistor extends AbstractPersistor { ) } - const stream = fs.createReadStream(null, opts) + const stream = fs.createReadStream(null, { ...opts, fd }) // Return a PassThrough stream with a minimal interface. It will buffer until the caller starts reading. It will emit errors from the source stream (Stream.pipeline passes errors along). const pass = new PassThrough() pipeline(stream, observer, pass).catch(() => {}) diff --git a/libraries/object-persistor/test/unit/FSPersistorTests.js b/libraries/object-persistor/test/unit/FSPersistorTests.js index 386878930c..0a29f20d34 100644 --- a/libraries/object-persistor/test/unit/FSPersistorTests.js +++ b/libraries/object-persistor/test/unit/FSPersistorTests.js @@ -1,6 +1,6 @@ const crypto = require('node:crypto') +const os = require('node:os') const { expect } = require('chai') -const mockFs = require('mock-fs') const fs = require('node:fs') const fsPromises = require('node:fs/promises') const Path = require('node:path') @@ -10,22 +10,59 @@ const Errors = require('../../src/Errors') const MODULE_PATH = '../../src/FSPersistor.js' +function createTree(base, tree) { + fs.mkdirSync(base, { recursive: true }) + for (const [name, content] of Object.entries(tree)) { + const fullPath = Path.join(base, name) + if (Buffer.isBuffer(content) || typeof content === 'string') { + fs.writeFileSync(fullPath, content) + } else if (content && typeof content.symlink === 'string') { + fs.symlinkSync(content.symlink, fullPath) + } else { + createTree(fullPath, content) + } + } +} + describe('FSPersistorTests', function () { - const localFiles = { - '/uploads/info.txt': Buffer.from('This information is critical', { + const fileContents = { + 'info.txt': Buffer.from('This information is critical', { encoding: 'utf-8', }), - '/uploads/other.txt': Buffer.from('Some other content', { + 'other.txt': Buffer.from('Some other content', { encoding: 'utf-8', }), } - const location = '/bucket' + let tmpDir + let location + let notADirPath const files = { wombat: 'animals/wombat.tex', giraffe: 'animals/giraffe.tex', potato: 'vegetables/potato.tex', } + beforeEach(function () { + tmpDir = fs.mkdtempSync(Path.join(os.tmpdir(), 'fs-persistor-test-')) + createTree(tmpDir, { + uploads: { + 'info.txt': fileContents['info.txt'], + 'other.txt': fileContents['other.txt'], + }, + 'not-a-dir': + 'This regular file is meant to prevent using this path as a directory', + directory: { + subdirectory: {}, + }, + }) + notADirPath = Path.join(tmpDir, 'not-a-dir') + location = Path.join(tmpDir, 'bucket') + }) + + afterEach(function () { + fs.rmSync(tmpDir, { recursive: true }) + }) + const scenarios = [ { description: 'default settings', @@ -54,31 +91,26 @@ describe('FSPersistorTests', function () { persistor = new FSPersistor(scenario.settings) }) - beforeEach(function () { - mockFs({ - ...localFiles, - '/not-a-dir': - 'This regular file is meant to prevent using this path as a directory', - '/directory/subdirectory': {}, - }) - }) - - afterEach(function () { - mockFs.restore() - }) - describe('sendFile', function () { it('should copy the file', async function () { - await persistor.sendFile(location, files.wombat, '/uploads/info.txt') + await persistor.sendFile( + location, + files.wombat, + Path.join(tmpDir, 'uploads', 'info.txt') + ) const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect(contents.equals(localFiles['/uploads/info.txt'])).to.be.true + expect(contents.equals(fileContents['info.txt'])).to.be.true }) it('should return an error if the file cannot be stored', async function () { await expect( - persistor.sendFile('/not-a-dir', files.wombat, '/uploads/info.txt') + persistor.sendFile( + notADirPath, + files.wombat, + Path.join(tmpDir, 'uploads', 'info.txt') + ) ).to.be.rejectedWith(Errors.WriteError) }) }) @@ -88,7 +120,9 @@ describe('FSPersistorTests', function () { describe("when the file doesn't exist", function () { beforeEach(function () { - stream = fs.createReadStream('/uploads/info.txt') + stream = fs.createReadStream( + Path.join(tmpDir, 'uploads', 'info.txt') + ) }) it('should write the stream to disk', async function () { @@ -96,7 +130,7 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect(contents.equals(localFiles['/uploads/info.txt'])).to.be.true + expect(contents.equals(fileContents['info.txt'])).to.be.true }) it('should delete the temporary file', async function () { @@ -109,7 +143,7 @@ describe('FSPersistorTests', function () { describe('on error', function () { beforeEach(async function () { await expect( - persistor.sendStream('/not-a-dir', files.wombat, stream) + persistor.sendStream(notADirPath, files.wombat, stream) ).to.be.rejectedWith(Errors.WriteError) }) @@ -129,13 +163,12 @@ describe('FSPersistorTests', function () { describe('when the md5 hash matches', function () { it('should write the stream to disk', async function () { await persistor.sendStream(location, files.wombat, stream, { - sourceMd5: md5(localFiles['/uploads/info.txt']), + sourceMd5: md5(fileContents['info.txt']), }) const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect(contents.equals(localFiles['/uploads/info.txt'])).to.be - .true + expect(contents.equals(fileContents['info.txt'])).to.be.true }) }) @@ -169,9 +202,11 @@ describe('FSPersistorTests', function () { await persistor.sendFile( location, files.wombat, - '/uploads/info.txt' + Path.join(tmpDir, 'uploads', 'info.txt') + ) + stream = fs.createReadStream( + Path.join(tmpDir, 'uploads', 'other.txt') ) - stream = fs.createReadStream('/uploads/other.txt') }) it('should write the stream to disk', async function () { @@ -179,7 +214,7 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect(contents.equals(localFiles['/uploads/other.txt'])).to.be.true + expect(contents.equals(fileContents['other.txt'])).to.be.true }) it('should delete the temporary file', async function () { @@ -192,7 +227,7 @@ describe('FSPersistorTests', function () { describe('on error', function () { beforeEach(async function () { await expect( - persistor.sendStream('/not-a-dir', files.wombat, stream) + persistor.sendStream(notADirPath, files.wombat, stream) ).to.be.rejectedWith(Errors.WriteError) }) @@ -200,8 +235,7 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect(contents.equals(localFiles['/uploads/info.txt'])).to.be - .true + expect(contents.equals(fileContents['info.txt'])).to.be.true }) it('should delete the temporary file', async function () { @@ -215,13 +249,12 @@ describe('FSPersistorTests', function () { describe('when the md5 hash matches', function () { it('should write the stream to disk', async function () { await persistor.sendStream(location, files.wombat, stream, { - sourceMd5: md5(localFiles['/uploads/other.txt']), + sourceMd5: md5(fileContents['other.txt']), }) const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect(contents.equals(localFiles['/uploads/other.txt'])).to.be - .true + expect(contents.equals(fileContents['other.txt'])).to.be.true }) }) @@ -238,8 +271,7 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.wombat) ) - expect(contents.equals(localFiles['/uploads/info.txt'])).to.be - .true + expect(contents.equals(fileContents['info.txt'])).to.be.true }) it('should delete the temporary file', async function () { @@ -254,13 +286,17 @@ describe('FSPersistorTests', function () { describe('getObjectStream', function () { beforeEach(async function () { - await persistor.sendFile(location, files.wombat, '/uploads/info.txt') + await persistor.sendFile( + location, + files.wombat, + Path.join(tmpDir, 'uploads', 'info.txt') + ) }) it('should return a string with the object contents', async function () { const stream = await persistor.getObjectStream(location, files.wombat) const contents = await streamToBuffer(stream) - expect(contents.equals(localFiles['/uploads/info.txt'])).to.be.true + expect(contents.equals(fileContents['info.txt'])).to.be.true }) it('should support ranges', async function () { @@ -274,8 +310,8 @@ describe('FSPersistorTests', function () { ) const contents = await streamToBuffer(stream) // end is inclusive in ranges, but exclusive in slice() - expect(contents.equals(localFiles['/uploads/info.txt'].slice(5, 17))) - .to.be.true + expect(contents.equals(fileContents['info.txt'].slice(5, 17))).to.be + .true }) it('should give a NotFoundError if the file does not exist', async function () { @@ -287,13 +323,17 @@ describe('FSPersistorTests', function () { describe('getObjectSize', function () { beforeEach(async function () { - await persistor.sendFile(location, files.wombat, '/uploads/info.txt') + await persistor.sendFile( + location, + files.wombat, + Path.join(tmpDir, 'uploads', 'info.txt') + ) }) it('should return the file size', async function () { expect( await persistor.getObjectSize(location, files.wombat) - ).to.equal(localFiles['/uploads/info.txt'].length) + ).to.equal(fileContents['info.txt'].length) }) it('should throw a NotFoundError if the file does not exist', async function () { @@ -305,7 +345,11 @@ describe('FSPersistorTests', function () { describe('copyObject', function () { beforeEach(async function () { - await persistor.sendFile(location, files.wombat, '/uploads/info.txt') + await persistor.sendFile( + location, + files.wombat, + Path.join(tmpDir, 'uploads', 'info.txt') + ) }) it('Should copy the file to the new location', async function () { @@ -313,13 +357,17 @@ describe('FSPersistorTests', function () { const contents = await fsPromises.readFile( scenario.fsPath(files.potato) ) - expect(contents.equals(localFiles['/uploads/info.txt'])).to.be.true + expect(contents.equals(fileContents['info.txt'])).to.be.true }) }) describe('deleteObject', function () { beforeEach(async function () { - await persistor.sendFile(location, files.wombat, '/uploads/info.txt') + await persistor.sendFile( + location, + files.wombat, + Path.join(tmpDir, 'uploads', 'info.txt') + ) await fsPromises.access(scenario.fsPath(files.wombat)) }) @@ -337,7 +385,11 @@ describe('FSPersistorTests', function () { describe('deleteDirectory', function () { beforeEach(async function () { for (const file of Object.values(files)) { - await persistor.sendFile(location, file, '/uploads/info.txt') + await persistor.sendFile( + location, + file, + Path.join(tmpDir, 'uploads', 'info.txt') + ) await fsPromises.access(scenario.fsPath(file)) } }) @@ -365,7 +417,11 @@ describe('FSPersistorTests', function () { describe('checkIfObjectExists', function () { beforeEach(async function () { - await persistor.sendFile(location, files.wombat, '/uploads/info.txt') + await persistor.sendFile( + location, + files.wombat, + Path.join(tmpDir, 'uploads', 'info.txt') + ) }) it('should return true for existing files', async function () { @@ -384,13 +440,17 @@ describe('FSPersistorTests', function () { describe('directorySize', function () { beforeEach(async function () { for (const file of Object.values(files)) { - await persistor.sendFile(location, file, '/uploads/info.txt') + await persistor.sendFile( + location, + file, + Path.join(tmpDir, 'uploads', 'info.txt') + ) } }) it('should sum directory files size', async function () { expect(await persistor.directorySize(location, 'animals')).to.equal( - 2 * localFiles['/uploads/info.txt'].length + 2 * fileContents['info.txt'].length ) }) @@ -404,7 +464,11 @@ describe('FSPersistorTests', function () { describe('listDirectoryKeys', function () { beforeEach(async function () { for (const file of Object.values(files)) { - await persistor.sendFile(location, file, '/uploads/info.txt') + await persistor.sendFile( + location, + file, + Path.join(tmpDir, 'uploads', 'info.txt') + ) } }) @@ -427,7 +491,11 @@ describe('FSPersistorTests', function () { describe('listDirectoryStats', function () { beforeEach(async function () { for (const file of Object.values(files)) { - await persistor.sendFile(location, file, '/uploads/info.txt') + await persistor.sendFile( + location, + file, + Path.join(tmpDir, 'uploads', 'info.txt') + ) } }) @@ -438,7 +506,7 @@ describe('FSPersistorTests', function () { expect(keys).to.include(scenario.fsPath(files.wombat)) expect(keys).to.include(scenario.fsPath(files.giraffe)) for (const stat of stats) { - expect(stat.size).to.equal(localFiles['/uploads/info.txt'].length) + expect(stat.size).to.equal(fileContents['info.txt'].length) } }) diff --git a/package.json b/package.json index f91aa1670f..725dc4c5cd 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,6 @@ "knip": "5.64.1", "eslint-plugin-testing-library": "7.5.3", "chart.js": "4.0.1", - "mock-fs": "5.2.0", "@customerio/cdp-analytics-node": "0.3.9", "@google-cloud/bigquery": "8.1.1", "moment": "2.29.4", diff --git a/services/clsi/package.json b/services/clsi/package.json index c9abae6c72..908f35e70b 100644 --- a/services/clsi/package.json +++ b/services/clsi/package.json @@ -46,7 +46,6 @@ "mocha": "^11.1.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", - "mock-fs": "^5.1.2", "node-fetch": "^2.7.0", "nyc": "^17.1.0", "sinon": "~9.0.1", diff --git a/services/clsi/test/unit/js/DraftModeManager.test.js b/services/clsi/test/unit/js/DraftModeManager.test.js index 0e8755b26d..7f2ed6373d 100644 --- a/services/clsi/test/unit/js/DraftModeManager.test.js +++ b/services/clsi/test/unit/js/DraftModeManager.test.js @@ -1,7 +1,8 @@ import { vi, expect, describe, beforeEach, afterEach, it } from 'vitest' import Path from 'node:path' +import fs from 'node:fs' import fsPromises from 'node:fs/promises' -import mockFs from 'mock-fs' +import os from 'node:os' const MODULE_PATH = Path.join( import.meta.dirname, @@ -15,20 +16,19 @@ describe('DraftModeManager', () => { })) ctx.DraftModeManager = (await import(MODULE_PATH)).default - ctx.filename = '/mock/filename.tex' + ctx.tmpDir = fs.mkdtempSync(Path.join(os.tmpdir(), 'draft-mode-test-')) + ctx.filename = Path.join(ctx.tmpDir, 'filename.tex') ctx.contents = `\ \\documentclass{article} \\begin{document} Hello world \\end{document}\ ` - mockFs({ - [ctx.filename]: ctx.contents, - }) + fs.writeFileSync(ctx.filename, ctx.contents) }) - afterEach(() => { - mockFs.restore() + afterEach(ctx => { + fs.rmSync(ctx.tmpDir, { recursive: true }) }) describe('injectDraftMode', () => { diff --git a/services/clsi/test/unit/js/OutputFileFinder.test.js b/services/clsi/test/unit/js/OutputFileFinder.test.js index 466f150812..39bd6e6ea4 100644 --- a/services/clsi/test/unit/js/OutputFileFinder.test.js +++ b/services/clsi/test/unit/js/OutputFileFinder.test.js @@ -1,6 +1,6 @@ -import sinon from 'sinon' import { expect, describe, beforeEach, afterEach, it } from 'vitest' -import mockFs from 'mock-fs' +import fs from 'node:fs' +import os from 'node:os' import path from 'node:path' const modulePath = path.join( @@ -8,30 +8,40 @@ const modulePath = path.join( '../../../app/js/OutputFileFinder' ) +function createTree(base, tree) { + fs.mkdirSync(base, { recursive: true }) + for (const [name, content] of Object.entries(tree)) { + const fullPath = path.join(base, name) + if (Buffer.isBuffer(content) || typeof content === 'string') { + fs.writeFileSync(fullPath, content) + } else if (content && content.symlink) { + fs.symlinkSync(content.symlink, fullPath) + } else { + createTree(fullPath, content) + } + } +} + describe('OutputFileFinder', function () { beforeEach(async function (ctx) { ctx.OutputFileFinder = (await import(modulePath)).default - ctx.directory = '/test/dir' - ctx.callback = sinon.stub() - - mockFs({ - [ctx.directory]: { - resource: { - 'path.tex': 'a source file', - }, - 'output.pdf': 'a generated pdf file', - extra: { - 'file.tex': 'a generated tex file', - }, - 'sneaky-file': mockFs.symlink({ - path: '../foo', - }), + ctx.directory = fs.mkdtempSync( + path.join(os.tmpdir(), 'output-finder-test-') + ) + createTree(ctx.directory, { + resource: { + 'path.tex': 'a source file', }, + 'output.pdf': 'a generated pdf file', + extra: { + 'file.tex': 'a generated tex file', + }, + 'sneaky-file': { symlink: '../foo' }, }) }) - afterEach(function () { - mockFs.restore() + afterEach(function (ctx) { + fs.rmSync(ctx.directory, { recursive: true }) }) describe('findOutputFiles', function () { diff --git a/services/web/package.json b/services/web/package.json index 3f148f87cd..7ae7a2ba51 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -370,7 +370,6 @@ "mocha-each": "^2.0.1", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", - "mock-fs": "^5.1.2", "nock": "^13.5.6", "nvd3": "^1.8.6", "nyc": "^17.1.0", diff --git a/services/web/test/unit/src/Uploads/FileSystemImportManager.test.mjs b/services/web/test/unit/src/Uploads/FileSystemImportManager.test.mjs index 7f9f90c4ae..50288130ff 100644 --- a/services/web/test/unit/src/Uploads/FileSystemImportManager.test.mjs +++ b/services/web/test/unit/src/Uploads/FileSystemImportManager.test.mjs @@ -1,6 +1,8 @@ import { vi, expect } from 'vitest' import sinon from 'sinon' -import mockFs from 'mock-fs' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' import mongodb from 'mongodb-legacy' import Settings from '@overleaf/settings' @@ -9,6 +11,20 @@ const { ObjectId } = mongodb const MODULE_PATH = '../../../../app/src/Features/Uploads/FileSystemImportManager.mjs' +function createTree(base, tree) { + fs.mkdirSync(base, { recursive: true }) + for (const [name, content] of Object.entries(tree)) { + const fullPath = path.join(base, name) + if (Buffer.isBuffer(content) || typeof content === 'string') { + fs.writeFileSync(fullPath, content) + } else if (content && typeof content.symlink === 'string') { + fs.symlinkSync(content.symlink, fullPath) + } else { + createTree(fullPath, content) + } + } +} + describe('FileSystemImportManager', function () { beforeEach(async function (ctx) { ctx.projectId = new ObjectId() @@ -48,14 +64,16 @@ describe('FileSystemImportManager', function () { describe('importDir', function () { beforeEach(async function (ctx) { - mockFs({ + ctx.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'import-test-')) + ctx.importPath = path.join(ctx.tmpDir, 'import-test') + createTree(ctx.tmpDir, { 'import-test': { 'main.tex': 'My thesis', - 'link-to-main.tex': mockFs.symlink({ path: 'import-test/main.tex' }), - '.DS_Store': 'Should be ignored', - images: { - 'cat.jpg': Buffer.from([1, 2, 3, 4]), + 'link-to-main.tex': { + symlink: path.join(ctx.tmpDir, 'import-test', 'main.tex'), }, + '.DS_Store': 'Should be ignored', + images: { 'cat.jpg': Buffer.from([1, 2, 3, 4]) }, 'line-endings': { 'unix.txt': 'one\ntwo\nthree', 'mac.txt': 'uno\rdos\rtres', @@ -67,15 +85,16 @@ describe('FileSystemImportManager', function () { 'latin1.txt': Buffer.from('tétanisant!', 'latin1'), }, }, - symlink: mockFs.symlink({ path: 'import-test' }), + symlink: { symlink: path.join(ctx.tmpDir, 'import-test') }, }) - ctx.entries = - await ctx.FileSystemImportManager.promises.importDir('import-test') + ctx.entries = await ctx.FileSystemImportManager.promises.importDir( + ctx.importPath + ) ctx.projectPaths = ctx.entries.map(x => x.projectPath) }) - afterEach(function () { - mockFs.restore() + afterEach(function (ctx) { + fs.rmSync(ctx.tmpDir, { recursive: true, force: true }) }) it('should import regular docs', function (ctx) { @@ -98,7 +117,7 @@ describe('FileSystemImportManager', function () { expect(ctx.entries).to.deep.include({ type: 'file', projectPath: '/images/cat.jpg', - fsPath: 'import-test/images/cat.jpg', + fsPath: path.join(ctx.importPath, 'images', 'cat.jpg'), }) }) @@ -142,15 +161,20 @@ describe('FileSystemImportManager', function () { }) it('should error when the root folder is a symlink', async function (ctx) { - await expect(ctx.FileSystemImportManager.promises.importDir('symlink')).to - .be.rejected + await expect( + ctx.FileSystemImportManager.promises.importDir( + path.join(ctx.tmpDir, 'symlink') + ) + ).to.be.rejected }) }) describe('addEntity', function () { describe('with directory', function () { beforeEach(async function (ctx) { - mockFs({ + ctx.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'addentity-dir-')) + ctx.fsPath = path.join(ctx.tmpDir, 'path', 'to', 'folder') + createTree(ctx.tmpDir, { path: { to: { folder: { @@ -160,19 +184,18 @@ describe('FileSystemImportManager', function () { }, }, }) - await ctx.FileSystemImportManager.promises.addEntity( ctx.userId, ctx.projectId, ctx.folderId, 'folder', - 'path/to/folder', + ctx.fsPath, false ) }) - afterEach(function () { - mockFs.restore() + afterEach(function (ctx) { + fs.rmSync(ctx.tmpDir, { recursive: true, force: true }) }) it('should add a folder to the project', function (ctx) { @@ -197,7 +220,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.newFolderId, 'image.jpg', - 'path/to/folder/image.jpg', + path.join(ctx.fsPath, 'image.jpg'), null, 'upload', ctx.userId @@ -206,12 +229,16 @@ describe('FileSystemImportManager', function () { }) describe('with binary file', function () { - beforeEach(function () { - mockFs({ 'uploaded-file': Buffer.from([1, 2, 3, 4]) }) + beforeEach(function (ctx) { + ctx.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'addentity-bin-')) + ctx.fsPath = path.join(ctx.tmpDir, 'uploaded-file') + createTree(ctx.tmpDir, { + 'uploaded-file': Buffer.from([1, 2, 3, 4]), + }) }) - afterEach(function () { - mockFs.restore() + afterEach(function (ctx) { + fs.rmSync(ctx.tmpDir, { recursive: true, force: true }) }) describe('with replace set to false', function () { @@ -221,7 +248,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.folderId, 'image.jpg', - 'uploaded-file', + ctx.fsPath, false ) }) @@ -231,7 +258,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.folderId, 'image.jpg', - 'uploaded-file', + ctx.fsPath, null, 'upload', ctx.userId @@ -246,7 +273,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.folderId, 'image.jpg', - 'uploaded-file', + ctx.fsPath, true ) }) @@ -256,7 +283,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.folderId, 'image.jpg', - 'uploaded-file', + ctx.fsPath, null, 'upload', ctx.userId @@ -271,16 +298,20 @@ describe('FileSystemImportManager', function () { ['Windows', '\r\n'], ]) { describe(`with text file (${lineEndingDescription} line endings)`, function () { - beforeEach(function () { - mockFs({ + beforeEach(function (ctx) { + ctx.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'addentity-txt-')) + ctx.fsPath = path.join(ctx.tmpDir, 'path', 'to', 'uploaded-file') + createTree(ctx.tmpDir, { path: { - to: { 'uploaded-file': `one${lineEnding}two${lineEnding}three` }, + to: { + 'uploaded-file': `one${lineEnding}two${lineEnding}three`, + }, }, }) }) - afterEach(function () { - mockFs.restore() + afterEach(function (ctx) { + fs.rmSync(ctx.tmpDir, { recursive: true, force: true }) }) describe('with replace set to false', function () { @@ -290,7 +321,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.folderId, 'doc.tex', - 'path/to/uploaded-file', + ctx.fsPath, false ) }) @@ -314,7 +345,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.folderId, 'doc.tex', - 'path/to/uploaded-file', + ctx.fsPath, true ) }) @@ -334,14 +365,20 @@ describe('FileSystemImportManager', function () { } describe('with symlink', function () { - beforeEach(function () { - mockFs({ - path: { to: { symlink: mockFs.symlink({ path: '/etc/passwd' }) } }, + beforeEach(function (ctx) { + ctx.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'addentity-sym-')) + ctx.fsPath = path.join(ctx.tmpDir, 'path', 'to', 'symlink') + createTree(ctx.tmpDir, { + path: { + to: { + symlink: { symlink: '/etc/passwd' }, + }, + }, }) }) - afterEach(function () { - mockFs.restore() + afterEach(function (ctx) { + fs.rmSync(ctx.tmpDir, { recursive: true, force: true }) }) it('should stop with an error', async function (ctx) { @@ -351,7 +388,7 @@ describe('FileSystemImportManager', function () { ctx.projectId, ctx.folderId, 'main.tex', - 'path/to/symlink', + ctx.fsPath, false ) ).to.be.rejectedWith('path is symlink') diff --git a/yarn.lock b/yarn.lock index a8c1b77c1e..324edec4a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6498,7 +6498,6 @@ __metadata: mocha: "npm:^11.1.0" mocha-junit-reporter: "npm:^2.2.1" mocha-multi-reporters: "npm:^1.5.1" - mock-fs: "npm:^5.1.2" multer: "npm:2.1.1" node-fetch: "npm:^2.7.0" nyc: "npm:^17.1.0" @@ -6878,7 +6877,6 @@ __metadata: mocha: "npm:^11.1.0" mocha-junit-reporter: "npm:^2.2.1" mocha-multi-reporters: "npm:^1.5.1" - mock-fs: "npm:^5.2.0" mongodb: "npm:6.12.0" range-parser: "npm:^1.2.1" sandboxed-module: "npm:^2.0.4" @@ -7475,7 +7473,6 @@ __metadata: mocha-each: "npm:^2.0.1" mocha-junit-reporter: "npm:^2.2.1" mocha-multi-reporters: "npm:^1.5.1" - mock-fs: "npm:^5.1.2" moment: "npm:^2.29.4" mongodb-legacy: "npm:6.1.3" mongoose: "npm:8.22.1" @@ -24737,13 +24734,6 @@ __metadata: languageName: node linkType: hard -"mock-fs@npm:5.2.0": - version: 5.2.0 - resolution: "mock-fs@npm:5.2.0" - checksum: 10c0/e917e71295ee9d805c7d9ab0214a66658db0212f35731ba0c75dc1ccbb9964e6787a6d725561f05fcf569c64cf4a1176f5ce438f98b009227520f646f8abf174 - languageName: node - linkType: hard - "module-details-from-path@npm:^1.0.3, module-details-from-path@npm:^1.0.4": version: 1.0.4 resolution: "module-details-from-path@npm:1.0.4"