diff -Nru node-mock-fs-4.10.2/changelog.md node-mock-fs-4.10.4/changelog.md --- node-mock-fs-4.10.2/changelog.md 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/changelog.md 2019-11-26 05:27:15.000000000 +0000 @@ -1,5 +1,13 @@ # Change Log +## 4.10.4 + + * Fix maybeCallback return (thanks @3cp, see [#281][#281]) + +## 4.10.3 + + * Fix bad promise rejection on some fs.promises methods (thanks @3cp, see [#279][#279]) + ## 4.10.2 * Fix timestamps for Node > 12.10 (thanks @3cp, see [#277][#277]) @@ -262,3 +270,5 @@ [#268]: https://github.com/tschaub/mock-fs/pull/268 [#271]: https://github.com/tschaub/mock-fs/pull/271 [#277]: https://github.com/tschaub/mock-fs/pull/277 +[#279]: https://github.com/tschaub/mock-fs/pull/279 +[#281]: https://github.com/tschaub/mock-fs/pull/281 diff -Nru node-mock-fs-4.10.2/debian/changelog node-mock-fs-4.10.4/debian/changelog --- node-mock-fs-4.10.2/debian/changelog 2019-10-31 16:41:20.000000000 +0000 +++ node-mock-fs-4.10.4/debian/changelog 2019-11-30 09:55:46.000000000 +0000 @@ -1,3 +1,13 @@ +node-mock-fs (4.10.4-1) unstable; urgency=medium + + [ upstream ] + * new release(s) + + [ Jonas Smedegaard ] + * fix clean generated docs + + -- Jonas Smedegaard Sat, 30 Nov 2019 10:55:46 +0100 + node-mock-fs (4.10.2-2) unstable; urgency=medium * Team upload. diff -Nru node-mock-fs-4.10.2/debian/copyright_hints node-mock-fs-4.10.4/debian/copyright_hints --- node-mock-fs-4.10.2/debian/copyright_hints 2019-10-31 16:41:00.000000000 +0000 +++ node-mock-fs-4.10.4/debian/copyright_hints 2019-11-30 09:55:26.000000000 +0000 @@ -13,10 +13,13 @@ benchmarks/write-mock.js benchmarks/write-real.js changelog.md + debian/TODO debian/compat debian/control debian/gbp.conf debian/install + debian/patches/2001_privacy.patch + debian/patches/series debian/rules debian/source/format debian/tests/control @@ -43,6 +46,31 @@ test/lib/directory.spec.js test/lib/file.spec.js test/lib/filesystem.spec.js + test/lib/fs.access.spec.js + test/lib/fs.appendFile.spec.js + test/lib/fs.chmod-fchmod.spec.js + test/lib/fs.chown-fchown.spec.js + test/lib/fs.copyFile.spec.js + test/lib/fs.createReadStream.spec.js + test/lib/fs.createWriteStream.spec.js + test/lib/fs.exists.spec.js + test/lib/fs.link-symlink.spec.js + test/lib/fs.lstat.spec.js + test/lib/fs.mkdir.spec.js + test/lib/fs.mkdtemp.spec.js + test/lib/fs.open-close.spec.js + test/lib/fs.read.spec.js + test/lib/fs.readFile.spec.js + test/lib/fs.readdir.spec.js + test/lib/fs.readlink.spec.js + test/lib/fs.realpath.spec.js + test/lib/fs.rename.spec.js + test/lib/fs.rmdir.spec.js + test/lib/fs.stat-fstat.spec.js + test/lib/fs.unlink.spec.js + test/lib/fs.utimes-futimes.spec.js + test/lib/fs.write.spec.js + test/lib/fs.writeFile.spec.js test/lib/index.spec.js test/lib/item.spec.js Copyright: NONE diff -Nru node-mock-fs-4.10.2/debian/rules node-mock-fs-4.10.4/debian/rules --- node-mock-fs-4.10.2/debian/rules 2019-10-31 16:41:00.000000000 +0000 +++ node-mock-fs-4.10.4/debian/rules 2019-11-30 09:54:33.000000000 +0000 @@ -26,7 +26,7 @@ override_dh_installchangelogs: dh_installchangelogs -- $(CHANGELOGS) -dh_clean: +override_dh_clean: dh_clean -- $(DOCS) $(CHANGELOGS) %.html: %.md diff -Nru node-mock-fs-4.10.2/lib/binding.js node-mock-fs-4.10.4/lib/binding.js --- node-mock-fs-4.10.2/lib/binding.js 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/lib/binding.js 2019-11-26 05:27:15.000000000 +0000 @@ -60,7 +60,7 @@ } return new Promise(function(resolve, reject) { process.nextTick(function() { - if (val === undefined) { + if (err) { reject(err); } else { resolve(val); @@ -356,9 +356,15 @@ throw new FSError('ENOENT', filepath); } + if (process.platform === 'win32' && realPath.startsWith('\\\\?\\')) { + // Remove win32 file namespace prefix \\?\ + realPath = realPath.slice(4); + } + if (encoding === 'buffer') { realPath = bufferFrom(realPath); } + return realPath; }); }; @@ -495,7 +501,7 @@ Binding.prototype.close = function(fd, callback, ctx) { markSyscall(ctx, 'close'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { untrackDescriptorById(fd); }); }; @@ -824,7 +830,7 @@ const buffer = bufferFrom(string, encoding); let wrapper; - if (callback) { + if (callback && callback !== kUsePromises) { if (callback.oncomplete) { callback = callback.oncomplete.bind(callback); } @@ -955,7 +961,7 @@ markSyscall(ctx, 'mkdir'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const item = _system.getItem(pathname); if (item) { if (recursive && item instanceof Directory) { @@ -995,7 +1001,7 @@ Binding.prototype.rmdir = function(pathname, callback, ctx) { markSyscall(ctx, 'rmdir'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const item = _system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); @@ -1087,7 +1093,7 @@ Binding.prototype.ftruncate = function(fd, len, callback, ctx) { markSyscall(ctx, 'ftruncate'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const descriptor = getDescriptorById(fd); if (!descriptor.isWrite()) { throw new FSError('EINVAL'); @@ -1123,7 +1129,7 @@ Binding.prototype.chown = function(pathname, uid, gid, callback, ctx) { markSyscall(ctx, 'chown'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const item = _system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); @@ -1144,7 +1150,7 @@ Binding.prototype.fchown = function(fd, uid, gid, callback, ctx) { markSyscall(ctx, 'fchown'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const descriptor = getDescriptorById(fd); const item = descriptor.getItem(); item.setUid(uid); @@ -1162,7 +1168,7 @@ Binding.prototype.chmod = function(pathname, mode, callback, ctx) { markSyscall(ctx, 'chmod'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const item = _system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); @@ -1181,7 +1187,7 @@ Binding.prototype.fchmod = function(fd, mode, callback, ctx) { markSyscall(ctx, 'fchmod'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const descriptor = getDescriptorById(fd); const item = descriptor.getItem(); item.setMode(mode); @@ -1197,7 +1203,7 @@ Binding.prototype.unlink = function(pathname, callback, ctx) { markSyscall(ctx, 'unlink'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const item = _system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); @@ -1221,7 +1227,7 @@ Binding.prototype.utimes = function(pathname, atime, mtime, callback, ctx) { markSyscall(ctx, 'utimes'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const item = _system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); @@ -1242,7 +1248,7 @@ Binding.prototype.futimes = function(fd, atime, mtime, callback, ctx) { markSyscall(ctx, 'futimes'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const descriptor = getDescriptorById(fd); const item = descriptor.getItem(); item.setATime(new Date(atime * 1000)); @@ -1259,7 +1265,7 @@ Binding.prototype.fsync = function(fd, callback, ctx) { markSyscall(ctx, 'fsync'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { getDescriptorById(fd); }); }; @@ -1273,7 +1279,7 @@ Binding.prototype.fdatasync = function(fd, callback, ctx) { markSyscall(ctx, 'fdatasync'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { getDescriptorById(fd); }); }; @@ -1288,7 +1294,7 @@ Binding.prototype.link = function(srcPath, destPath, callback, ctx) { markSyscall(ctx, 'link'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { const item = _system.getItem(srcPath); if (!item) { throw new FSError('ENOENT', srcPath); @@ -1321,7 +1327,7 @@ Binding.prototype.symlink = function(srcPath, destPath, type, callback, ctx) { markSyscall(ctx, 'symlink'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { if (_system.getItem(destPath)) { throw new FSError('EEXIST', destPath); } @@ -1420,7 +1426,7 @@ Binding.prototype.access = function(filepath, mode, callback, ctx) { markSyscall(ctx, 'access'); - maybeCallback(normalizeCallback(callback), ctx, this, function() { + return maybeCallback(normalizeCallback(callback), ctx, this, function() { let item = _system.getItem(filepath); let links = 0; while (item instanceof SymbolicLink) { diff -Nru node-mock-fs-4.10.2/lib/filesystem.js node-mock-fs-4.10.4/lib/filesystem.js --- node-mock-fs-4.10.2/lib/filesystem.js 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/lib/filesystem.js 2019-11-26 05:27:15.000000000 +0000 @@ -10,8 +10,14 @@ const isWindows = process.platform === 'win32'; +function toNamespacedPath(filePath) { + return path.toNamespacedPath + ? path.toNamespacedPath(filePath) + : path._makeLong(filePath); +} + function getPathParts(filepath) { - const parts = path._makeLong(path.resolve(filepath)).split(path.sep); + const parts = toNamespacedPath(path.resolve(filepath)).split(path.sep); parts.shift(); if (isWindows) { // parts currently looks like ['', '?', 'c:', ...] @@ -110,6 +116,9 @@ throw new FSError('EACCES', filepath); } } + if (item instanceof File) { + throw new FSError('ENOTDIR', filepath); + } item = item.getItem(name); } if (!item) { @@ -318,3 +327,4 @@ */ exports = module.exports = FileSystem; exports.getPathParts = getPathParts; +exports.toNamespacedPath = toNamespacedPath; diff -Nru node-mock-fs-4.10.2/lib/index.js node-mock-fs-4.10.4/lib/index.js --- node-mock-fs-4.10.2/lib/index.js 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/lib/index.js 2019-11-26 05:27:15.000000000 +0000 @@ -7,6 +7,8 @@ const path = require('path'); const fs = require('fs'); +const toNamespacedPath = FileSystem.toNamespacedPath; + const realProcessProps = { cwd: process.cwd, chdir: process.chdir @@ -136,7 +138,7 @@ return currentPath; }, function chdir(directory) { - if (!binding.stat(path._makeLong(directory)).isDirectory()) { + if (!binding.stat(toNamespacedPath(directory)).isDirectory()) { throw new FSError('ENOTDIR'); } currentPath = path.resolve(currentPath, directory); diff -Nru node-mock-fs-4.10.2/package.json node-mock-fs-4.10.4/package.json --- node-mock-fs-4.10.2/package.json 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/package.json 2019-11-26 05:27:15.000000000 +0000 @@ -1,7 +1,7 @@ { "name": "mock-fs", "description": "A configurable mock file system. You know, for testing.", - "version": "4.10.2", + "version": "4.10.4", "main": "lib/index.js", "homepage": "https://github.com/tschaub/mock-fs", "author": { diff -Nru node-mock-fs-4.10.2/package-lock.json node-mock-fs-4.10.4/package-lock.json --- node-mock-fs-4.10.2/package-lock.json 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/package-lock.json 2019-11-26 05:27:15.000000000 +0000 @@ -1,6 +1,6 @@ { "name": "mock-fs", - "version": "4.10.2", + "version": "4.10.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff -Nru node-mock-fs-4.10.2/test/helper.js node-mock-fs-4.10.4/test/helper.js --- node-mock-fs-4.10.2/test/helper.js 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/test/helper.js 2019-11-26 05:27:15.000000000 +0000 @@ -3,6 +3,8 @@ const chai = require('chai'); const constants = require('constants'); const semver = require('semver'); +const fs = require('fs'); +const hasPromise = !!fs.promises; /** @type {boolean} */ chai.config.includeStack = true; @@ -13,14 +15,19 @@ */ exports.assert = chai.assert; +const TEST = {it: it, xit: xit, describe: describe, xdescribe: xdescribe}; +const NO_TEST = {it: xit, xit: xit, describe: xdescribe, xdescribe: xdescribe}; + exports.inVersion = function(range) { if (semver.satisfies(process.version, range)) { - return {it: it, describe: describe}; + return TEST; } else { - return {it: xit, describe: xdescribe}; + return NO_TEST; } }; +exports.withPromise = hasPromise ? TEST : NO_TEST; + /** * Convert a string to flags for fs.open. * @param {string} str String. @@ -84,3 +91,11 @@ throw new Error('Unsupported flag: ' + str); } }; + +exports.assertEqualPaths = function(actual, expected) { + if (process.platform === 'win32') { + chai.assert.equal(actual.toLowerCase(), expected.toLowerCase()); + } else { + chai.assert(actual, expected); + } +}; diff -Nru node-mock-fs-4.10.2/test/lib/binding.spec.js node-mock-fs-4.10.4/test/lib/binding.spec.js --- node-mock-fs-4.10.2/test/lib/binding.spec.js 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/binding.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -13,6 +13,7 @@ const bufferAlloc = require('../../lib/buffer').alloc; const assert = helper.assert; +const assertEqualPaths = helper.assertEqualPaths; const flags = helper.flags; describe('Binding', function() { @@ -238,18 +239,6 @@ }); describe('#realpath()', function() { - function assertEqualPaths(actual, expected) { - if (path._makeLong(expected) === expected) { - // not on Windows - assert.equal(actual, expected); - } else { - assert.equal( - actual.toLowerCase(), - path._makeLong(expected).toLowerCase() - ); - } - } - it('returns the real path for a regular file', function(done) { const binding = new Binding(system); binding.realpath('mock-dir/one.txt', 'utf-8', function(err, realPath) { @@ -918,6 +907,16 @@ binding.read(fd, buffer, 0, 11, 0); }); }); + + it('throws ENOTDIR when trying to open an incorrect path (nested under existing file)', function() { + const binding = new Binding(system); + assert.throws(function() { + binding.open( + path.join('mock-dir', 'two.txt', 'bogus-path'), + flags('r') + ); + }, 'ENOTDIR'); + }); }); describe('#write()', function() { diff -Nru node-mock-fs-4.10.2/test/lib/fs.access.spec.js node-mock-fs-4.10.4/test/lib/fs.access.spec.js --- node-mock-fs-4.10.2/test/lib/fs.access.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.access.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,709 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const assert = helper.assert; +const withPromise = helper.withPromise; + +if (fs.access && fs.accessSync && process.getuid && process.getgid) { + // TODO: drop condition when dropping Node < 0.12 support + // TODO: figure out how fs.access() works on Windows (without gid/uid) + + describe('fs.access(path[, mode], callback)', function() { + beforeEach(function() { + mock({ + 'path/to/accessible/file': 'can access', + 'path/to/000': mock.file({ + mode: parseInt('0000', 8), + content: 'no permissions' + }), + 'path/to/111': mock.file({ + mode: parseInt('0111', 8), + content: 'execute only' + }), + 'path/to/write/only': mock.file({ + mode: parseInt('0222', 8), + content: 'write only' + }), + 'path/to/333': mock.file({ + mode: parseInt('0333', 8), + content: 'write and execute' + }), + 'path/to/444': mock.file({ + mode: parseInt('0444', 8), + content: 'read only' + }), + 'path/to/555': mock.file({ + mode: parseInt('0555', 8), + content: 'read and execute' + }), + 'path/to/666': mock.file({ + mode: parseInt('0666', 8), + content: 'read and write' + }), + 'path/to/777': mock.file({ + mode: parseInt('0777', 8), + content: 'read, write, and execute' + }), + unreadable: mock.directory({ + mode: parseInt('0000', 8), + items: { + 'readable-child': mock.file({ + mode: parseInt('0777', 8), + content: 'read, write, and execute' + }) + } + }) + }); + }); + afterEach(mock.restore); + + it('works for an accessible file', function(done) { + fs.access('path/to/accessible/file', done); + }); + + withPromise.it('promise works for an accessible file', function(done) { + fs.promises.access('path/to/accessible/file').then(done, done); + }); + + it('works 000 (and no mode arg)', function(done) { + fs.access('path/to/000', done); + }); + + withPromise.it('promise works 000 (and no mode arg)', function(done) { + fs.promises.access('path/to/000').then(done, done); + }); + + it('works F_OK and 000', function(done) { + fs.access('path/to/000', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 000', function(done) { + fs.promises.access('path/to/000', fs.F_OK).then(done, done); + }); + + it('generates EACCES for R_OK and 000', function(done) { + fs.access('path/to/000', fs.R_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for R_OK and 000', function(done) { + fs.promises.access('path/to/000', fs.R_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('generates EACCES for W_OK and 000', function(done) { + fs.access('path/to/000', fs.W_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for W_OK and 000', function(done) { + fs.promises.access('path/to/000', fs.W_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('generates EACCES for X_OK and 000', function(done) { + fs.access('path/to/000', fs.X_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for X_OK and 000', function(done) { + fs.promises.access('path/to/000', fs.X_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('works 111 (and no mode arg)', function(done) { + fs.access('path/to/111', done); + }); + + withPromise.it('promise works 111 (and no mode arg)', function(done) { + fs.promises.access('path/to/111').then(done, done); + }); + + it('works F_OK and 111', function(done) { + fs.access('path/to/111', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 111', function(done) { + fs.promises.access('path/to/111', fs.F_OK).then(done, done); + }); + + it('works X_OK and 111', function(done) { + fs.access('path/to/111', fs.X_OK, done); + }); + + withPromise.it('promise works X_OK and 111', function(done) { + fs.promises.access('path/to/111', fs.X_OK).then(done, done); + }); + + it('generates EACCES for R_OK and 111', function(done) { + fs.access('path/to/111', fs.R_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for R_OK and 111', function(done) { + fs.promises.access('path/to/111', fs.R_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('generates EACCES for W_OK and 111', function(done) { + fs.access('path/to/111', fs.W_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for W_OK and 111', function(done) { + fs.promises.access('path/to/111', fs.W_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('works for 222 (and no mode arg)', function(done) { + fs.access('path/to/write/only', done); + }); + + withPromise.it('promise works for 222 (and no mode arg)', function(done) { + fs.promises.access('path/to/write/only').then(done, done); + }); + + it('works F_OK and 222', function(done) { + fs.access('path/to/write/only', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 222', function(done) { + fs.promises.access('path/to/write/only', fs.F_OK).then(done, done); + }); + + it('works W_OK and 222', function(done) { + fs.access('path/to/write/only', fs.W_OK, done); + }); + + withPromise.it('promise works W_OK and 222', function(done) { + fs.promises.access('path/to/write/only', fs.W_OK).then(done, done); + }); + + it('generates EACCES for R_OK and 222', function(done) { + fs.access('path/to/write/only', fs.R_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for R_OK and 222', function(done) { + fs.promises.access('path/to/write/only', fs.R_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('generates EACCES for X_OK and 222', function(done) { + fs.access('path/to/write/only', fs.X_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for X_OK and 222', function(done) { + fs.promises.access('path/to/write/only', fs.X_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('works for 333 (and no mode arg)', function(done) { + fs.access('path/to/333', done); + }); + + withPromise.it('promise works for 333 (and no mode arg)', function(done) { + fs.promises.access('path/to/333').then(done, done); + }); + + it('works F_OK and 333', function(done) { + fs.access('path/to/333', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 333', function(done) { + fs.promises.access('path/to/333', fs.F_OK).then(done, done); + }); + + it('works W_OK and 333', function(done) { + fs.access('path/to/333', fs.W_OK, done); + }); + + withPromise.it('promise works W_OK and 333', function(done) { + fs.promises.access('path/to/333', fs.W_OK).then(done, done); + }); + + it('works X_OK and 333', function(done) { + fs.access('path/to/333', fs.X_OK, done); + }); + + withPromise.it('promise works X_OK and 333', function(done) { + fs.promises.access('path/to/333', fs.X_OK).then(done, done); + }); + + it('works X_OK | W_OK and 333', function(done) { + fs.access('path/to/333', fs.X_OK | fs.W_OK, done); + }); + + withPromise.it('promise works X_OK | W_OK and 333', function(done) { + fs.promises.access('path/to/333', fs.X_OK | fs.W_OK).then(done, done); + }); + + it('generates EACCES for R_OK and 333', function(done) { + fs.access('path/to/333', fs.R_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for R_OK and 333', function(done) { + fs.promises.access('path/to/333', fs.R_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('works for 444 (and no mode arg)', function(done) { + fs.access('path/to/444', done); + }); + + withPromise.it('promise works for 444 (and no mode arg)', function(done) { + fs.promises.access('path/to/444').then(done, done); + }); + + it('works F_OK and 444', function(done) { + fs.access('path/to/444', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 444', function(done) { + fs.promises.access('path/to/444', fs.F_OK).then(done, done); + }); + + it('works R_OK and 444', function(done) { + fs.access('path/to/444', fs.R_OK, done); + }); + + withPromise.it('promise works R_OK and 444', function(done) { + fs.promises.access('path/to/444', fs.R_OK).then(done, done); + }); + + it('generates EACCES for W_OK and 444', function(done) { + fs.access('path/to/444', fs.W_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for W_OK and 444', function(done) { + fs.promises.access('path/to/444', fs.W_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('generates EACCES for X_OK and 444', function(done) { + fs.access('path/to/444', fs.X_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for X_OK and 444', function(done) { + fs.promises.access('path/to/444', fs.X_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('works for 555 (and no mode arg)', function(done) { + fs.access('path/to/555', done); + }); + + withPromise.it('promise works for 555 (and no mode arg)', function(done) { + fs.promises.access('path/to/555').then(done, done); + }); + + it('works F_OK and 555', function(done) { + fs.access('path/to/555', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 555', function(done) { + fs.promises.access('path/to/555', fs.F_OK).then(done, done); + }); + + it('works R_OK and 555', function(done) { + fs.access('path/to/555', fs.R_OK, done); + }); + + withPromise.it('promise works R_OK and 555', function(done) { + fs.promises.access('path/to/555', fs.R_OK).then(done, done); + }); + + it('works X_OK and 555', function(done) { + fs.access('path/to/555', fs.X_OK, done); + }); + + withPromise.it('promise works X_OK and 555', function(done) { + fs.promises.access('path/to/555', fs.X_OK).then(done, done); + }); + + it('works R_OK | X_OK and 555', function(done) { + fs.access('path/to/555', fs.R_OK | fs.X_OK, done); + }); + + withPromise.it('promise works R_OK | X_OK and 555', function(done) { + fs.promises.access('path/to/555', fs.R_OK | fs.X_OK).then(done, done); + }); + + it('generates EACCES for W_OK and 555', function(done) { + fs.access('path/to/555', fs.W_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for W_OK and 555', function(done) { + fs.promises.access('path/to/555', fs.W_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('works for 666 (and no mode arg)', function(done) { + fs.access('path/to/666', done); + }); + + withPromise.it('promise works for 666 (and no mode arg)', function(done) { + fs.promises.access('path/to/666').then(done, done); + }); + + it('works F_OK and 666', function(done) { + fs.access('path/to/666', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 666', function(done) { + fs.promises.access('path/to/666', fs.F_OK).then(done, done); + }); + + it('works R_OK and 666', function(done) { + fs.access('path/to/666', fs.R_OK, done); + }); + + withPromise.it('promise works R_OK and 666', function(done) { + fs.promises.access('path/to/666', fs.R_OK).then(done, done); + }); + + it('works W_OK and 666', function(done) { + fs.access('path/to/666', fs.W_OK, done); + }); + + withPromise.it('promise works W_OK and 666', function(done) { + fs.promises.access('path/to/666', fs.W_OK).then(done, done); + }); + + it('works R_OK | W_OK and 666', function(done) { + fs.access('path/to/666', fs.R_OK | fs.W_OK, done); + }); + + withPromise.it('promise works R_OK | W_OK and 666', function(done) { + fs.promises.access('path/to/666', fs.R_OK | fs.W_OK).then(done, done); + }); + + it('generates EACCES for X_OK and 666', function(done) { + fs.access('path/to/666', fs.X_OK, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise generates EACCES for X_OK and 666', function(done) { + fs.promises.access('path/to/666', fs.X_OK).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + + it('works for 777 (and no mode arg)', function(done) { + fs.access('path/to/777', done); + }); + + withPromise.it('promise works for 777 (and no mode arg)', function(done) { + fs.promises.access('path/to/777').then(done, done); + }); + + it('works F_OK and 777', function(done) { + fs.access('path/to/777', fs.F_OK, done); + }); + + withPromise.it('promise works F_OK and 777', function(done) { + fs.promises.access('path/to/777', fs.F_OK).then(done, done); + }); + + it('works R_OK and 777', function(done) { + fs.access('path/to/777', fs.R_OK, done); + }); + + withPromise.it('promise works R_OK and 777', function(done) { + fs.promises.access('path/to/777', fs.R_OK).then(done, done); + }); + + it('works W_OK and 777', function(done) { + fs.access('path/to/777', fs.W_OK, done); + }); + + withPromise.it('promise works W_OK and 777', function(done) { + fs.promises.access('path/to/777', fs.W_OK).then(done, done); + }); + + it('works X_OK and 777', function(done) { + fs.access('path/to/777', fs.X_OK, done); + }); + + withPromise.it('promise works X_OK and 777', function(done) { + fs.promises.access('path/to/777', fs.X_OK).then(done, done); + }); + + it('works X_OK | W_OK and 777', function(done) { + fs.access('path/to/777', fs.X_OK | fs.W_OK, done); + }); + + withPromise.it('promise works X_OK | W_OK and 777', function(done) { + fs.promises.access('path/to/777', fs.X_OK | fs.W_OK).then(done, done); + }); + + it('works X_OK | R_OK and 777', function(done) { + fs.access('path/to/777', fs.X_OK | fs.R_OK, done); + }); + + withPromise.it('promise works X_OK | R_OK and 777', function(done) { + fs.promises.access('path/to/777', fs.X_OK | fs.R_OK).then(done, done); + }); + + it('works R_OK | W_OK and 777', function(done) { + fs.access('path/to/777', fs.R_OK | fs.W_OK, done); + }); + + withPromise.it('promise works R_OK | W_OK and 777', function(done) { + fs.promises.access('path/to/777', fs.R_OK | fs.W_OK).then(done, done); + }); + + it('works R_OK | W_OK | X_OK and 777', function(done) { + fs.access('path/to/777', fs.R_OK | fs.W_OK | fs.X_OK, done); + }); + + withPromise.it('promise works R_OK | W_OK | X_OK and 777', function(done) { + fs.promises + .access('path/to/777', fs.R_OK | fs.W_OK | fs.X_OK) + .then(done, done); + }); + + it('generates EACCESS for F_OK and an unreadable parent', function(done) { + fs.access('unreadable/readable-child', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it( + 'promise generates EACCESS for F_OK and an unreadable parent', + function(done) { + fs.promises.access('unreadable/readable-child').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + } + ); + }); + + describe('fs.accessSync(path[, mode])', function() { + beforeEach(function() { + mock({ + 'path/to/777': mock.file({ + mode: parseInt('0777', 8), + content: 'all access' + }), + 'path/to/000': mock.file({ + mode: parseInt('0000', 8), + content: 'no permissions' + }), + 'broken-link': mock.symlink({path: './path/to/nothing'}), + 'circular-link': mock.symlink({path: './loop-link'}), + 'loop-link': mock.symlink({path: './circular-link'}) + }); + }); + afterEach(mock.restore); + + it('works for an accessible file', function() { + fs.accessSync('path/to/777'); + fs.accessSync('path/to/777', fs.F_OK); + fs.accessSync('path/to/777', fs.X_OK); + fs.accessSync('path/to/777', fs.W_OK); + fs.accessSync('path/to/777', fs.X_OK | fs.W_OK); + fs.accessSync('path/to/777', fs.R_OK); + fs.accessSync('path/to/777', fs.X_OK | fs.R_OK); + fs.accessSync('path/to/777', fs.W_OK | fs.R_OK); + fs.accessSync('path/to/777', fs.X_OK | fs.W_OK | fs.R_OK); + }); + + it('throws EACCESS for broken link', function() { + assert.throws(function() { + fs.accessSync('broken-link'); + }); + }); + + it('throws ELOOP for circular link', function() { + assert.throws(function() { + fs.accessSync('circular-link'); + }); + }); + + it('throws EACCESS for all but F_OK for 000', function() { + fs.accessSync('path/to/000'); + assert.throws(function() { + fs.accessSync('path/to/000', fs.X_OK); + }); + assert.throws(function() { + fs.accessSync('path/to/000', fs.W_OK); + }); + assert.throws(function() { + fs.accessSync('path/to/000', fs.X_OK | fs.W_OK); + }); + assert.throws(function() { + fs.accessSync('path/to/000', fs.R_OK); + }); + assert.throws(function() { + fs.accessSync('path/to/000', fs.X_OK | fs.R_OK); + }); + assert.throws(function() { + fs.accessSync('path/to/000', fs.W_OK | fs.R_OK); + }); + assert.throws(function() { + fs.accessSync('path/to/000', fs.X_OK | fs.W_OK | fs.R_OK); + }); + }); + }); +} diff -Nru node-mock-fs-4.10.2/test/lib/fs.appendFile.spec.js node-mock-fs-4.10.4/test/lib/fs.appendFile.spec.js --- node-mock-fs-4.10.2/test/lib/fs.appendFile.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.appendFile.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,135 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const bufferFrom = require('../../lib/buffer').from; + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.appendFile(filename, data, [options], callback)', function() { + beforeEach(function() { + mock({ + 'dir/file.txt': 'file content', + 'link.txt': mock.symlink({path: 'dir/file.txt'}) + }); + }); + afterEach(mock.restore); + + it('writes a string to a new file', function(done) { + fs.appendFile('foo', 'bar', function(err) { + if (err) { + return done(err); + } + assert.equal(String(fs.readFileSync('foo')), 'bar'); + done(); + }); + }); + + withPromise.it('promise writes a string to a new file', function(done) { + fs.promises.appendFile('foo', 'bar').then(function() { + assert.equal(String(fs.readFileSync('foo')), 'bar'); + done(); + }, done); + }); + + it('appends a string to an existing file', function(done) { + fs.appendFile('dir/file.txt', ' bar', function(err) { + if (err) { + return done(err); + } + assert.equal(String(fs.readFileSync('dir/file.txt')), 'file content bar'); + done(); + }); + }); + + withPromise.it('promise appends a string to an existing file', function( + done + ) { + fs.promises.appendFile('dir/file.txt', ' bar').then(function() { + assert.equal(String(fs.readFileSync('dir/file.txt')), 'file content bar'); + done(); + }, done); + }); + + it('appends a buffer to a file', function(done) { + fs.appendFile('dir/file.txt', bufferFrom(' bar'), function(err) { + if (err) { + return done(err); + } + assert.equal(String(fs.readFileSync('dir/file.txt')), 'file content bar'); + done(); + }); + }); + + withPromise.it('promise appends a buffer to a file', function(done) { + fs.promises.appendFile('dir/file.txt', bufferFrom(' bar')).then(function() { + assert.equal(String(fs.readFileSync('dir/file.txt')), 'file content bar'); + done(); + }, done); + }); + + it('appends via a symbolic link file', function(done) { + fs.appendFile('link.txt', ' bar', function(err) { + if (err) { + return done(err); + } + assert.equal(String(fs.readFileSync('dir/file.txt')), 'file content bar'); + done(); + }); + }); + + withPromise.it('promise appends via a symbolic link file', function(done) { + fs.promises.appendFile('link.txt', ' bar').then(function() { + assert.equal(String(fs.readFileSync('dir/file.txt')), 'file content bar'); + done(); + }, done); + }); + + it('fails if directory does not exist', function(done) { + fs.appendFile('foo/bar', 'baz', function(err) { + assert.instanceOf(err, Error); + done(); + }); + }); + + withPromise.it('promise fails if directory does not exist', function(done) { + fs.promises.appendFile('foo/bar', 'baz').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +describe('fs.appendFileSync(filename, data, [options]', function() { + beforeEach(function() { + mock({ + 'path/to/file': 'content' + }); + }); + afterEach(mock.restore); + + it('writes a string to a new file', function() { + fs.appendFileSync('foo', 'bar'); + assert.equal(String(fs.readFileSync('foo')), 'bar'); + }); + + it('appends a string to an existing file', function() { + fs.appendFileSync('path/to/file', ' bar'); + assert.equal(String(fs.readFileSync('path/to/file')), 'content bar'); + }); + + it('fails if directory does not exist', function() { + assert.throws(function() { + fs.appendFileSync('foo/bar', 'baz'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.chmod-fchmod.spec.js node-mock-fs-4.10.4/test/lib/fs.chmod-fchmod.spec.js --- node-mock-fs-4.10.2/test/lib/fs.chmod-fchmod.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.chmod-fchmod.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,129 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.chmod(path, mode, callback)', function() { + beforeEach(function() { + mock({ + 'file.txt': mock.file({mode: parseInt('0644', 8)}) + }); + }); + afterEach(mock.restore); + + it('changes permissions of a file', function(done) { + fs.chmod('file.txt', parseInt('0664', 8), function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('file.txt'); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0664', 8)); + done(); + }); + }); + + withPromise.it('promise changes permissions of a file', function(done) { + fs.promises.chmod('file.txt', parseInt('0664', 8)).then(function() { + const stats = fs.statSync('file.txt'); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0664', 8)); + done(); + }, done); + }); + + it('fails if file does not exist', function(done) { + fs.chmod('bogus.txt', parseInt('0664', 8), function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails if file does not exist', function(done) { + fs.promises.chmod('bogus.txt', parseInt('0664', 8)).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +describe('fs.chmodSync(path, mode)', function() { + beforeEach(function() { + mock({ + 'file.txt': mock.file({mode: parseInt('0666', 8)}) + }); + }); + afterEach(mock.restore); + + it('changes permissions of a file', function() { + fs.chmodSync('file.txt', parseInt('0644', 8)); + const stats = fs.statSync('file.txt'); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0644', 8)); + }); + + it('fails if file does not exist', function() { + assert.throws(function() { + fs.chmodSync('bogus.txt', parseInt('0644', 8)); + }); + }); +}); + +describe('fs.fchmod(fd, mode, callback)', function() { + beforeEach(function() { + mock({ + 'file.txt': mock.file({mode: parseInt('0666', 8)}) + }); + }); + afterEach(mock.restore); + + it('changes permissions of a file', function(done) { + const fd = fs.openSync('file.txt', 'r'); + fs.fchmod(fd, parseInt('0644', 8), function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('file.txt'); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0644', 8)); + done(); + }); + }); + + withPromise.it('promise changes permissions of a file', function(done) { + fs.promises + .open('file.txt', 'r') + .then(function(fd) { + return fd.chmod(parseInt('0644', 8)); + }) + .then(function() { + const stats = fs.statSync('file.txt'); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0644', 8)); + done(); + }, done); + }); +}); + +describe('fs.fchmodSync(fd, mode)', function() { + beforeEach(function() { + mock({ + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('changes permissions of a file', function() { + const fd = fs.openSync('file.txt', 'r'); + fs.fchmodSync(fd, parseInt('0444', 8)); + const stats = fs.statSync('file.txt'); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0444', 8)); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.chown-fchown.spec.js node-mock-fs-4.10.4/test/lib/fs.chown-fchown.spec.js --- node-mock-fs-4.10.2/test/lib/fs.chown-fchown.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.chown-fchown.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,106 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.chown(path, uid, gid, callback)', function() { + beforeEach(function() { + mock({ + 'path/empty': {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('changes ownership of a file', function(done) { + fs.chown('file.txt', 42, 43, done); + }); + + withPromise.it('promise changes ownership of a file', function(done) { + fs.promises.chown('file.txt', 42, 43).then(done, done); + }); + + it('fails if file does not exist', function(done) { + fs.chown('bogus.txt', 42, 43, function(err) { + assert.instanceOf(err, Error); + done(); + }); + }); + + withPromise.it('promise fails if file does not exist', function(done) { + fs.promises.chown('bogus.txt', 42, 43).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +describe('fs.chownSync(path, uid, gid)', function() { + beforeEach(function() { + mock({ + 'path/empty': {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('changes ownership of a file', function() { + fs.chownSync('file.txt', 42, 43); + }); + + it('fails if file does not exist', function() { + assert.throws(function() { + fs.chownSync('bogus.txt', 42, 43); + }); + }); +}); + +describe('fs.fchown(fd, uid, gid, callback)', function() { + beforeEach(function() { + mock({ + 'path/empty': {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('changes ownership of a file', function(done) { + const fd = fs.openSync('file.txt', 'r'); + fs.fchown(fd, 42, 43, done); + }); + + withPromise.it('promise changes ownership of a file', function(done) { + fs.promises + .open('file.txt', 'r') + .then(function(fd) { + return fd.chown(42, 43); + }) + .then(done, done); + }); +}); + +describe('fs.fchownSync(fd, uid, gid)', function() { + beforeEach(function() { + mock({ + 'path/empty': {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('changes ownership of a file', function() { + const fd = fs.openSync('file.txt', 'r'); + fs.fchownSync(fd, 42, 43); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.copyFile.spec.js node-mock-fs-4.10.4/test/lib/fs.copyFile.spec.js --- node-mock-fs-4.10.2/test/lib/fs.copyFile.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.copyFile.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,169 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +if (fs.copyFile && fs.copyFileSync) { + describe('fs.copyFile(src, dest[, flags], callback)', function() { + beforeEach(function() { + mock({ + 'path/to/src.txt': 'file content', + 'path/to/other.txt': 'other file content', + empty: {} + }); + }); + afterEach(mock.restore); + + it('copies a file to an empty directory', function(done) { + fs.copyFile('path/to/src.txt', 'empty/dest.txt', function(err) { + assert.isTrue(!err); + assert.isTrue(fs.existsSync('empty/dest.txt')); + assert.equal(String(fs.readFileSync('empty/dest.txt')), 'file content'); + done(); + }); + }); + + withPromise.it('promise copies a file to an empty directory', function( + done + ) { + fs.promises + .copyFile('path/to/src.txt', 'empty/dest.txt') + .then(function() { + assert.isTrue(fs.existsSync('empty/dest.txt')); + assert.equal( + String(fs.readFileSync('empty/dest.txt')), + 'file content' + ); + done(); + }, done); + }); + + it('truncates dest file if it exists', function(done) { + fs.copyFile('path/to/src.txt', 'path/to/other.txt', function(err) { + assert.isTrue(!err); + assert.equal( + String(fs.readFileSync('path/to/other.txt')), + 'file content' + ); + done(); + }); + }); + + withPromise.it('promise truncates dest file if it exists', function(done) { + fs.promises + .copyFile('path/to/src.txt', 'path/to/other.txt') + .then(function() { + assert.equal( + String(fs.readFileSync('path/to/other.txt')), + 'file content' + ); + done(); + }, done); + }); + + it('throws if dest exists and exclusive', function(done) { + fs.copyFile( + 'path/to/src.txt', + 'path/to/other.txt', + fs.constants.COPYFILE_EXCL, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + } + ); + }); + + withPromise.it('promise throws if dest exists and exclusive', function( + done + ) { + fs.promises + .copyFile( + 'path/to/src.txt', + 'path/to/other.txt', + fs.constants.COPYFILE_EXCL + ) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + } + ); + }); + + it('fails if src does not exist', function(done) { + fs.copyFile('path/to/bogus.txt', 'empty/dest.txt', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails if src does not exist', function(done) { + fs.promises.copyFile('path/to/bogus.txt', 'empty/dest.txt').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); + + it('fails if dest path does not exist', function(done) { + fs.copyFile('path/to/src.txt', 'path/nope/dest.txt', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails if dest path does not exist', function(done) { + fs.promises.copyFile('path/to/src.txt', 'path/nope/dest.txt').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); + + it('fails if dest is a directory', function(done) { + fs.copyFile('path/to/src.txt', 'empty', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EISDIR'); + done(); + }); + }); + + withPromise.it('promise fails if dest is a directory', function(done) { + fs.promises.copyFile('path/to/src.txt', 'empty').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EISDIR'); + done(); + } + ); + }); + }); +} diff -Nru node-mock-fs-4.10.2/test/lib/fs.createReadStream.spec.js node-mock-fs-4.10.4/test/lib/fs.createReadStream.spec.js --- node-mock-fs-4.10.2/test/lib/fs.createReadStream.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.createReadStream.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,38 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; + +describe('fs.createReadStream(path, [options])', function() { + beforeEach(function() { + mock({ + 'dir/source': 'source content' + }); + }); + afterEach(mock.restore); + + it('creates a readable stream', function() { + const stream = fs.createReadStream('dir/source'); + assert.isTrue(stream.readable); + }); + + it('allows piping to a writable stream', function(done) { + const input = fs.createReadStream('dir/source'); + const output = fs.createWriteStream('dir/dest'); + output.on('close', function() { + fs.readFile('dir/dest', function(err, data) { + if (err) { + return done(err); + } + assert.equal(String(data), 'source content'); + done(); + }); + }); + output.on('error', done); + + input.pipe(output); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.createWriteStream.spec.js node-mock-fs-4.10.4/test/lib/fs.createWriteStream.spec.js --- node-mock-fs-4.10.2/test/lib/fs.createWriteStream.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.createWriteStream.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,85 @@ +'use strict'; + +const Writable = require('stream').Writable; +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const bufferFrom = require('../../lib/buffer').from; + +const assert = helper.assert; + +describe('fs.createWriteStream(path[, options])', function() { + beforeEach(function() { + mock(); + }); + afterEach(mock.restore); + + it('provides a write stream for a file in buffered mode', function(done) { + const output = fs.createWriteStream('test.txt'); + output.on('close', function() { + fs.readFile('test.txt', function(err, data) { + if (err) { + return done(err); + } + assert.equal(String(data), 'lots of source content'); + done(); + }); + }); + output.on('error', done); + + // if output._writev is available, buffered multiple writes will hit _writev. + // otherwise, hit multiple _write. + output.write(bufferFrom('lots ')); + output.write(bufferFrom('of ')); + output.write(bufferFrom('source ')); + output.end(bufferFrom('content')); + }); + + it('provides a write stream for a file', function(done) { + const output = fs.createWriteStream('test.txt'); + output.on('close', function() { + fs.readFile('test.txt', function(err, data) { + if (err) { + return done(err); + } + assert.equal(String(data), 'lots of source content'); + done(); + }); + }); + output.on('error', done); + + output.write(bufferFrom('lots ')); + setTimeout(function() { + output.write(bufferFrom('of ')); + setTimeout(function() { + output.write(bufferFrom('source ')); + setTimeout(function() { + output.end(bufferFrom('content')); + }, 50); + }, 50); + }, 50); + }); + + if (Writable && Writable.prototype.cork) { + it('works when write stream is corked', function(done) { + const output = fs.createWriteStream('test.txt'); + output.on('close', function() { + fs.readFile('test.txt', function(err, data) { + if (err) { + return done(err); + } + assert.equal(String(data), 'lots of source content'); + done(); + }); + }); + output.on('error', done); + + output.cork(); + output.write(bufferFrom('lots ')); + output.write(bufferFrom('of ')); + output.write(bufferFrom('source ')); + output.end(bufferFrom('content')); + output.uncork(); + }); + } +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.exists.spec.js node-mock-fs-4.10.4/test/lib/fs.exists.spec.js --- node-mock-fs-4.10.2/test/lib/fs.exists.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.exists.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,127 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const path = require('path'); +const bufferFrom = require('../../lib/buffer').from; + +const assert = helper.assert; + +describe('fs.exists(path, callback)', function() { + beforeEach(function() { + mock({ + 'path/to/a.bin': bufferFrom([1, 2, 3]), + empty: {}, + nested: { + dir: { + 'file.txt': '' + } + } + }); + }); + afterEach(mock.restore); + + it('calls with true if file exists', function(done) { + fs.exists(path.join('path', 'to', 'a.bin'), function(exists) { + assert.isTrue(exists); + done(); + }); + }); + + it('calls with true if directory exists', function(done) { + fs.exists('path', function(exists) { + assert.isTrue(exists); + done(); + }); + }); + + it('calls with true if empty directory exists', function(done) { + fs.exists('empty', function(exists) { + assert.isTrue(exists); + done(); + }); + }); + + it('calls with true if nested directory exists', function(done) { + fs.exists(path.join('nested', 'dir'), function(exists) { + assert.isTrue(exists); + done(); + }); + }); + + it('calls with true if file exists', function(done) { + fs.exists(path.join('path', 'to', 'a.bin'), function(exists) { + assert.isTrue(exists); + done(); + }); + }); + + it('calls with true if empty file exists', function(done) { + fs.exists(path.join('nested', 'dir', 'file.txt'), function(exists) { + assert.isTrue(exists); + done(); + }); + }); + + it('calls with false for bogus path', function(done) { + fs.exists(path.join('bogus', 'path'), function(exists) { + assert.isFalse(exists); + done(); + }); + }); + + it('calls with false for bogus path (II)', function(done) { + fs.exists(path.join('nested', 'dir', 'none'), function(exists) { + assert.isFalse(exists); + done(); + }); + }); +}); + +describe('fs.existsSync(path)', function() { + beforeEach(function() { + mock({ + 'path/to/a.bin': bufferFrom([1, 2, 3]), + empty: {}, + nested: { + dir: { + 'file.txt': '' + } + } + }); + }); + afterEach(mock.restore); + + it('returns true if file exists', function() { + assert.isTrue(fs.existsSync(path.join('path', 'to', 'a.bin'))); + }); + + it('returns true if directory exists', function() { + assert.isTrue(fs.existsSync('path')); + }); + + it('returns true if empty directory exists', function() { + assert.isTrue(fs.existsSync('empty')); + }); + + it('returns true if nested directory exists', function() { + assert.isTrue(fs.existsSync(path.join('nested', 'dir'))); + }); + + it('returns true if file exists', function() { + assert.isTrue(fs.existsSync(path.join('path', 'to', 'a.bin'))); + }); + + it('returns true if empty file exists', function() { + assert.isTrue(fs.existsSync(path.join('nested', 'dir', 'file.txt'))); + }); + + it('returns false for bogus path', function() { + assert.isFalse(fs.existsSync(path.join('bogus', 'path'))); + }); + + it('returns false for bogus path (II)', function() { + assert.isFalse(fs.existsSync(path.join('nested', 'dir', 'none'))); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.link-symlink.spec.js node-mock-fs-4.10.4/test/lib/fs.link-symlink.spec.js --- node-mock-fs-4.10.2/test/lib/fs.link-symlink.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.link-symlink.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,249 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.link(srcpath, dstpath, callback)', function() { + beforeEach(function() { + mock({ + dir: {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('creates a link to a file', function(done) { + assert.equal(fs.statSync('file.txt').nlink, 1); + + fs.link('file.txt', 'link.txt', function(err) { + if (err) { + return done(err); + } + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(fs.statSync('link.txt').nlink, 2); + assert.equal(fs.statSync('file.txt').nlink, 2); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + done(); + }); + }); + + withPromise.it('promise creates a link to a file', function(done) { + assert.equal(fs.statSync('file.txt').nlink, 1); + + fs.promises.link('file.txt', 'link.txt').then(function() { + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(fs.statSync('link.txt').nlink, 2); + assert.equal(fs.statSync('file.txt').nlink, 2); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + done(); + }, done); + }); + + it('works if original is renamed', function(done) { + fs.link('file.txt', 'link.txt', function(err) { + if (err) { + return done(err); + } + fs.renameSync('file.txt', 'renamed.txt'); + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + done(); + }); + }); + + withPromise.it('promise works if original is renamed', function(done) { + fs.promises.link('file.txt', 'link.txt').then(function() { + fs.renameSync('file.txt', 'renamed.txt'); + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + done(); + }, done); + }); + + it('works if original is removed', function(done) { + assert.equal(fs.statSync('file.txt').nlink, 1); + + fs.link('file.txt', 'link.txt', function(err) { + if (err) { + return done(err); + } + assert.equal(fs.statSync('link.txt').nlink, 2); + assert.equal(fs.statSync('file.txt').nlink, 2); + fs.unlinkSync('file.txt'); + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(fs.statSync('link.txt').nlink, 1); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + done(); + }); + }); + + withPromise.it('promise works if original is removed', function(done) { + assert.equal(fs.statSync('file.txt').nlink, 1); + + fs.promises.link('file.txt', 'link.txt').then(function() { + assert.equal(fs.statSync('link.txt').nlink, 2); + assert.equal(fs.statSync('file.txt').nlink, 2); + fs.unlinkSync('file.txt'); + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(fs.statSync('link.txt').nlink, 1); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + done(); + }, done); + }); + + it('fails if original is a directory', function(done) { + fs.link('dir', 'link', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EPERM'); + done(); + }); + }); + + withPromise.it('promise fails if original is a directory', function(done) { + fs.promises.link('dir', 'link').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EPERM'); + done(); + } + ); + }); +}); + +describe('fs.linkSync(srcpath, dstpath)', function() { + beforeEach(function() { + mock({ + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('creates a link to a file', function() { + fs.linkSync('file.txt', 'link.txt'); + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + }); + + it('works if original is renamed', function() { + fs.linkSync('file.txt', 'link.txt'); + fs.renameSync('file.txt', 'renamed.txt'); + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + }); + + it('works if original is removed', function() { + fs.linkSync('file.txt', 'link.txt'); + fs.unlinkSync('file.txt'); + assert.isTrue(fs.statSync('link.txt').isFile()); + assert.equal(String(fs.readFileSync('link.txt')), 'content'); + }); + + it('fails if original is a directory', function() { + assert.throws(function() { + fs.linkSync('dir', 'link'); + }); + }); +}); + +describe('fs.symlink(srcpath, dstpath, [type], callback)', function() { + beforeEach(function() { + mock({ + dir: {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('creates a symbolic link to a file', function(done) { + fs.symlink('../file.txt', 'dir/link.txt', function(err) { + if (err) { + return done(err); + } + assert.isTrue(fs.statSync('dir/link.txt').isFile()); + assert.equal(String(fs.readFileSync('dir/link.txt')), 'content'); + done(); + }); + }); + + withPromise.it('promise creates a symbolic link to a file', function(done) { + fs.promises.symlink('../file.txt', 'dir/link.txt').then(function() { + assert.isTrue(fs.statSync('dir/link.txt').isFile()); + assert.equal(String(fs.readFileSync('dir/link.txt')), 'content'); + done(); + }, done); + }); + + it('breaks if original is renamed', function(done) { + fs.symlink('file.txt', 'link.txt', function(err) { + if (err) { + return done(err); + } + assert.isTrue(fs.existsSync('link.txt')); + fs.renameSync('file.txt', 'renamed.txt'); + assert.isFalse(fs.existsSync('link.txt')); + done(); + }); + }); + + withPromise.it('promise breaks if original is renamed', function(done) { + fs.promises.symlink('file.txt', 'link.txt').then(function() { + assert.isTrue(fs.existsSync('link.txt')); + fs.renameSync('file.txt', 'renamed.txt'); + assert.isFalse(fs.existsSync('link.txt')); + done(); + }, done); + }); + + it('works if original is a directory', function(done) { + fs.symlink('dir', 'link', function(err) { + if (err) { + return done(err); + } + assert.isTrue(fs.statSync('link').isDirectory()); + done(); + }); + }); + + withPromise.it('promise works if original is a directory', function(done) { + fs.promises.symlink('dir', 'link').then(function() { + assert.isTrue(fs.statSync('link').isDirectory()); + done(); + }, done); + }); +}); + +describe('fs.symlinkSync(srcpath, dstpath, [type])', function() { + beforeEach(function() { + mock({ + dir: {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('creates a symbolic link to a file', function() { + fs.symlinkSync('../file.txt', 'dir/link.txt'); + assert.isTrue(fs.statSync('dir/link.txt').isFile()); + assert.equal(String(fs.readFileSync('dir/link.txt')), 'content'); + }); + + it('breaks if original is renamed', function() { + fs.symlinkSync('file.txt', 'link.txt'); + assert.isTrue(fs.existsSync('link.txt')); + fs.renameSync('file.txt', 'renamed.txt'); + assert.isFalse(fs.existsSync('link.txt')); + }); + + it('works if original is a directory', function() { + fs.symlinkSync('dir', 'link'); + assert.isTrue(fs.statSync('link').isDirectory()); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.lstat.spec.js node-mock-fs-4.10.4/test/lib/fs.lstat.spec.js --- node-mock-fs-4.10.2/test/lib/fs.lstat.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.lstat.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,124 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.lstat(path, callback)', function() { + beforeEach(function() { + mock({ + 'file.txt': mock.file({ + content: 'content', + mtime: new Date(1) + }), + link: mock.symlink({ + path: './file.txt', + mtime: new Date(2) + }) + }); + }); + afterEach(mock.restore); + + it('stats a symbolic link', function(done) { + fs.lstat('link', function(err, stats) { + if (err) { + return done(err); + } + assert.isTrue(stats.isSymbolicLink()); + assert.isFalse(stats.isFile()); + assert.equal(stats.mtime.getTime(), 2); + done(); + }); + }); + + withPromise.it('promise stats a symbolic link', function(done) { + fs.promises.lstat('link').then(function(stats) { + assert.isTrue(stats.isSymbolicLink()); + assert.isFalse(stats.isFile()); + assert.equal(stats.mtime.getTime(), 2); + done(); + }, done); + }); + + it('stats a regular file', function(done) { + fs.lstat('file.txt', function(err, stats) { + if (err) { + return done(err); + } + assert.isTrue(stats.isFile()); + assert.isFalse(stats.isSymbolicLink()); + assert.equal(stats.mtime.getTime(), 1); + done(); + }); + }); + + withPromise.it('promise stats a regular file', function(done) { + fs.promises.lstat('file.txt').then(function(stats) { + assert.isTrue(stats.isFile()); + assert.isFalse(stats.isSymbolicLink()); + assert.equal(stats.mtime.getTime(), 1); + done(); + }, done); + }); + + it('fails on file not exist', function(done) { + fs.lstat('bogus', function(err, stats) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails on file not exist', function(done) { + fs.promises.lstat('bogus').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +describe('fs.lstatSync(path)', function() { + beforeEach(function() { + mock({ + 'file.txt': mock.file({ + content: 'content', + mtime: new Date(1) + }), + link: mock.symlink({ + path: './file.txt', + mtime: new Date(2) + }) + }); + }); + afterEach(mock.restore); + + it('stats a symbolic link', function() { + const stats = fs.lstatSync('link'); + assert.isTrue(stats.isSymbolicLink()); + assert.isFalse(stats.isFile()); + assert.equal(stats.mtime.getTime(), 2); + }); + + it('stats a regular file', function() { + const stats = fs.lstatSync('file.txt'); + assert.isTrue(stats.isFile()); + assert.isFalse(stats.isSymbolicLink()); + assert.equal(stats.mtime.getTime(), 1); + }); + + it('fails on file not exist', function() { + assert.throws(function() { + fs.lstatSync('bogus'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.mkdir.spec.js node-mock-fs-4.10.4/test/lib/fs.mkdir.spec.js --- node-mock-fs-4.10.2/test/lib/fs.mkdir.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.mkdir.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,468 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const inVersion = helper.inVersion; +const withPromise = helper.withPromise; + +const testParentPerms = + fs.access && fs.accessSync && process.getuid && process.getgid; + +describe('fs.mkdir(path, [mode], callback)', function() { + beforeEach(function() { + mock({ + parent: { + 'file.md': '', + child: {} + }, + 'file.txt': '', + unwriteable: mock.directory({mode: parseInt('0555', 8)}) + }); + }); + afterEach(mock.restore); + + it('creates a new directory', function(done) { + fs.mkdir('parent/dir', function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('parent/dir'); + assert.isTrue(stats.isDirectory()); + done(); + }); + }); + + withPromise.it('promise creates a new directory', function(done) { + fs.promises.mkdir('parent/dir').then(function() { + const stats = fs.statSync('parent/dir'); + assert.isTrue(stats.isDirectory()); + done(); + }, done); + }); + + inVersion('>=10.12').it('creates a new directory recursively', function( + done + ) { + fs.mkdir('parent/foo/bar/dir', {recursive: true}, function(err) { + if (err) { + return done(err); + } + let stats = fs.statSync('parent/foo/bar/dir'); + assert.isTrue(stats.isDirectory()); + stats = fs.statSync('parent/foo/bar'); + assert.isTrue(stats.isDirectory()); + stats = fs.statSync('parent/foo'); + assert.isTrue(stats.isDirectory()); + done(); + }); + }); + + withPromise.it('promise creates a new directory recursively', function(done) { + fs.promises.mkdir('parent/foo/bar/dir', {recursive: true}).then(function() { + let stats = fs.statSync('parent/foo/bar/dir'); + assert.isTrue(stats.isDirectory()); + stats = fs.statSync('parent/foo/bar'); + assert.isTrue(stats.isDirectory()); + stats = fs.statSync('parent/foo'); + assert.isTrue(stats.isDirectory()); + done(); + }, done); + }); + + it('accepts dir mode', function(done) { + fs.mkdir('parent/dir', parseInt('0755', 8), function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('parent/dir'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + done(); + }); + }); + + withPromise.it('promise accepts dir mode', function(done) { + fs.promises.mkdir('parent/dir', parseInt('0755', 8)).then(function() { + const stats = fs.statSync('parent/dir'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + done(); + }, done); + }); + + inVersion('>=10.12').it('accepts dir mode recursively', function(done) { + fs.mkdir( + 'parent/foo/bar/dir', + {recursive: true, mode: parseInt('0755', 8)}, + function(err) { + if (err) { + return done(err); + } + let stats = fs.statSync('parent/foo/bar/dir'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + + stats = fs.statSync('parent/foo/bar'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + + stats = fs.statSync('parent/foo'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + done(); + } + ); + }); + + withPromise.it('promise accepts dir mode recursively', function(done) { + fs.promises + .mkdir('parent/foo/bar/dir', {recursive: true, mode: parseInt('0755', 8)}) + .then(function() { + let stats = fs.statSync('parent/foo/bar/dir'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + + stats = fs.statSync('parent/foo/bar'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + + stats = fs.statSync('parent/foo'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + done(); + }, done); + }); + + it('fails if parent does not exist', function(done) { + fs.mkdir('parent/bogus/dir', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails if parent does not exist', function(done) { + fs.promises.mkdir('parent/bogus/dir').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); + + inVersion('>=10.12').it( + 'fails if one parent is not a folder in recursive creation', + function(done) { + fs.mkdir('file.txt/bogus/dir', {recursive: true}, function(err) { + assert.instanceOf(err, Error); + done(); + }); + } + ); + + withPromise.it( + 'promise fails if one parent is not a folder in recursive creation', + function(done) { + fs.promises.mkdir('file.txt/bogus/dir', {recursive: true}).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + done(); + } + ); + } + ); + + inVersion('>=10.12').it( + 'fails if permission does not allow recursive creation', + function(done) { + fs.mkdir( + 'parent/foo/bar/dir', + {recursive: true, mode: parseInt('0400', 8)}, + function(err) { + assert.instanceOf(err, Error); + done(); + } + ); + } + ); + + withPromise.it( + 'promise fails if permission does not allow recursive creation', + function(done) { + fs.promises + .mkdir('parent/foo/bar/dir', { + recursive: true, + mode: parseInt('0400', 8) + }) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + done(); + } + ); + } + ); + + it('fails if directory already exists', function(done) { + fs.mkdir('parent', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + }); + }); + + withPromise.it('promise fails if directory already exists', function(done) { + fs.promises.mkdir('parent').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + } + ); + }); + + it('fails if file already exists', function(done) { + fs.mkdir('file.txt', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + }); + }); + + withPromise.it('promise fails if file already exists', function(done) { + fs.promises.mkdir('file.txt').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + } + ); + }); + + inVersion('>=10.12').it( + 'fails in recursive mode if file already exists', + function(done) { + fs.mkdir('parent/file.md', {recursive: true}, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + }); + } + ); + + withPromise.it( + 'promise fails in recursive mode if file already exists', + function(done) { + fs.promises.mkdir('parent/file.md', {recursive: true}).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + } + ); + } + ); + + inVersion('>=10.12').it( + 'passes in recursive mode if directory already exists', + function(done) { + fs.mkdir('parent/child', {recursive: true}, function(err) { + assert.isNotOk(err, Error); + done(); + }); + } + ); + + withPromise.it( + 'promise passes in recursive mode if directory already exists', + function(done) { + fs.promises.mkdir('parent/child', {recursive: true}).then(done, done); + } + ); + + if (testParentPerms) { + it('fails if parent is not writeable', function(done) { + fs.mkdir('unwriteable/child', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise fails if parent is not writeable', function(done) { + fs.promises.mkdir('unwriteable/child').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + } + + it('calls callback with a single argument on success', function(done) { + fs.mkdir('parent/arity', function(_) { + assert.equal(arguments.length, 1); + done(); + }); + }); + + it('calls callback with a single argument on failure', function(done) { + fs.mkdir('parent', function(err) { + assert.instanceOf(err, Error); + done(); + }); + }); +}); + +describe('fs.mkdirSync(path, [mode])', function() { + beforeEach(function() { + mock({ + parent: { + 'file.md': '', + child: {} + }, + 'file.txt': 'content', + unwriteable: mock.directory({mode: parseInt('0555', 8)}) + }); + }); + afterEach(mock.restore); + + it('creates a new directory', function() { + fs.mkdirSync('parent/dir'); + const stats = fs.statSync('parent/dir'); + assert.isTrue(stats.isDirectory()); + }); + + inVersion('>=10.12').it('creates a new directory recursively', function() { + fs.mkdirSync('parent/foo/bar/dir', {recursive: true}); + let stats = fs.statSync('parent/foo/bar/dir'); + assert.isTrue(stats.isDirectory()); + stats = fs.statSync('parent/foo/bar'); + assert.isTrue(stats.isDirectory()); + stats = fs.statSync('parent/foo'); + assert.isTrue(stats.isDirectory()); + }); + + it('accepts dir mode', function() { + fs.mkdirSync('parent/dir', parseInt('0755', 8)); + const stats = fs.statSync('parent/dir'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + }); + + inVersion('>=10.12').it('accepts dir mode recursively', function() { + fs.mkdirSync('parent/foo/bar/dir', { + recursive: true, + mode: parseInt('0755', 8) + }); + let stats = fs.statSync('parent/foo/bar/dir'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + + stats = fs.statSync('parent/foo/bar'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + + stats = fs.statSync('parent/foo'); + assert.isTrue(stats.isDirectory()); + assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); + }); + + it('fails if parent does not exist', function() { + assert.throws(function() { + fs.mkdirSync('parent/bogus/dir'); + }); + }); + + inVersion('>=10.12').it( + 'fails if one parent is not a folder in recursive creation', + function() { + assert.throws(function() { + fs.mkdirSync('file.txt/bogus/dir', {recursive: true}); + }); + } + ); + + inVersion('>=10.12').it( + 'fails if permission does not allow recursive creation', + function() { + assert.throws(function() { + fs.mkdirSync('parent/foo/bar/dir', { + recursive: true, + mode: parseInt('0400', 8) + }); + }); + } + ); + + it('fails if directory already exists', function() { + assert.throws(function() { + fs.mkdirSync('parent'); + }); + }); + + it('fails if file already exists', function() { + assert.throws(function() { + fs.mkdirSync('file.txt'); + }); + }); + + inVersion('>=10.12').it( + 'fails in recursive mode if file already exists', + function() { + assert.throws(function() { + fs.mkdirSync('parent/file.md', {recursive: true}); + }); + } + ); + + inVersion('>=10.12').it( + 'passes in recursive mode if directory already exists', + function() { + assert.doesNotThrow(function() { + fs.mkdirSync('parent/child', {recursive: true}); + }); + } + ); + + if (testParentPerms) { + it('fails if parent is not writeable', function() { + assert.throws(function() { + fs.mkdirSync('unwriteable/child'); + }); + }); + } +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.mkdtemp.spec.js node-mock-fs-4.10.4/test/lib/fs.mkdtemp.spec.js --- node-mock-fs-4.10.2/test/lib/fs.mkdtemp.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.mkdtemp.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,343 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const path = require('path'); + +const assert = helper.assert; +const inVersion = helper.inVersion; +const withPromise = helper.withPromise; + +const testParentPerms = + fs.access && fs.accessSync && process.getuid && process.getgid; + +if (fs.mkdtemp) { + describe('fs.mkdtemp(prefix[, options], callback)', function() { + beforeEach(function() { + mock({ + parent: {}, + file: 'contents', + unwriteable: mock.directory({mode: parseInt('0555', 8)}) + }); + }); + afterEach(mock.restore); + + it('creates a new directory', function(done) { + fs.mkdtemp('parent/dir', function(err, dirPath) { + if (err) { + return done(err); + } + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }); + }); + + withPromise.it('promise creates a new directory', function(done) { + fs.promises.mkdtemp('parent/dir').then(function(dirPath) { + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }, done); + }); + + inVersion('>=6').it('accepts a "utf8" encoding argument', function(done) { + fs.mkdtemp('parent/dir', 'utf8', function(err, dirPath) { + if (err) { + return done(err); + } + assert.isString(dirPath); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }); + }); + + withPromise.it('promise accepts a "utf8" encoding argument', function( + done + ) { + fs.promises.mkdtemp('parent/dir', 'utf8').then(function(dirPath) { + assert.isString(dirPath); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }, done); + }); + + inVersion('>=6').it('accepts a "buffer" encoding argument', function(done) { + fs.mkdtemp('parent/dir', 'buffer', function(err, buffer) { + if (err) { + return done(err); + } + assert.instanceOf(buffer, Buffer); + const dirPath = buffer.toString(); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }); + }); + + withPromise.it('promise accepts a "buffer" encoding argument', function( + done + ) { + fs.promises.mkdtemp('parent/dir', 'buffer').then(function(buffer) { + assert.instanceOf(buffer, Buffer); + const dirPath = buffer.toString(); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }, done); + }); + + inVersion('>=6').it( + 'accepts an options argument with "utf8" encoding', + function(done) { + fs.mkdtemp('parent/dir', {encoding: 'utf8'}, function(err, dirPath) { + if (err) { + return done(err); + } + assert.isString(dirPath); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }); + } + ); + + withPromise.it( + 'promise accepts an options argument with "utf8" encoding', + function(done) { + fs.promises + .mkdtemp('parent/dir', {encoding: 'utf8'}) + .then(function(dirPath) { + assert.isString(dirPath); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }, done); + } + ); + + inVersion('>=6').it( + 'accepts an options argument with "buffer" encoding', + function(done) { + fs.mkdtemp('parent/dir', {encoding: 'buffer'}, function(err, buffer) { + if (err) { + return done(err); + } + assert.instanceOf(buffer, Buffer); + const dirPath = buffer.toString(); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }); + } + ); + + withPromise.it( + 'promise accepts an options argument with "buffer" encoding', + function(done) { + fs.promises + .mkdtemp('parent/dir', {encoding: 'buffer'}) + .then(function(buffer) { + assert.instanceOf(buffer, Buffer); + const dirPath = buffer.toString(); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + done(); + }, done); + } + ); + + it('fails if parent does not exist', function(done) { + fs.mkdtemp('unknown/child', function(err, dirPath) { + if (!err || dirPath) { + done(new Error('Expected failure')); + } else { + assert.isTrue(!dirPath); + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + }); + }); + + withPromise.it('promise fails if parent does not exist', function(done) { + fs.promises.mkdtemp('unknown/child').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); + + it('fails if parent is a file', function(done) { + fs.mkdtemp('file/child', function(err, dirPath) { + if (!err || dirPath) { + done(new Error('Expected failure')); + } else { + assert.isTrue(!dirPath); + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOTDIR'); + done(); + } + }); + }); + + withPromise.it('promise fails if parent is a file', function(done) { + fs.promises.mkdtemp('file/child').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOTDIR'); + done(); + } + ); + }); + + if (testParentPerms) { + it('fails if parent is not writeable', function(done) { + fs.mkdtemp('unwriteable/child', function(err, dirPath) { + if (!err || dirPath) { + done(new Error('Expected failure')); + } else { + assert.isTrue(!dirPath); + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + }); + }); + + withPromise.it('promise fails if parent is not writeable', function( + done + ) { + fs.promises.mkdtemp('unwriteable/child').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + } + }); +} + +if (fs.mkdtempSync) { + describe('fs.mkdtempSync(prefix[, options])', function() { + beforeEach(function() { + mock({ + parent: {}, + file: 'contents', + unwriteable: mock.directory({mode: parseInt('0555', 8)}) + }); + }); + afterEach(mock.restore); + + it('creates a new directory', function() { + const dirPath = fs.mkdtempSync('parent/dir'); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + }); + + inVersion('>=6').it('accepts a "utf8" encoding argument', function() { + const dirPath = fs.mkdtempSync('parent/dir', 'utf8'); + assert.isString(dirPath); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + }); + + inVersion('>=6').it('accepts a "buffer" encoding argument', function() { + const buffer = fs.mkdtempSync('parent/dir', 'buffer'); + assert.instanceOf(buffer, Buffer); + const dirPath = buffer.toString(); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + }); + + inVersion('>=6').it( + 'accepts an options argument with "utf8" encoding', + function() { + const dirPath = fs.mkdtempSync('parent/dir', {encoding: 'utf8'}); + assert.isString(dirPath); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + } + ); + + inVersion('>=6').it( + 'accepts an options argument with "buffer" encoding', + function() { + const buffer = fs.mkdtempSync('parent/dir', {encoding: 'buffer'}); + assert.instanceOf(buffer, Buffer); + const dirPath = buffer.toString(); + const parentPath = path.dirname(dirPath); + assert.equal(parentPath, 'parent'); + const stats = fs.statSync(dirPath); + assert.isTrue(stats.isDirectory()); + } + ); + + it('fails if parent does not exist', function() { + assert.throws(function() { + fs.mkdtempSync('unknown/child'); + }); + }); + + it('fails if parent is a file', function() { + assert.throws(function() { + fs.mkdtempSync('file/child'); + }); + }); + + if (testParentPerms) { + it('fails if parent is not writeable', function() { + assert.throws(function() { + fs.mkdtempSync('unwriteable/child'); + }); + }); + } + }); +} diff -Nru node-mock-fs-4.10.2/test/lib/fs.open-close.spec.js node-mock-fs-4.10.4/test/lib/fs.open-close.spec.js --- node-mock-fs-4.10.2/test/lib/fs.open-close.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.open-close.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,255 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.open(path, flags, [mode], callback)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content', + nested: { + sub: { + dir: { + 'one.txt': 'one content', + 'two.txt': 'two content', + empty: {} + } + } + } + }); + }); + afterEach(mock.restore); + + it('opens an existing file for reading (r)', function(done) { + fs.open('nested/sub/dir/one.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + assert.isNumber(fd); + done(); + }); + }); + + withPromise.it('promise opens an existing file for reading (r)', function( + done + ) { + fs.promises.open('nested/sub/dir/one.txt', 'r').then(function(fd) { + assert.isNumber(fd.fd); + done(); + }, done); + }); + + it('fails if file does not exist (r)', function(done) { + fs.open('bogus.txt', 'r', function(err, fd) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails if file does not exist (r)', function(done) { + fs.promises.open('bogus.txt', 'r').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); + + it('creates a new file for writing (w)', function(done) { + fs.open('path/to/new.txt', 'w', parseInt('0666', 8), function(err, fd) { + if (err) { + return done(err); + } + assert.isNumber(fd); + assert.isTrue(fs.existsSync('path/to/new.txt')); + done(); + }); + }); + + withPromise.it('promise creates a new file for writing (w)', function(done) { + fs.promises + .open('path/to/new.txt', 'w', parseInt('0666', 8)) + .then(function(fd) { + assert.isNumber(fd.fd); + assert.isTrue(fs.existsSync('path/to/new.txt')); + done(); + }, done); + }); + + it('opens an existing file for writing (w)', function(done) { + fs.open('path/to/file.txt', 'w', parseInt('0666', 8), function(err, fd) { + if (err) { + return done(err); + } + assert.isNumber(fd); + done(); + }); + }); + + withPromise.it('promise opens an existing file for writing (w)', function( + done + ) { + fs.promises + .open('path/to/file.txt', 'w', parseInt('0666', 8)) + .then(function(fd) { + assert.isNumber(fd.fd); + done(); + }, done); + }); + + it('fails if file exists (wx)', function(done) { + fs.open('path/to/file.txt', 'wx', parseInt('0666', 8), function(err, fd) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + }); + }); + + withPromise.it('promise fails if file exists (wx)', function(done) { + fs.promises.open('path/to/file.txt', 'wx', parseInt('0666', 8)).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EEXIST'); + done(); + } + ); + }); +}); + +describe('fs.openSync(path, flags, [mode])', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content', + nested: { + sub: { + dir: { + 'one.txt': 'one content', + 'two.txt': 'two content', + empty: {} + } + } + } + }); + }); + afterEach(mock.restore); + + it('opens an existing file for reading (r)', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + assert.isNumber(fd); + }); + + it('fails if file does not exist (r)', function() { + assert.throws(function() { + fs.openSync('bogus.txt', 'r'); + }); + }); + + it('creates a new file for writing (w)', function() { + const fd = fs.openSync('nested/sub/new.txt', 'w', parseInt('0666', 8)); + assert.isNumber(fd); + assert.isTrue(fs.existsSync('nested/sub/new.txt')); + }); + + it('opens an existing file for writing (w)', function() { + const fd = fs.openSync('path/to/one.txt', 'w', parseInt('0666', 8)); + assert.isNumber(fd); + }); + + it('fails if file exists (wx)', function() { + assert.throws(function() { + fs.openSync('path/to/file.txt', 'wx', parseInt('0666', 8)); + }); + }); +}); + +describe('fs.close(fd, callback)', function() { + beforeEach(function() { + mock({dir: {}}); + }); + afterEach(mock.restore); + + it('closes a file descriptor', function(done) { + const fd = fs.openSync('dir/file.txt', 'w'); + fs.close(fd, function(err) { + done(err); + }); + }); + + withPromise.it('promise closes a file descriptor', function(done) { + fs.promises + .open('dir/file.txt', 'w') + .then(function(fd) { + return fd.close(); + }) + .then(done, done); + }); + + it('fails for closed file descriptors', function(done) { + const fd = fs.openSync('dir/file.txt', 'w'); + fs.close(fd, function(err) { + if (err) { + return done(err); + } + fs.close(fd, function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + }); + }); + }); + + withPromise.it('promise fails for closed file descriptors', function(done) { + fs.promises + .open('dir/file.txt', 'w') + .then(function(fd) { + return fd.close().then(function() { + return fd.close(); + }); + }) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + } + ); + }); +}); + +describe('fs.closeSync(fd)', function() { + beforeEach(function() { + mock({dir: {}}); + }); + afterEach(mock.restore); + + it('closes a file descriptor', function() { + const fd = fs.openSync('dir/file.txt', 'w'); + fs.closeSync(fd); + }); + + it('fails for closed file descriptors', function() { + const fd = fs.openSync('dir/file.txt', 'w'); + fs.closeSync(fd); + assert.throws(function() { + fs.closeSync(fd); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.readdir.spec.js node-mock-fs-4.10.4/test/lib/fs.readdir.spec.js --- node-mock-fs-4.10.2/test/lib/fs.readdir.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.readdir.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,120 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const path = require('path'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.readdir(path, callback)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content', + nested: { + sub: { + dir: { + 'one.txt': 'one content', + 'two.txt': 'two content', + empty: {} + } + } + } + }); + }); + afterEach(mock.restore); + + it('lists directory contents', function(done) { + fs.readdir(path.join('path', 'to'), function(err, items) { + assert.isNull(err); + assert.isArray(items); + assert.deepEqual(items, ['file.txt']); + done(); + }); + }); + + withPromise.it('promise lists directory contents', function(done) { + fs.promises.readdir(path.join('path', 'to')).then(function(items) { + assert.isArray(items); + assert.deepEqual(items, ['file.txt']); + done(); + }, done); + }); + + it('lists nested directory contents', function(done) { + fs.readdir(path.join('nested', 'sub', 'dir'), function(err, items) { + assert.isNull(err); + assert.isArray(items); + assert.deepEqual(items, ['empty', 'one.txt', 'two.txt']); + done(); + }); + }); + + withPromise.it('promise lists nested directory contents', function(done) { + fs.promises + .readdir(path.join('nested', 'sub', 'dir')) + .then(function(items) { + assert.isArray(items); + assert.deepEqual(items, ['empty', 'one.txt', 'two.txt']); + done(); + }, done); + }); + + it('calls with an error for bogus path', function(done) { + fs.readdir('bogus', function(err, items) { + assert.instanceOf(err, Error); + assert.isUndefined(items); + done(); + }); + }); + + withPromise.it('promise calls with an error for bogus path', function(done) { + fs.promises.readdir('bogus').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + done(); + } + ); + }); +}); + +describe('fs.readdirSync(path)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content', + nested: { + sub: { + dir: { + 'one.txt': 'one content', + 'two.txt': 'two content', + empty: {} + } + } + } + }); + }); + afterEach(mock.restore); + + it('lists directory contents', function() { + const items = fs.readdirSync(path.join('path', 'to')); + assert.isArray(items); + assert.deepEqual(items, ['file.txt']); + }); + + it('lists nested directory contents', function() { + const items = fs.readdirSync(path.join('nested', 'sub', 'dir')); + assert.isArray(items); + assert.deepEqual(items, ['empty', 'one.txt', 'two.txt']); + }); + + it('throws for bogus path', function() { + assert.throws(function() { + fs.readdirSync('bogus'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.readFile.spec.js node-mock-fs-4.10.4/test/lib/fs.readFile.spec.js --- node-mock-fs-4.10.2/test/lib/fs.readFile.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.readFile.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,117 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.readFile(filename, [options], callback)', function() { + // this is provided by fs.open, fs.fstat, and fs.read + // so more heavily tested elsewhere + + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('allows a file to be read asynchronously', function(done) { + fs.readFile('path/to/file.txt', function(err, data) { + if (err) { + return done(err); + } + assert.isTrue(Buffer.isBuffer(data)); + assert.equal(String(data), 'file content'); + done(); + }); + }); + + withPromise.it('promise allows a file to be read asynchronously', function( + done + ) { + fs.promises.readFile('path/to/file.txt').then(function(data) { + assert.isTrue(Buffer.isBuffer(data)); + assert.equal(String(data), 'file content'); + done(); + }, done); + }); + + it('fails for directory', function(done) { + fs.readFile('path/to', function(err, data) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + }); + }); + + withPromise.it('promise fails for directory', function(done) { + fs.promises.readFile('path/to').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + } + ); + }); + + it('fails for bad path', function(done) { + fs.readFile('path/to/bogus', function(err, data) { + assert.instanceOf(err, Error); + // windows has different errno for ENOENT + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails for bad path', function(done) { + fs.promises.readFile('path/to/bogus').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + // windows has different errno for ENOENT + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +describe('fs.readFileSync(filename, [options])', function() { + // this is provided by fs.openSync, fs.fstatSync, and fs.readSync + // so more heavily tested elsewhere + + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('allows a file to be read synchronously', function() { + const data = fs.readFileSync('path/to/file.txt'); + assert.isTrue(Buffer.isBuffer(data)); + assert.equal(String(data), 'file content'); + }); + + it('fails for directory', function() { + assert.throws(function() { + fs.readFileSync('path/to'); + }); + }); + + it('fails for bad path', function() { + assert.throws(function() { + fs.readFileSync('path/to/bogus'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.readlink.spec.js node-mock-fs-4.10.4/test/lib/fs.readlink.spec.js --- node-mock-fs-4.10.2/test/lib/fs.readlink.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.readlink.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,75 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.readlink(path, callback)', function() { + beforeEach(function() { + mock({ + 'file.txt': 'content', + link: mock.symlink({path: './file.txt'}) + }); + }); + afterEach(mock.restore); + + it('reads a symbolic link', function(done) { + fs.readlink('link', function(err, srcPath) { + if (err) { + return done(err); + } + assert.equal(srcPath, './file.txt'); + done(); + }); + }); + + withPromise.it('promise reads a symbolic link', function(done) { + fs.promises.readlink('link').then(function(srcPath) { + assert.equal(srcPath, './file.txt'); + done(); + }, done); + }); + + it('fails for regular files', function(done) { + fs.readlink('file.txt', function(err, srcPath) { + assert.instanceOf(err, Error); + done(); + }); + }); + + withPromise.it('promise fails for regular files', function(done) { + fs.promises.readlink('file.txt').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + done(); + } + ); + }); +}); + +describe('fs.readlinkSync(path)', function() { + beforeEach(function() { + mock({ + 'file.txt': 'content', + link: mock.symlink({path: './file.txt'}) + }); + }); + afterEach(mock.restore); + + it('reads a symbolic link', function() { + assert.equal(fs.readlinkSync('link'), './file.txt'); + }); + + it('fails for regular files', function() { + assert.throws(function() { + fs.readlinkSync('file.txt'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.read.spec.js node-mock-fs-4.10.4/test/lib/fs.read.spec.js --- node-mock-fs-4.10.2/test/lib/fs.read.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.read.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,330 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const bufferAlloc = require('../../lib/buffer').alloc; + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.read(fd, buffer, offset, length, position, callback)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('allows file contents to be read', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferAlloc(12); + fs.read(fd, buffer, 0, 12, 0, function(err2, bytesRead, buf) { + if (err2) { + return done(err2); + } + assert.equal(bytesRead, 12); + assert.equal(buf, buffer); + assert.equal(String(buffer), 'file content'); + done(); + }); + }); + }); + + withPromise.it('promise allows file contents to be read', function(done) { + const buffer = bufferAlloc(12); + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.read(buffer, 0, 12, 0); + }) + .then(function(result) { + assert.equal(result.bytesRead, 12); + assert.equal(result.buffer, buffer); + assert.equal(String(buffer), 'file content'); + done(); + }, done); + }); + + it('allows file contents to be read w/ offset', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferAlloc(12); + fs.read(fd, buffer, 5, 7, 0, function(err2, bytesRead, buf) { + if (err2) { + return done(err2); + } + assert.equal(bytesRead, 7); + assert.equal(buf, buffer); + assert.equal(String(buffer.slice(5)), 'file co'); + done(); + }); + }); + }); + + withPromise.it('promise allows file contents to be read w/ offset', function( + done + ) { + const buffer = bufferAlloc(12); + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.read(buffer, 5, 7, 0); + }) + .then(function(result) { + assert.equal(result.bytesRead, 7); + assert.equal(result.buffer, buffer); + assert.equal(String(buffer.slice(5)), 'file co'); + done(); + }, done); + }); + + it('allows file contents to be read w/ length', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferAlloc(12); + fs.read(fd, buffer, 0, 4, 0, function(err2, bytesRead, buf) { + if (err2) { + return done(err2); + } + assert.equal(bytesRead, 4); + assert.equal(buf, buffer); + assert.equal(String(buffer.slice(0, 4)), 'file'); + done(); + }); + }); + }); + + withPromise.it('promise allows file contents to be read w/ length', function( + done + ) { + const buffer = bufferAlloc(12); + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.read(buffer, 0, 4, 0); + }) + .then(function(result) { + assert.equal(result.bytesRead, 4); + assert.equal(result.buffer, buffer); + assert.equal(String(buffer.slice(0, 4)), 'file'); + done(); + }, done); + }); + + it('allows file contents to be read w/ offset & length', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferAlloc(12); + fs.read(fd, buffer, 2, 4, 0, function(err2, bytesRead, buf) { + if (err2) { + return done(err2); + } + assert.equal(bytesRead, 4); + assert.equal(buf, buffer); + assert.equal(String(buffer.slice(2, 6)), 'file'); + done(); + }); + }); + }); + + withPromise.it( + 'promise allows file contents to be read w/ offset & length', + function(done) { + const buffer = bufferAlloc(12); + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.read(buffer, 2, 4, 0); + }) + .then(function(result) { + assert.equal(result.bytesRead, 4); + assert.equal(result.buffer, buffer); + assert.equal(String(buffer.slice(2, 6)), 'file'); + done(); + }, done); + } + ); + + it('allows file contents to be read w/ position', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferAlloc(7); + fs.read(fd, buffer, 0, 7, 5, function(err2, bytesRead, buf) { + if (err2) { + return done(err2); + } + assert.equal(bytesRead, 7); + assert.equal(buf, buffer); + assert.equal(String(buffer), 'content'); + done(); + }); + }); + }); + + withPromise.it( + 'promise allows file contents to be read w/ position', + function(done) { + const buffer = bufferAlloc(7); + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.read(buffer, 0, 7, 5); + }) + .then(function(result) { + assert.equal(result.bytesRead, 7); + assert.equal(result.buffer, buffer); + assert.equal(String(buffer), 'content'); + done(); + }, done); + } + ); + + it('allows read w/ offset, length, & position', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferAlloc(12); + fs.read(fd, buffer, 2, 7, 5, function(err2, bytesRead, buf) { + if (err2) { + return done(err2); + } + assert.equal(bytesRead, 7); + assert.equal(buf, buffer); + assert.equal(String(buffer.slice(2, 9)), 'content'); + done(); + }); + }); + }); + + withPromise.it('promise allows read w/ offset, length, & position', function( + done + ) { + const buffer = bufferAlloc(12); + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.read(buffer, 2, 7, 5); + }) + .then(function(result) { + assert.equal(result.bytesRead, 7); + assert.equal(result.buffer, buffer); + assert.equal(String(buffer.slice(2, 9)), 'content'); + done(); + }, done); + }); + + it('fails for closed file descriptor', function(done) { + const fd = fs.openSync('path/to/file.txt', 'r'); + fs.closeSync(fd); + fs.read(fd, bufferAlloc(12), 0, 12, 0, function(err, bytesRead, buf) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + assert.equal(0, bytesRead); + done(); + }); + }); + + withPromise.it('promise fails for closed file descriptor', function(done) { + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.close().then(function() { + return fd.read(bufferAlloc(12), 0, 12, 0); + }); + }) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + } + ); + }); + + it('fails if not open for reading', function(done) { + const fd = fs.openSync('path/to/file.txt', 'w'); + fs.read(fd, bufferAlloc(12), 0, 12, 0, function(err, bytesRead, buf) { + assert.instanceOf(err, Error); + assert.equal(0, bytesRead); + done(); + }); + }); + + withPromise.it('promise fails if not open for reading', function(done) { + fs.promises + .open('path/to/file.txt', 'w') + .then(function(fd) { + return fd.read(bufferAlloc(12), 0, 12, 0); + }) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + } + ); + }); +}); + +describe('fs.readSync(fd, buffer, offset, length, position)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('allows a file to be read synchronously', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + const buffer = bufferAlloc(12); + const read = fs.readSync(fd, buffer, 0, 12, 0); + assert.equal(read, 12); + assert.equal(String(buffer), 'file content'); + }); + + it('allows a file to be read in two parts', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + const first = bufferAlloc(4); + fs.readSync(fd, first, 0, 4, 0); + assert.equal(String(first), 'file'); + + const second = bufferAlloc(7); + fs.readSync(fd, second, 0, 7, 5); + assert.equal(String(second), 'content'); + }); + + it('treats null position as current position', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + const first = bufferAlloc(4); + fs.readSync(fd, first, 0, 4, null); + assert.equal(String(first), 'file'); + + // consume the space + assert.equal(fs.readSync(fd, bufferAlloc(1), 0, 1, null), 1); + + const second = bufferAlloc(7); + fs.readSync(fd, second, 0, 7, null); + assert.equal(String(second), 'content'); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.realpath.spec.js node-mock-fs-4.10.4/test/lib/fs.realpath.spec.js --- node-mock-fs-4.10.2/test/lib/fs.realpath.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.realpath.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,172 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const path = require('path'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const assertEqualPaths = helper.assertEqualPaths; +const withPromise = helper.withPromise; + +describe('fs.realpath(path, [cache], callback)', function() { + beforeEach(function() { + mock({ + 'dir/file.txt': 'content', + link: mock.symlink({path: './dir/file.txt'}) + }); + }); + afterEach(mock.restore); + + it('resolves the real path for a symbolic link', function(done) { + fs.realpath('link', function(err, resolved) { + if (err) { + return done(err); + } + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + done(); + }); + }); + + withPromise.it('promise resolves the real path for a symbolic link', function( + done + ) { + fs.promises.realpath('link').then(function(resolved) { + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + done(); + }, done); + }); + + it('resolves the real path regular file', function(done) { + fs.realpath('dir/file.txt', function(err, resolved) { + if (err) { + return done(err); + } + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + done(); + }); + }); + + withPromise.it('promise resolves the real path regular file', function(done) { + fs.promises.realpath('dir/file.txt').then(function(resolved) { + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + done(); + }, done); + }); + + it('fails on file not exist', function(done) { + fs.realpath('bogus', function(err, resolved) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails on file not exist', function(done) { + fs.promises.realpath('bogus').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +if (fs.realpath.native) { + describe('fs.realpath.native(path, [cache], callback)', function() { + beforeEach(function() { + mock({ + 'dir/file.txt': 'content', + link: mock.symlink({path: './dir/file.txt'}) + }); + }); + afterEach(mock.restore); + + it('resolves the real path for a symbolic link', function(done) { + fs.realpath.native('link', function(err, resolved) { + if (err) { + return done(err); + } + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + done(); + }); + }); + + it('resolves the real path regular file', function(done) { + fs.realpath.native('dir/file.txt', function(err, resolved) { + if (err) { + return done(err); + } + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + done(); + }); + }); + + it('fails on file not exist', function(done) { + fs.realpath.native('bogus', function(err, resolved) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + }); +} + +describe('fs.realpathSync(path, [cache])', function() { + beforeEach(function() { + mock({ + 'dir/file.txt': 'content', + link: mock.symlink({path: './dir/file.txt'}) + }); + }); + afterEach(mock.restore); + + it('resolves the real path for a symbolic link', function() { + const resolved = fs.realpathSync('link'); + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + }); + + it('resolves the real path regular file', function() { + const resolved = fs.realpathSync('dir/file.txt'); + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + }); + + it('fails on file not exist', function() { + assert.throws(function() { + fs.realpathSync('bogus'); + }); + }); +}); + +if (fs.realpathSync.native) { + describe('fs.realpathSync.native(path, [cache])', function() { + beforeEach(function() { + mock({ + 'dir/file.txt': 'content', + link: mock.symlink({path: './dir/file.txt'}) + }); + }); + afterEach(mock.restore); + + it('resolves the real path for a symbolic link', function() { + const resolved = fs.realpathSync.native('link'); + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + }); + + it('resolves the real path regular file', function() { + const resolved = fs.realpathSync.native('dir/file.txt'); + assertEqualPaths(resolved, path.resolve('dir/file.txt')); + }); + + it('fails on file not exist', function() { + assert.throws(function() { + fs.realpathSync.native('bogus'); + }); + }); + }); +} diff -Nru node-mock-fs-4.10.2/test/lib/fs.rename.spec.js node-mock-fs-4.10.4/test/lib/fs.rename.spec.js --- node-mock-fs-4.10.2/test/lib/fs.rename.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.rename.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,216 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const bufferFrom = require('../../lib/buffer').from; + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.rename(oldPath, newPath, callback)', function() { + beforeEach(function() { + mock({ + 'path/to/a.bin': bufferFrom([1, 2, 3]), + empty: {}, + nested: { + dir: mock.directory({ + mtime: new Date(1), + items: {'file.txt': ''} + }) + } + }); + }); + afterEach(mock.restore); + + it('allows files to be renamed', function(done) { + fs.rename('path/to/a.bin', 'path/to/b.bin', function(err) { + assert.isTrue(!err); + assert.isFalse(fs.existsSync('path/to/a.bin')); + assert.isTrue(fs.existsSync('path/to/b.bin')); + done(); + }); + }); + + withPromise.it('promise allows files to be renamed', function(done) { + fs.promises.rename('path/to/a.bin', 'path/to/b.bin').then(function() { + assert.isFalse(fs.existsSync('path/to/a.bin')); + assert.isTrue(fs.existsSync('path/to/b.bin')); + done(); + }, done); + }); + + it('updates mtime of parent directory', function(done) { + const oldTime = fs.statSync('nested/dir').mtime; + fs.rename('nested/dir/file.txt', 'nested/dir/renamed.txt', function(err) { + assert.isTrue(!err); + assert.isFalse(fs.existsSync('nested/dir/file.txt')); + assert.isTrue(fs.existsSync('nested/dir/renamed.txt')); + const newTime = fs.statSync('nested/dir').mtime; + assert.isTrue(newTime > oldTime); + done(); + }); + }); + + withPromise.it('promise updates mtime of parent directory', function(done) { + const oldTime = fs.statSync('nested/dir').mtime; + fs.promises + .rename('nested/dir/file.txt', 'nested/dir/renamed.txt') + .then(function() { + assert.isFalse(fs.existsSync('nested/dir/file.txt')); + assert.isTrue(fs.existsSync('nested/dir/renamed.txt')); + const newTime = fs.statSync('nested/dir').mtime; + assert.isTrue(newTime > oldTime); + done(); + }, done); + }); + + it('calls callback with error if old path does not exist', function(done) { + fs.rename('bogus', 'empty', function(err) { + assert.instanceOf(err, Error); + done(); + }); + }); + + withPromise.it( + 'promise calls callback with error if old path does not exist', + function(done) { + fs.promises.rename('bogus', 'empty').then( + function() { + assert.fail('Should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + done(); + } + ); + } + ); + + it('overwrites existing files', function(done) { + fs.rename('path/to/a.bin', 'nested/dir/file.txt', function(err) { + assert.isTrue(!err); + assert.isFalse(fs.existsSync('path/to/a.bin')); + assert.isTrue(fs.existsSync('nested/dir/file.txt')); + done(); + }); + }); + + withPromise.it('promise overwrites existing files', function(done) { + fs.promises.rename('path/to/a.bin', 'nested/dir/file.txt').then(function() { + assert.isFalse(fs.existsSync('path/to/a.bin')); + assert.isTrue(fs.existsSync('nested/dir/file.txt')); + done(); + }, done); + }); + + it('allows directories to be renamed', function(done) { + fs.rename('path/to', 'path/foo', function(err) { + assert.isTrue(!err); + assert.isFalse(fs.existsSync('path/to')); + assert.isTrue(fs.existsSync('path/foo')); + assert.deepEqual(fs.readdirSync('path/foo'), ['a.bin']); + done(); + }); + }); + + withPromise.it('promise allows directories to be renamed', function(done) { + fs.promises.rename('path/to', 'path/foo').then(function() { + assert.isFalse(fs.existsSync('path/to')); + assert.isTrue(fs.existsSync('path/foo')); + assert.deepEqual(fs.readdirSync('path/foo'), ['a.bin']); + done(); + }, done); + }); + + it('calls callback with error if new directory not empty', function(done) { + fs.rename('path', 'nested', function(err) { + assert.instanceOf(err, Error); + done(); + }); + }); + + withPromise.it( + 'promise calls callback with error if new directory not empty', + function(done) { + fs.promises.rename('path', 'nested').then( + function() { + assert.fail('Should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + done(); + } + ); + } + ); +}); + +describe('fs.renameSync(oldPath, newPath)', function() { + beforeEach(function() { + mock({ + 'path/to/a.bin': bufferFrom([1, 2, 3]), + empty: {}, + nested: { + dir: { + 'file.txt': '' + } + }, + link: mock.symlink({path: './path/to/a.bin'}) + }); + }); + afterEach(mock.restore); + + it('allows files to be renamed', function() { + fs.renameSync('path/to/a.bin', 'path/to/b.bin'); + assert.isFalse(fs.existsSync('path/to/a.bin')); + assert.isTrue(fs.existsSync('path/to/b.bin')); + }); + + it('overwrites existing files', function() { + fs.renameSync('path/to/a.bin', 'nested/dir/file.txt'); + assert.isFalse(fs.existsSync('path/to/a.bin')); + assert.isTrue(fs.existsSync('nested/dir/file.txt')); + }); + + it('allows directories to be renamed', function() { + fs.renameSync('path/to', 'path/foo'); + assert.isFalse(fs.existsSync('path/to')); + assert.isTrue(fs.existsSync('path/foo')); + assert.deepEqual(fs.readdirSync('path/foo'), ['a.bin']); + }); + + it('replaces existing directories (if empty)', function() { + fs.renameSync('path/to', 'empty'); + assert.isFalse(fs.existsSync('path/to')); + assert.isTrue(fs.existsSync('empty')); + assert.deepEqual(fs.readdirSync('empty'), ['a.bin']); + }); + + it('renames symbolic links', function() { + fs.renameSync('link', 'renamed'); + assert.isTrue(fs.existsSync('renamed')); + assert.isFalse(fs.existsSync('link')); + assert.isTrue(fs.existsSync('path/to/a.bin')); + }); + + it('throws if old path does not exist', function() { + assert.throws(function() { + fs.renameSync('bogus', 'empty'); + }); + }); + + it('throws if new path basename is not directory', function() { + assert.throws(function() { + fs.renameSync('path/to/a.bin', 'bogus/a.bin'); + }); + }); + + it('throws if new dir is not empty dir', function() { + assert.throws(function() { + fs.renameSync('path/to', 'nested'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.rmdir.spec.js node-mock-fs-4.10.4/test/lib/fs.rmdir.spec.js --- node-mock-fs-4.10.2/test/lib/fs.rmdir.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.rmdir.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,138 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +const testParentPerms = + fs.access && fs.accessSync && process.getuid && process.getgid; + +describe('fs.rmdir(path, callback)', function() { + beforeEach(function() { + mock({ + 'path/to/empty': {}, + unwriteable: mock.directory({ + mode: parseInt('0555', 8), + items: {child: {}} + }) + }); + }); + afterEach(mock.restore); + + it('removes an empty directory', function(done) { + assert.equal(fs.statSync('path/to').nlink, 3); + + fs.rmdir('path/to/empty', function(err) { + if (err) { + return done(err); + } + assert.isFalse(fs.existsSync('path/to/empty')); + assert.equal(fs.statSync('path/to').nlink, 2); + done(); + }); + }); + + withPromise.it('promise removes an empty directory', function(done) { + assert.equal(fs.statSync('path/to').nlink, 3); + + fs.promises.rmdir('path/to/empty').then(function() { + assert.isFalse(fs.existsSync('path/to/empty')); + assert.equal(fs.statSync('path/to').nlink, 2); + done(); + }, done); + }); + + it('fails if not empty', function(done) { + fs.rmdir('path/to', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOTEMPTY'); + done(); + }); + }); + + withPromise.it('promise fails if not empty', function(done) { + fs.promises.rmdir('path/to').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOTEMPTY'); + done(); + } + ); + }); + + if (testParentPerms) { + it('fails if parent is not writeable', function(done) { + fs.rmdir('unwriteable/child', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + }); + }); + + withPromise.it('promise fails if parent is not writeable', function(done) { + fs.promises.rmdir('unwriteable/child').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); + done(); + } + ); + }); + } +}); + +describe('fs.rmdirSync(path)', function() { + beforeEach(function() { + mock({ + 'path/empty': {}, + 'file.txt': 'content', + unwriteable: mock.directory({ + mode: parseInt('0555', 8), + items: {child: {}} + }) + }); + }); + afterEach(mock.restore); + + it('removes an empty directory', function() { + fs.rmdirSync('path/empty'); + assert.isFalse(fs.existsSync('path/empty')); + }); + + it('fails if directory does not exist', function() { + assert.throws(function() { + fs.rmdirSync('path/bogus'); + }); + }); + + it('fails if not empty', function() { + assert.throws(function() { + fs.rmdirSync('path'); + }); + }); + + it('fails if file', function() { + assert.throws(function() { + fs.rmdirSync('file.txt'); + }); + }); + + if (testParentPerms) { + it('fails if parent is not writeable', function() { + assert.throws(function() { + fs.rmdirSync('unwriteable/child'); + }); + }); + } +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.stat-fstat.spec.js node-mock-fs-4.10.4/test/lib/fs.stat-fstat.spec.js --- node-mock-fs-4.10.2/test/lib/fs.stat-fstat.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.stat-fstat.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,340 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const semver = require('semver'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.stat(path, callback)', function() { + beforeEach(function() { + mock({ + '/path/to/file.txt': mock.file({ + ctime: new Date(1), + mtime: new Date(2), + atime: new Date(3), + uid: 42, + gid: 43 + }), + '/dir/symlink': mock.symlink({path: '/path/to/file.txt'}), + '/empty': {} + }); + }); + afterEach(mock.restore); + + xit('creates an instance of fs.Stats', function(done) { + fs.stat('/path/to/file.txt', function(err, stats) { + if (err) { + return done(err); + } + assert.instanceOf(stats, fs.Stats); + done(); + }); + }); + + withPromise.xit('promise creates an instance of fs.Stats', function(done) { + fs.promises.stat('/path/to/file.txt').then(function(stats) { + assert.instanceOf(stats, fs.Stats); + done(); + }, done); + }); + + it('identifies files', function(done) { + fs.stat('/path/to/file.txt', function(err, stats) { + if (err) { + return done(err); + } + assert.isTrue(stats.isFile()); + assert.isFalse(stats.isDirectory()); + done(); + }); + }); + + withPromise.it('promise identifies files', function(done) { + fs.promises.stat('/path/to/file.txt').then(function(stats) { + assert.isTrue(stats.isFile()); + assert.isFalse(stats.isDirectory()); + done(); + }, done); + }); + + it('identifies directories', function(done) { + fs.stat('/empty', function(err, stats) { + if (err) { + return done(err); + } + assert.isTrue(stats.isDirectory()); + assert.isFalse(stats.isFile()); + done(); + }); + }); + + withPromise.it('promise identifies directories', function(done) { + fs.promises.stat('/empty').then(function(stats) { + assert.isTrue(stats.isDirectory()); + assert.isFalse(stats.isFile()); + done(); + }, done); + }); + + it('provides file stats', function(done) { + fs.stat('/path/to/file.txt', function(err, stats) { + if (err) { + return done(err); + } + assert.equal(stats.ctime.getTime(), 1); + assert.equal(stats.mtime.getTime(), 2); + assert.equal(stats.atime.getTime(), 3); + assert.equal(stats.uid, 42); + assert.equal(stats.gid, 43); + assert.equal(stats.nlink, 1); + assert.isNumber(stats.rdev); + done(); + }); + }); + + withPromise.it('promise provides file stats', function(done) { + fs.promises.stat('/path/to/file.txt').then(function(stats) { + assert.equal(stats.ctime.getTime(), 1); + assert.equal(stats.mtime.getTime(), 2); + assert.equal(stats.atime.getTime(), 3); + assert.equal(stats.uid, 42); + assert.equal(stats.gid, 43); + assert.equal(stats.nlink, 1); + assert.isNumber(stats.rdev); + done(); + }, done); + }); + + if ( + process.platform !== 'win32' || + semver.coerce(process.version).major !== 10 + ) { + // The fix for https://github.com/nodejs/node/issues/25913 + // is not shipped in v10. But it's shipped in v12. + it('includes blocks and blksize in stats', function(done) { + fs.stat('/path/to/file.txt', function(err, stats) { + if (err) { + return done(err); + } + assert.isNumber(stats.blocks); + assert.isNumber(stats.blksize); + done(); + }); + }); + + withPromise.it('promise includes blocks and blksize in stats', function( + done + ) { + fs.promises.stat('/path/to/file.txt').then(function(stats) { + assert.isNumber(stats.blocks); + assert.isNumber(stats.blksize); + done(); + }, done); + }); + } + + it('provides directory stats', function(done) { + fs.stat('/path', function(err, stats) { + if (err) { + return done(err); + } + assert.instanceOf(stats.ctime, Date); + assert.instanceOf(stats.mtime, Date); + assert.instanceOf(stats.atime, Date); + if (process.getuid) { + assert.isNumber(stats.uid); + } else { + assert.isNaN(stats.uid); + } + if (process.getgid) { + assert.isNumber(stats.gid); + } else { + assert.isNaN(stats.gid); + } + assert.equal(stats.nlink, 3); + assert.isNumber(stats.rdev); + done(); + }); + }); + + withPromise.it('promise provides directory stats', function(done) { + fs.promises.stat('/path').then(function(stats) { + assert.instanceOf(stats.ctime, Date); + assert.instanceOf(stats.mtime, Date); + assert.instanceOf(stats.atime, Date); + if (process.getuid) { + assert.isNumber(stats.uid); + } else { + assert.isNaN(stats.uid); + } + if (process.getgid) { + assert.isNumber(stats.gid); + } else { + assert.isNaN(stats.gid); + } + assert.equal(stats.nlink, 3); + assert.isNumber(stats.rdev); + done(); + }, done); + }); + + if ( + process.platform !== 'win32' || + semver.coerce(process.version).major !== 10 + ) { + // The fix for https://github.com/nodejs/node/issues/25913 + // is not shipped in v10. But it's shipped in v12. + it('includes blocks and blksize in directory stats', function(done) { + fs.stat('/path', function(err, stats) { + if (err) { + return done(err); + } + assert.isNumber(stats.blocks); + assert.isNumber(stats.blksize); + done(); + }); + }); + + withPromise.it( + 'promise includes blocks and blksize in directory stats', + function(done) { + fs.promises.stat('/path').then(function(stats) { + assert.isNumber(stats.blocks); + assert.isNumber(stats.blksize); + done(); + }, done); + } + ); + } +}); + +describe('fs.fstat(fd, callback)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content', + empty: {} + }); + }); + afterEach(mock.restore); + + it('accepts a file descriptor for a file (r)', function(done) { + const fd = fs.openSync('path/to/file.txt', 'r'); + fs.fstat(fd, function(err, stats) { + if (err) { + return done(err); + } + assert.isTrue(stats.isFile()); + assert.equal(stats.size, 12); + done(); + }); + }); + + withPromise.it('promise accepts a file descriptor for a file (r)', function( + done + ) { + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.stat(); + }) + .then(function(stats) { + assert.isTrue(stats.isFile()); + assert.equal(stats.size, 12); + done(); + }, done); + }); + + it('accepts a file descriptor for a directory (r)', function(done) { + const fd = fs.openSync('path/to', 'r'); + fs.fstat(fd, function(err, stats) { + if (err) { + return done(err); + } + assert.isTrue(stats.isDirectory()); + assert.isTrue(stats.size > 0); + done(); + }); + }); + + withPromise.it( + 'promise accepts a file descriptor for a directory (r)', + function(done) { + fs.promises + .open('path/to', 'r') + .then(function(fd) { + return fd.stat(); + }) + .then(function(stats) { + assert.isTrue(stats.isDirectory()); + assert.isTrue(stats.size > 0); + done(); + }, done); + } + ); + + it('fails for bad file descriptor', function(done) { + const fd = fs.openSync('path/to/file.txt', 'r'); + fs.closeSync(fd); + fs.fstat(fd, function(err, stats) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + }); + }); + + withPromise.it('promise fails for bad file descriptor', function(done) { + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.close().then(function() { + return fd.stat(); + }); + }) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + } + ); + }); +}); + +describe('fs.fstatSync(fd)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content', + empty: {} + }); + }); + afterEach(mock.restore); + + it('accepts a file descriptor for a file (r)', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + const stats = fs.fstatSync(fd); + assert.isTrue(stats.isFile()); + assert.equal(stats.size, 12); + }); + + it('accepts a file descriptor for a directory (r)', function() { + const fd = fs.openSync('path/to', 'r'); + const stats = fs.fstatSync(fd); + assert.isTrue(stats.isDirectory()); + assert.isTrue(stats.size > 0); + }); + + it('fails for bad file descriptor', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + fs.closeSync(fd); + assert.throws(function() { + fs.fstatSync(fd); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.unlink.spec.js node-mock-fs-4.10.4/test/lib/fs.unlink.spec.js --- node-mock-fs-4.10.2/test/lib/fs.unlink.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.unlink.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,144 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const bufferAlloc = require('../../lib/buffer').alloc; + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.unlink(path, callback)', function() { + beforeEach(function() { + mock({ + dir: {}, + dir2: mock.directory({ + mtime: new Date(1), + items: {file: 'content here'} + }), + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('deletes a file', function(done) { + fs.unlink('file.txt', function(err) { + if (err) { + return done(err); + } + assert.isFalse(fs.existsSync('file.txt')); + done(); + }); + }); + + withPromise.it('promise deletes a file', function(done) { + fs.promises.unlink('file.txt').then(function() { + assert.isFalse(fs.existsSync('file.txt')); + done(); + }, done); + }); + + it('updates mtime of parent', function(done) { + const oldTime = fs.statSync('dir2').mtime; + fs.unlink('dir2/file', function(err) { + if (err) { + return done(err); + } + assert.isFalse(fs.existsSync('dir2/file')); + const newTime = fs.statSync('dir2').mtime; + assert.isTrue(newTime > oldTime); + done(); + }); + }); + + withPromise.it('updates mtime of parent', function(done) { + const oldTime = fs.statSync('dir2').mtime; + fs.promises.unlink('dir2/file').then(function() { + assert.isFalse(fs.existsSync('dir2/file')); + const newTime = fs.statSync('dir2').mtime; + assert.isTrue(newTime > oldTime); + done(); + }, done); + }); + + it('fails for a directory', function(done) { + fs.unlink('dir', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EPERM'); + assert.isTrue(fs.existsSync('dir')); + done(); + }); + }); + + withPromise.it('promise fails for a directory', function(done) { + fs.promises.unlink('dir').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EPERM'); + assert.isTrue(fs.existsSync('dir')); + done(); + } + ); + }); + + it('respects previously opened file descriptors', function(done) { + const fd = fs.openSync('file.txt', 'r'); + fs.unlink('file.txt', function(err) { + if (err) { + return done(err); + } + assert.isFalse(fs.existsSync('file.txt')); + // but we can still use fd to read + const buffer = bufferAlloc(7); + const read = fs.readSync(fd, buffer, 0, 7); + assert.equal(read, 7); + assert.equal(String(buffer), 'content'); + done(); + }); + }); + + withPromise.it( + 'promise respects previously opened file descriptors', + function(done) { + const fd = fs.openSync('file.txt', 'r'); + fs.promises.unlink('file.txt').then(function() { + assert.isFalse(fs.existsSync('file.txt')); + // but we can still use fd to read + const buffer = bufferAlloc(7); + const read = fs.readSync(fd, buffer, 0, 7); + assert.equal(read, 7); + assert.equal(String(buffer), 'content'); + done(); + }, done); + } + ); +}); + +describe('fs.unlinkSync(path)', function() { + beforeEach(function() { + mock({ + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('deletes a file', function() { + fs.unlinkSync('file.txt'); + assert.isFalse(fs.existsSync('file.txt')); + }); + + it('respects previously opened file descriptors', function() { + const fd = fs.openSync('file.txt', 'r'); + fs.unlinkSync('file.txt'); + assert.isFalse(fs.existsSync('file.txt')); + // but we can still use fd to read + const buffer = bufferAlloc(7); + const read = fs.readSync(fd, buffer, 0, 7); + assert.equal(read, 7); + assert.equal(String(buffer), 'content'); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.utimes-futimes.spec.js node-mock-fs-4.10.4/test/lib/fs.utimes-futimes.spec.js --- node-mock-fs-4.10.2/test/lib/fs.utimes-futimes.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.utimes-futimes.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,181 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.utimes(path, atime, mtime, callback)', function() { + beforeEach(function() { + mock({ + dir: {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('updates timestamps for a file', function(done) { + fs.utimes('file.txt', new Date(100), new Date(200), function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('file.txt'); + assert.equal(stats.atime.getTime(), 100); + assert.equal(stats.mtime.getTime(), 200); + done(); + }); + }); + + withPromise.it('promise updates timestamps for a file', function(done) { + fs.promises + .utimes('file.txt', new Date(100), new Date(200)) + .then(function() { + const stats = fs.statSync('file.txt'); + assert.equal(stats.atime.getTime(), 100); + assert.equal(stats.mtime.getTime(), 200); + done(); + }, done); + }); + + it('updates timestamps for a directory', function(done) { + fs.utimes('dir', new Date(300), new Date(400), function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('dir'); + assert.equal(stats.atime.getTime(), 300); + assert.equal(stats.mtime.getTime(), 400); + done(); + }); + }); + + withPromise.it('promise updates timestamps for a directory', function(done) { + fs.promises.utimes('dir', new Date(300), new Date(400)).then(function() { + const stats = fs.statSync('dir'); + assert.equal(stats.atime.getTime(), 300); + assert.equal(stats.mtime.getTime(), 400); + done(); + }, done); + }); + + it('fails for a bogus path', function(done) { + fs.utimes('bogus.txt', new Date(100), new Date(200), function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails for a bogus path', function(done) { + fs.promises.utimes('bogus.txt', new Date(100), new Date(200)).then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +describe('fs.utimesSync(path, atime, mtime)', function() { + beforeEach(function() { + mock({ + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('updates timestamps for a file', function() { + fs.utimesSync('file.txt', new Date(100), new Date(200)); + const stats = fs.statSync('file.txt'); + assert.equal(stats.atime.getTime(), 100); + assert.equal(stats.mtime.getTime(), 200); + }); +}); + +describe('fs.futimes(fd, atime, mtime, callback)', function() { + beforeEach(function() { + mock({ + dir: {}, + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('updates timestamps for a file', function(done) { + const fd = fs.openSync('file.txt', 'r'); + fs.futimes(fd, new Date(100), new Date(200), function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('file.txt'); + assert.equal(stats.atime.getTime(), 100); + assert.equal(stats.mtime.getTime(), 200); + done(); + }); + }); + + withPromise.it('promise updates timestamps for a file', function(done) { + fs.promises + .open('file.txt', 'r') + .then(function(fd) { + return fd.utimes(new Date(100), new Date(200)); + }) + .then(function() { + const stats = fs.statSync('file.txt'); + assert.equal(stats.atime.getTime(), 100); + assert.equal(stats.mtime.getTime(), 200); + done(); + }, done); + }); + + it('updates timestamps for a directory', function(done) { + const fd = fs.openSync('dir', 'r'); + fs.futimes(fd, new Date(300), new Date(400), function(err) { + if (err) { + return done(err); + } + const stats = fs.statSync('dir'); + assert.equal(stats.atime.getTime(), 300); + assert.equal(stats.mtime.getTime(), 400); + done(); + }); + }); + + withPromise.it('promise updates timestamps for a directory', function(done) { + fs.promises + .open('dir', 'r') + .then(function(fd) { + return fd.utimes(new Date(300), new Date(400)); + }) + .then(function() { + const stats = fs.statSync('dir'); + assert.equal(stats.atime.getTime(), 300); + assert.equal(stats.mtime.getTime(), 400); + done(); + }, done); + }); +}); + +describe('fs.futimesSync(path, atime, mtime)', function() { + beforeEach(function() { + mock({ + 'file.txt': 'content' + }); + }); + afterEach(mock.restore); + + it('updates timestamps for a file', function() { + const fd = fs.openSync('file.txt', 'r'); + fs.futimesSync(fd, new Date(100), new Date(200)); + const stats = fs.statSync('file.txt'); + assert.equal(stats.atime.getTime(), 100); + assert.equal(stats.mtime.getTime(), 200); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.writeFile.spec.js node-mock-fs-4.10.4/test/lib/fs.writeFile.spec.js --- node-mock-fs-4.10.2/test/lib/fs.writeFile.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.writeFile.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,122 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const bufferFrom = require('../../lib/buffer').from; + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.writeFile(filename, data, [options], callback)', function() { + beforeEach(function() { + mock({ + dir: mock.directory({ + mtime: new Date(1) + }) + }); + }); + afterEach(mock.restore); + + it('writes a string to a file', function(done) { + fs.writeFile('dir/foo', 'bar', function(err) { + if (err) { + return done(err); + } + assert.equal(String(fs.readFileSync('dir/foo')), 'bar'); + done(); + }); + }); + + withPromise.it('promise writes a string to a file', function(done) { + fs.promises.writeFile('dir/foo', 'bar').then(function() { + assert.equal(String(fs.readFileSync('dir/foo')), 'bar'); + done(); + }, done); + }); + + it('updates mtime of parent directory', function(done) { + const oldTime = fs.statSync('dir').mtime; + fs.writeFile('dir/foo', 'bar', function(err) { + if (err) { + return done(err); + } + const newTime = fs.statSync('dir').mtime; + assert.isTrue(newTime > oldTime); + done(); + }); + }); + + withPromise.it('promise updates mtime of parent directory', function(done) { + const oldTime = fs.statSync('dir').mtime; + fs.promises.writeFile('dir/foo', 'bar').then(function() { + const newTime = fs.statSync('dir').mtime; + assert.isTrue(newTime > oldTime); + done(); + }, done); + }); + + it('writes a buffer to a file', function(done) { + fs.writeFile('dir/foo', bufferFrom('bar'), function(err) { + if (err) { + return done(err); + } + assert.equal(String(fs.readFileSync('dir/foo')), 'bar'); + done(); + }); + }); + + withPromise.it('promise writes a buffer to a file', function(done) { + fs.promises.writeFile('dir/foo', bufferFrom('bar')).then(function() { + assert.equal(String(fs.readFileSync('dir/foo')), 'bar'); + done(); + }, done); + }); + + it('fails if directory does not exist', function(done) { + fs.writeFile('foo/bar', 'baz', function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + }); + }); + + withPromise.it('promise fails if directory does not exist', function(done) { + fs.promises.writeFile('foo/bar', 'baz').then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + done(); + } + ); + }); +}); + +describe('fs.writeFileSync(filename, data, [options]', function() { + beforeEach(function() { + mock({ + '.': {} + }); + }); + afterEach(mock.restore); + + it('writes a string to a file', function() { + fs.writeFileSync('foo', 'bar'); + assert.equal(String(fs.readFileSync('foo')), 'bar'); + }); + + it('writes a buffer to a file', function() { + fs.writeFileSync('foo', bufferFrom('bar')); + assert.equal(String(fs.readFileSync('foo')), 'bar'); + }); + + it('fails if directory does not exist', function() { + assert.throws(function() { + fs.writeFileSync('foo/bar', 'baz'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/fs.write.spec.js node-mock-fs-4.10.4/test/lib/fs.write.spec.js --- node-mock-fs-4.10.2/test/lib/fs.write.spec.js 1970-01-01 00:00:00.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/fs.write.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -0,0 +1,570 @@ +'use strict'; + +const helper = require('../helper'); +const fs = require('fs'); +const mock = require('../../lib/index'); +const bufferFrom = require('../../lib/buffer').from; + +const assert = helper.assert; +const withPromise = helper.withPromise; + +describe('fs.write(fd, buffer, offset, length, position, callback)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('writes a buffer to a file', function(done) { + const fd = fs.openSync('path/new-file.txt', 'w'); + const buffer = bufferFrom('new file'); + fs.write(fd, buffer, 0, buffer.length, null, function(err, written, buf) { + if (err) { + return done(err); + } + assert.equal(written, 8); + assert.equal(buf, buffer); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'new file'); + done(); + }); + }); + + withPromise.it('promise writes a buffer to a file', function(done) { + const buffer = bufferFrom('new file'); + fs.promises + .open('path/new-file.txt', 'w') + .then(function(fd) { + return fd.write(buffer, 0, buffer.length); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 8); + assert.equal(result.buffer, buffer); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'new file'); + done(); + }, done); + }); + + it('writes a buffer to a file with implicit offset, length, position', function(done) { + const fd = fs.openSync('path/new-file.txt', 'w'); + const buffer = bufferFrom('new file'); + fs.write(fd, buffer, function(err, written, buf) { + if (err) { + return done(err); + } + assert.equal(written, 8); + assert.equal(buf, buffer); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'new file'); + done(); + }); + }); + + withPromise.it( + 'promise writes a buffer to a file with implicit offset, length, position', + function(done) { + const buffer = bufferFrom('new file'); + fs.promises + .open('path/new-file.txt', 'w') + .then(function(fd) { + return fd.write(buffer); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 8); + assert.equal(result.buffer, buffer); + assert.equal( + String(fs.readFileSync('path/new-file.txt')), + 'new file' + ); + done(); + }, done); + } + ); + + it('can write a portion of a buffer to a file', function(done) { + fs.open('path/new-file.txt', 'w', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferFrom('new file'); + fs.write(fd, buffer, 1, 5, null, function(err2, written, buf) { + if (err2) { + return done(err2); + } + assert.equal(written, 5); + assert.equal(buf, buffer); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'ew fi'); + done(); + }); + }); + }); + + withPromise.it('promise can write a portion of a buffer to a file', function( + done + ) { + const buffer = bufferFrom('new file'); + fs.promises + .open('path/new-file.txt', 'w') + .then(function(fd) { + return fd.write(buffer, 1, 5); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 5); + assert.equal(result.buffer, buffer); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'ew fi'); + done(); + }, done); + }); + + it('can write a portion of a buffer to a file position', function(done) { + fs.open('path/to/file.txt', 'a', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferFrom('new file'); + fs.write(fd, buffer, 1, 5, 2, function(err2, written, buf) { + if (err2) { + return done(err2); + } + assert.equal(written, 5); + assert.equal(buf, buffer); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'fiew fintent' + ); + done(); + }); + }); + }); + + withPromise.it( + 'promise can write a portion of a buffer to a file position', + function(done) { + const buffer = bufferFrom('new file'); + fs.promises + .open('path/to/file.txt', 'a') + .then(function(fd) { + return fd.write(buffer, 1, 5, 2); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 5); + assert.equal(result.buffer, buffer); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'fiew fintent' + ); + done(); + }, done); + } + ); + + it('can write a portion of a buffer to a file position and enlarge the file', function(done) { + fs.open('path/to/file.txt', 'a', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferFrom('new file'); + fs.write(fd, buffer, 1, 5, 8, function(err2, written, buf) { + if (err2) { + return done(err2); + } + assert.equal(written, 5); + assert.equal(buf, buffer); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'file conew fi' + ); + done(); + }); + }); + }); + + withPromise.it( + 'promise can write a portion of a buffer to a file position and enlarge the file', + function(done) { + const buffer = bufferFrom('new file'); + fs.promises + .open('path/to/file.txt', 'a') + .then(function(fd) { + return fd.write(buffer, 1, 5, 8); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 5); + assert.equal(result.buffer, buffer); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'file conew fi' + ); + done(); + }, done); + } + ); + + it('can append to a file', function(done) { + fs.open('path/to/file.txt', 'a', function(err, fd) { + if (err) { + return done(err); + } + const buffer = bufferFrom(' more'); + fs.write(fd, buffer, 0, 5, null, function(err2, written, buf) { + if (err2) { + return done(err2); + } + assert.equal(written, 5); + assert.equal(buf, buffer); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'file content more' + ); + done(); + }); + }); + }); + + withPromise.it('promise can append to a file', function(done) { + const buffer = bufferFrom(' more'); + fs.promises + .open('path/to/file.txt', 'a') + .then(function(fd) { + return fd.write(buffer, 0, 5); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 5); + assert.equal(result.buffer, buffer); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'file content more' + ); + done(); + }, done); + }); + + it('fails if file not open for writing', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + fs.write(fd, bufferFrom('oops'), 0, 4, null, function(err2) { + assert.instanceOf(err2, Error); + assert.equal(err2.code, 'EBADF'); + done(); + }); + }); + }); + + withPromise.it('fails if file not open for writing', function(done) { + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.write(bufferFrom('oops'), 0, 4); + }) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + } + ); + }); +}); + +describe('fs.writeSync(fd, buffer, offset, length, position)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('writes a buffer to a file', function() { + const buffer = bufferFrom('new file'); + const fd = fs.openSync('path/new-file.txt', 'w'); + const written = fs.writeSync(fd, buffer, 0, buffer.length); + assert.equal(written, 8); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'new file'); + }); + + it('can write a portion of a buffer to a file', function() { + const buffer = bufferFrom('new file'); + const fd = fs.openSync('path/new-file.txt', 'w'); + const written = fs.writeSync(fd, buffer, 1, 5); + assert.equal(written, 5); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'ew fi'); + }); + + it('can append to a file', function() { + const buffer = bufferFrom(' more'); + const fd = fs.openSync('path/to/file.txt', 'a'); + const written = fs.writeSync(fd, buffer, 0, 5); + assert.equal(written, 5); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'file content more' + ); + }); + + it('fails if file not open for writing', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + assert.throws(function() { + fs.writeSync(fd, bufferFrom('oops'), 0, 4); + }); + }); +}); + +describe('fs.write(fd, data[, position[, encoding]], callback)', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('writes a string to a file', function(done) { + fs.open('path/new-file.txt', 'w', function(err, fd) { + if (err) { + return done(err); + } + const string = 'new file'; + fs.write(fd, string, null, 'utf-8', function(err2, written, str) { + if (err2) { + return done(err2); + } + assert.equal(written, 8); + assert.equal(str, string); + assert.equal(fs.readFileSync('path/new-file.txt'), 'new file'); + done(); + }); + }); + }); + + withPromise.it('promise writes a string to a file', function(done) { + const string = 'new file'; + fs.promises + .open('path/new-file.txt', 'w') + .then(function(fd) { + return fd.write(string, null, 'utf-8'); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 8); + assert.equal(String(result.buffer), string); + assert.equal(String(fs.readFileSync('path/new-file.txt')), 'new file'); + done(); + }, done); + }); + + it('writes a string to a file with implicit position and encoding', function(done) { + fs.open('path/new-file.txt', 'w', function(err, fd) { + if (err) { + return done(err); + } + const string = 'new file'; + fs.write(fd, string, function(err2, written, str) { + if (err2) { + return done(err2); + } + assert.equal(written, 8); + assert.equal(str, string); + assert.equal(fs.readFileSync('path/new-file.txt'), 'new file'); + done(); + }); + }); + }); + + withPromise.it( + 'promise writes a string to a file with implicit position and encoding', + function(done) { + const string = 'new file'; + fs.promises + .open('path/new-file.txt', 'w') + .then(function(fd) { + return fd.write(string); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 8); + assert.equal(String(result.buffer), string); + assert.equal( + String(fs.readFileSync('path/new-file.txt')), + 'new file' + ); + done(); + }, done); + } + ); + + it('can append to a file', function(done) { + fs.open('path/to/file.txt', 'a', function(err, fd) { + if (err) { + return done(err); + } + const string = ' more'; + fs.write(fd, string, null, 'utf-8', function(err2, written, str) { + if (err2) { + return done(err2); + } + assert.equal(written, 5); + assert.equal(str, string); + assert.equal(fs.readFileSync('path/to/file.txt'), 'file content more'); + done(); + }); + }); + }); + + withPromise.it('promise can append to a file', function(done) { + const string = ' more'; + fs.promises + .open('path/to/file.txt', 'a') + .then(function(fd) { + return fd.write(string); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 5); + assert.equal(String(result.buffer), string); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'file content more' + ); + done(); + }, done); + }); + + it('can write to a position of a file', function(done) { + fs.open('path/to/file.txt', 'a', function(err, fd) { + if (err) { + return done(err); + } + const string = ' more'; + fs.write(fd, string, 3, function(err2, written, str) { + if (err2) { + return done(err2); + } + assert.equal(written, 5); + assert.equal(str, string); + assert.equal(fs.readFileSync('path/to/file.txt'), 'fil moretent'); + done(); + }); + }); + }); + + withPromise.it('promise can write to a position of a file', function(done) { + const string = ' more'; + fs.promises + .open('path/to/file.txt', 'a') + .then(function(fd) { + return fd.write(string, 3); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 5); + assert.equal(String(result.buffer), string); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'fil moretent' + ); + done(); + }, done); + }); + + it('can write to a position of a file and enlarge it', function(done) { + fs.open('path/to/file.txt', 'a', function(err, fd) { + if (err) { + return done(err); + } + const string = ' more'; + fs.write(fd, string, 9, function(err2, written, str) { + if (err2) { + return done(err2); + } + assert.equal(written, 5); + assert.equal(str, string); + assert.equal(fs.readFileSync('path/to/file.txt'), 'file cont more'); + done(); + }); + }); + }); + + withPromise.it( + 'promise can write to a position of a file and enlarge it', + function(done) { + const string = ' more'; + fs.promises + .open('path/to/file.txt', 'a') + .then(function(fd) { + return fd.write(string, 9); + }) + .then(function(result) { + assert.equal(result.bytesWritten, 5); + assert.equal(String(result.buffer), string); + assert.equal( + String(fs.readFileSync('path/to/file.txt')), + 'file cont more' + ); + done(); + }, done); + } + ); + + it('fails if file not open for writing', function(done) { + fs.open('path/to/file.txt', 'r', function(err, fd) { + if (err) { + return done(err); + } + fs.write(fd, 'oops', null, 'utf-8', function(err2) { + assert.instanceOf(err2, Error); + done(); + }); + }); + }); + + withPromise.it('promise fails if file not open for writing', function(done) { + fs.promises + .open('path/to/file.txt', 'r') + .then(function(fd) { + return fd.write('oops'); + }) + .then( + function() { + assert.fail('should not succeed.'); + done(); + }, + function(err) { + assert.instanceOf(err, Error); + assert.equal(err.code, 'EBADF'); + done(); + } + ); + }); +}); + +describe('fs.writeSync(fd, data[, position[, encoding]])', function() { + beforeEach(function() { + mock({ + 'path/to/file.txt': 'file content' + }); + }); + afterEach(mock.restore); + + it('writes a string to a file', function() { + const fd = fs.openSync('path/new-file.txt', 'w'); + const string = 'new file'; + const written = fs.writeSync(fd, string, null, 'utf-8'); + assert.equal(written, 8); + assert.equal(fs.readFileSync('path/new-file.txt'), 'new file'); + }); + + it('can append to a file', function() { + const fd = fs.openSync('path/to/file.txt', 'a'); + const string = ' more'; + const written = fs.writeSync(fd, string, null, 'utf-8'); + assert.equal(written, 5); + assert.equal(fs.readFileSync('path/to/file.txt'), 'file content more'); + }); + + it('fails if file not open for writing', function() { + const fd = fs.openSync('path/to/file.txt', 'r'); + assert.throws(function() { + fs.writeSync(fd, 'oops', null, 'utf-8'); + }); + }); +}); diff -Nru node-mock-fs-4.10.2/test/lib/index.spec.js node-mock-fs-4.10.4/test/lib/index.spec.js --- node-mock-fs-4.10.2/test/lib/index.spec.js 2019-10-18 19:24:44.000000000 +0000 +++ node-mock-fs-4.10.4/test/lib/index.spec.js 2019-11-26 05:27:15.000000000 +0000 @@ -1,19 +1,12 @@ 'use strict'; -const Writable = require('stream').Writable; const helper = require('../helper'); const fs = require('fs'); const mock = require('../../lib/index'); const os = require('os'); const path = require('path'); -const bufferFrom = require('../../lib/buffer').from; -const bufferAlloc = require('../../lib/buffer').alloc; const assert = helper.assert; -const inVersion = helper.inVersion; - -const testParentPerms = - fs.access && fs.accessSync && process.getuid && process.getgid; describe('The API', function() { describe('mock()', function() { @@ -232,3352 +225,166 @@ }); }); -describe('Mocking the file system', function() { - if (fs.access && fs.accessSync && process.getuid && process.getgid) { - // TODO: drop condition when dropping Node < 0.12 support - // TODO: figure out how fs.access() works on Windows (without gid/uid) - - describe('fs.access(path[, mode], callback)', function() { - beforeEach(function() { - mock({ - 'path/to/accessible/file': 'can access', - 'path/to/000': mock.file({ - mode: parseInt('0000', 8), - content: 'no permissions' - }), - 'path/to/111': mock.file({ - mode: parseInt('0111', 8), - content: 'execute only' - }), - 'path/to/write/only': mock.file({ - mode: parseInt('0222', 8), - content: 'write only' - }), - 'path/to/333': mock.file({ - mode: parseInt('0333', 8), - content: 'write and execute' - }), - 'path/to/444': mock.file({ - mode: parseInt('0444', 8), - content: 'read only' - }), - 'path/to/555': mock.file({ - mode: parseInt('0555', 8), - content: 'read and execute' - }), - 'path/to/666': mock.file({ - mode: parseInt('0666', 8), - content: 'read and write' - }), - 'path/to/777': mock.file({ - mode: parseInt('0777', 8), - content: 'read, write, and execute' - }), - unreadable: mock.directory({ - mode: parseInt('0000', 8), - items: { - 'readable-child': mock.file({ - mode: parseInt('0777', 8), - content: 'read, write, and execute' - }) - } - }) - }); - }); - afterEach(mock.restore); - - it('works for an accessible file', function(done) { - fs.access('path/to/accessible/file', done); - }); - - it('works 000 (and no mode arg)', function(done) { - fs.access('path/to/000', done); - }); - - it('works F_OK and 000', function(done) { - fs.access('path/to/000', fs.F_OK, done); - }); - - it('generates EACCES for R_OK and 000', function(done) { - fs.access('path/to/000', fs.R_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('generates EACCES for W_OK and 000', function(done) { - fs.access('path/to/000', fs.W_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('generates EACCES for X_OK and 000', function(done) { - fs.access('path/to/000', fs.X_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('works 111 (and no mode arg)', function(done) { - fs.access('path/to/111', done); - }); - - it('works F_OK and 111', function(done) { - fs.access('path/to/111', fs.F_OK, done); - }); - - it('works X_OK and 111', function(done) { - fs.access('path/to/111', fs.X_OK, done); - }); - - it('generates EACCES for R_OK and 111', function(done) { - fs.access('path/to/111', fs.R_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('generates EACCES for W_OK and 111', function(done) { - fs.access('path/to/111', fs.W_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('works for 222 (and no mode arg)', function(done) { - fs.access('path/to/write/only', done); - }); - - it('works F_OK and 222', function(done) { - fs.access('path/to/write/only', fs.F_OK, done); - }); - - it('works W_OK and 222', function(done) { - fs.access('path/to/write/only', fs.W_OK, done); - }); - - it('generates EACCES for R_OK and 222', function(done) { - fs.access('path/to/write/only', fs.R_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('generates EACCES for X_OK and 222', function(done) { - fs.access('path/to/write/only', fs.X_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('works for 333 (and no mode arg)', function(done) { - fs.access('path/to/333', done); - }); - - it('works F_OK and 333', function(done) { - fs.access('path/to/333', fs.F_OK, done); - }); - - it('works W_OK and 333', function(done) { - fs.access('path/to/333', fs.W_OK, done); - }); - - it('works X_OK and 333', function(done) { - fs.access('path/to/333', fs.X_OK, done); - }); - - it('works X_OK | W_OK and 333', function(done) { - fs.access('path/to/333', fs.X_OK | fs.W_OK, done); - }); - - it('generates EACCES for R_OK and 333', function(done) { - fs.access('path/to/333', fs.R_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('works for 444 (and no mode arg)', function(done) { - fs.access('path/to/444', done); - }); - - it('works F_OK and 444', function(done) { - fs.access('path/to/444', fs.F_OK, done); - }); - - it('works R_OK and 444', function(done) { - fs.access('path/to/444', fs.R_OK, done); - }); - - it('generates EACCES for W_OK and 444', function(done) { - fs.access('path/to/444', fs.W_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('generates EACCES for X_OK and 444', function(done) { - fs.access('path/to/444', fs.X_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('works for 555 (and no mode arg)', function(done) { - fs.access('path/to/555', done); - }); - - it('works F_OK and 555', function(done) { - fs.access('path/to/555', fs.F_OK, done); - }); - - it('works R_OK and 555', function(done) { - fs.access('path/to/555', fs.R_OK, done); - }); - - it('works X_OK and 555', function(done) { - fs.access('path/to/555', fs.X_OK, done); - }); - - it('works R_OK | X_OK and 555', function(done) { - fs.access('path/to/555', fs.R_OK | fs.X_OK, done); - }); - - it('generates EACCES for W_OK and 555', function(done) { - fs.access('path/to/555', fs.W_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('works for 666 (and no mode arg)', function(done) { - fs.access('path/to/666', done); - }); - - it('works F_OK and 666', function(done) { - fs.access('path/to/666', fs.F_OK, done); - }); - - it('works R_OK and 666', function(done) { - fs.access('path/to/666', fs.R_OK, done); - }); - - it('works W_OK and 666', function(done) { - fs.access('path/to/666', fs.W_OK, done); - }); - - it('works R_OK | W_OK and 666', function(done) { - fs.access('path/to/666', fs.R_OK | fs.W_OK, done); - }); - - it('generates EACCES for X_OK and 666', function(done) { - fs.access('path/to/666', fs.X_OK, function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - - it('works for 777 (and no mode arg)', function(done) { - fs.access('path/to/777', done); - }); - - it('works F_OK and 777', function(done) { - fs.access('path/to/777', fs.F_OK, done); - }); - - it('works R_OK and 777', function(done) { - fs.access('path/to/777', fs.R_OK, done); - }); - - it('works W_OK and 777', function(done) { - fs.access('path/to/777', fs.W_OK, done); - }); - - it('works X_OK and 777', function(done) { - fs.access('path/to/777', fs.X_OK, done); - }); - - it('works X_OK | W_OK and 777', function(done) { - fs.access('path/to/777', fs.X_OK | fs.W_OK, done); - }); - - it('works X_OK | R_OK and 777', function(done) { - fs.access('path/to/777', fs.X_OK | fs.R_OK, done); - }); - - it('works R_OK | W_OK and 777', function(done) { - fs.access('path/to/777', fs.R_OK | fs.W_OK, done); - }); - - it('works R_OK | W_OK | X_OK and 777', function(done) { - fs.access('path/to/777', fs.R_OK | fs.W_OK | fs.X_OK, done); - }); - - it('generates EACCESS for F_OK and an unreadable parent', function(done) { - fs.access('unreadable/readable-child', function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - }); - }); - }); - - describe('fs.accessSync(path[, mode])', function() { - beforeEach(function() { - mock({ - 'path/to/777': mock.file({ - mode: parseInt('0777', 8), - content: 'all access' - }), - 'path/to/000': mock.file({ - mode: parseInt('0000', 8), - content: 'no permissions' - }), - 'broken-link': mock.symlink({path: './path/to/nothing'}), - 'circular-link': mock.symlink({path: './loop-link'}), - 'loop-link': mock.symlink({path: './circular-link'}) - }); - }); - afterEach(mock.restore); - - it('works for an accessible file', function() { - fs.accessSync('path/to/777'); - fs.accessSync('path/to/777', fs.F_OK); - fs.accessSync('path/to/777', fs.X_OK); - fs.accessSync('path/to/777', fs.W_OK); - fs.accessSync('path/to/777', fs.X_OK | fs.W_OK); - fs.accessSync('path/to/777', fs.R_OK); - fs.accessSync('path/to/777', fs.X_OK | fs.R_OK); - fs.accessSync('path/to/777', fs.W_OK | fs.R_OK); - fs.accessSync('path/to/777', fs.X_OK | fs.W_OK | fs.R_OK); - }); +describe('process.cwd()', function() { + afterEach(mock.restore); - it('throws EACCESS for broken link', function() { - assert.throws(function() { - fs.accessSync('broken-link'); - }); - }); + it('maintains current working directory', function() { + const originalCwd = process.cwd(); + mock(); - it('throws ELOOP for circular link', function() { - assert.throws(function() { - fs.accessSync('circular-link'); - }); - }); + const cwd = process.cwd(); + assert.equal(cwd, originalCwd); + }); - it('throws EACCESS for all but F_OK for 000', function() { - fs.accessSync('path/to/000'); - assert.throws(function() { - fs.accessSync('path/to/000', fs.X_OK); - }); - assert.throws(function() { - fs.accessSync('path/to/000', fs.W_OK); - }); - assert.throws(function() { - fs.accessSync('path/to/000', fs.X_OK | fs.W_OK); - }); - assert.throws(function() { - fs.accessSync('path/to/000', fs.R_OK); - }); - assert.throws(function() { - fs.accessSync('path/to/000', fs.X_OK | fs.R_OK); - }); - assert.throws(function() { - fs.accessSync('path/to/000', fs.W_OK | fs.R_OK); - }); - assert.throws(function() { - fs.accessSync('path/to/000', fs.X_OK | fs.W_OK | fs.R_OK); - }); - }); + it('allows changing directory', function() { + const originalCwd = process.cwd(); + mock({ + dir: {} }); - } - - if (fs.copyFile && fs.copyFileSync) { - describe('fs.copyFile(src, dest[, flags], callback)', function() { - beforeEach(function() { - mock({ - 'path/to/src.txt': 'file content', - 'path/to/other.txt': 'other file content', - empty: {} - }); - }); - afterEach(mock.restore); - - it('copies a file to an empty directory', function(done) { - fs.copyFile('path/to/src.txt', 'empty/dest.txt', function(err) { - assert.isTrue(!err); - assert.isTrue(fs.existsSync('empty/dest.txt')); - assert.equal( - String(fs.readFileSync('empty/dest.txt')), - 'file content' - ); - done(); - }); - }); - - it('truncates dest file if it exists', function(done) { - fs.copyFile('path/to/src.txt', 'path/to/other.txt', function(err) { - assert.isTrue(!err); - assert.equal( - String(fs.readFileSync('path/to/other.txt')), - 'file content' - ); - done(); - }); - }); - - it('throws if dest exists and exclusive', function(done) { - fs.copyFile( - 'path/to/src.txt', - 'path/to/other.txt', - fs.constants.COPYFILE_EXCL, - function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EEXIST'); - done(); - } - ); - }); - it('fails if src does not exist', function(done) { - fs.copyFile('path/to/bogus.txt', 'empty/dest.txt', function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'ENOENT'); - done(); - }); - }); + process.chdir('dir'); + const cwd = process.cwd(); + assert.equal(cwd, path.join(originalCwd, 'dir')); + }); - it('fails if dest path does not exist', function(done) { - fs.copyFile('path/to/src.txt', 'path/nope/dest.txt', function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'ENOENT'); - done(); - }); - }); + it('prevents changing directory to non-existent path', function() { + mock(); - it('fails if dest is a directory', function(done) { - fs.copyFile('path/to/src.txt', 'empty', function(err) { - assert.instanceOf(err, Error); - assert.equal(err.code, 'EISDIR'); - done(); - }); - }); - }); - } + let err; + try { + process.chdir('dir'); + } catch (e) { + err = e; + } + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOENT'); + }); - describe('fs.rename(oldPath, newPath, callback)', function() { - beforeEach(function() { - mock({ - 'path/to/a.bin': bufferFrom([1, 2, 3]), - empty: {}, - nested: { - dir: mock.directory({ - mtime: new Date(1), - items: {'file.txt': ''} - }) - } - }); + it('prevents changing directory to non-directory path', function() { + mock({ + file: '' }); - afterEach(mock.restore); - it('allows files to be renamed', function(done) { - fs.rename('path/to/a.bin', 'path/to/b.bin', function(err) { - assert.isTrue(!err); - assert.isFalse(fs.existsSync('path/to/a.bin')); - assert.isTrue(fs.existsSync('path/to/b.bin')); - done(); - }); - }); + let err; + try { + process.chdir('file'); + } catch (e) { + err = e; + } + assert.instanceOf(err, Error); + assert.equal(err.code, 'ENOTDIR'); + }); - it('updates mtime of parent directory', function(done) { - const oldTime = fs.statSync('nested/dir').mtime; - fs.rename('nested/dir/file.txt', 'nested/dir/renamed.txt', function(err) { - assert.isTrue(!err); - assert.isFalse(fs.existsSync('nested/dir/file.txt')); - assert.isTrue(fs.existsSync('nested/dir/renamed.txt')); - const newTime = fs.statSync('nested/dir').mtime; - assert.isTrue(newTime > oldTime); - done(); - }); - }); + it('restores original methods on restore', function() { + const originalCwd = process.cwd; + const originalChdir = process.chdir; + mock(); - it('calls callback with error if old path does not exist', function(done) { - fs.rename('bogus', 'empty', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); + mock.restore(); + assert.equal(process.cwd, originalCwd); + assert.equal(process.chdir, originalChdir); + }); - it('overwrites existing files', function(done) { - fs.rename('path/to/a.bin', 'nested/dir/file.txt', function(err) { - assert.isTrue(!err); - assert.isFalse(fs.existsSync('path/to/a.bin')); - assert.isTrue(fs.existsSync('nested/dir/file.txt')); - done(); - }); + it('restores original working directory on restore', function() { + const originalCwd = process.cwd(); + mock({ + dir: {} }); - it('allows directories to be renamed', function(done) { - fs.rename('path/to', 'path/foo', function(err) { - assert.isTrue(!err); - assert.isFalse(fs.existsSync('path/to')); - assert.isTrue(fs.existsSync('path/foo')); - assert.deepEqual(fs.readdirSync('path/foo'), ['a.bin']); - done(); - }); - }); + process.chdir('dir'); + mock.restore(); - it('calls callback with error if new directory not empty', function(done) { - fs.rename('path', 'nested', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); + const cwd = process.cwd(); + assert.equal(cwd, originalCwd); }); +}); - describe('fs.renameSync(oldPath, newPath)', function() { - beforeEach(function() { - mock({ - 'path/to/a.bin': bufferFrom([1, 2, 3]), - empty: {}, - nested: { - dir: { - 'file.txt': '' - } - }, - link: mock.symlink({path: './path/to/a.bin'}) - }); - }); +if (process.getuid && process.getgid) { + describe('security', function() { afterEach(mock.restore); - it('allows files to be renamed', function() { - fs.renameSync('path/to/a.bin', 'path/to/b.bin'); - assert.isFalse(fs.existsSync('path/to/a.bin')); - assert.isTrue(fs.existsSync('path/to/b.bin')); - }); - - it('overwrites existing files', function() { - fs.renameSync('path/to/a.bin', 'nested/dir/file.txt'); - assert.isFalse(fs.existsSync('path/to/a.bin')); - assert.isTrue(fs.existsSync('nested/dir/file.txt')); - }); - - it('allows directories to be renamed', function() { - fs.renameSync('path/to', 'path/foo'); - assert.isFalse(fs.existsSync('path/to')); - assert.isTrue(fs.existsSync('path/foo')); - assert.deepEqual(fs.readdirSync('path/foo'), ['a.bin']); - }); - - it('replaces existing directories (if empty)', function() { - fs.renameSync('path/to', 'empty'); - assert.isFalse(fs.existsSync('path/to')); - assert.isTrue(fs.existsSync('empty')); - assert.deepEqual(fs.readdirSync('empty'), ['a.bin']); - }); - - it('renames symbolic links', function() { - fs.renameSync('link', 'renamed'); - assert.isTrue(fs.existsSync('renamed')); - assert.isFalse(fs.existsSync('link')); - assert.isTrue(fs.existsSync('path/to/a.bin')); - }); - - it('throws if old path does not exist', function() { - assert.throws(function() { - fs.renameSync('bogus', 'empty'); - }); - }); - - it('throws if new path basename is not directory', function() { - assert.throws(function() { - fs.renameSync('path/to/a.bin', 'bogus/a.bin'); + it('denies dir listing without execute on parent', function() { + mock({ + secure: mock.directory({ + mode: parseInt('0666', 8), + items: { + insecure: { + file: 'file content' + } + } + }) }); - }); - it('throws if new dir is not empty dir', function() { - assert.throws(function() { - fs.renameSync('path/to', 'nested'); - }); + let err; + try { + fs.readdirSync('secure/insecure'); + } catch (e) { + err = e; + } + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); }); - }); - describe('fs.stat(path, callback)', function() { - beforeEach(function() { + it('denies file read without execute on parent', function() { mock({ - '/path/to/file.txt': mock.file({ - ctime: new Date(1), - mtime: new Date(2), - atime: new Date(3), - uid: 42, - gid: 43 - }), - '/dir/symlink': mock.symlink({path: '/path/to/file.txt'}), - '/empty': {} - }); - }); - afterEach(mock.restore); - - xit('creates an instance of fs.Stats', function(done) { - fs.stat('/path/to/file.txt', function(err, stats) { - if (err) { - return done(err); - } - assert.instanceOf(stats, fs.Stats); - done(); + secure: mock.directory({ + mode: parseInt('0666', 8), + items: { + insecure: { + file: 'file content' + } + } + }) }); - }); - it('identifies files', function(done) { - fs.stat('/path/to/file.txt', function(err, stats) { - if (err) { - return done(err); - } - assert.isTrue(stats.isFile()); - assert.isFalse(stats.isDirectory()); - done(); - }); + let err; + try { + fs.readFileSync('secure/insecure/file'); + } catch (e) { + err = e; + } + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); }); - it('identifies directories', function(done) { - fs.stat('/empty', function(err, stats) { - if (err) { - return done(err); + it('denies file read without read on file', function() { + mock({ + insecure: { + 'write-only': mock.file({ + mode: parseInt('0222', 8), + content: 'write only' + }) } - assert.isTrue(stats.isDirectory()); - assert.isFalse(stats.isFile()); - done(); }); - }); - it('provides file stats', function(done) { - fs.stat('/path/to/file.txt', function(err, stats) { - if (err) { - return done(err); - } - assert.equal(stats.ctime.getTime(), 1); - assert.equal(stats.mtime.getTime(), 2); - assert.equal(stats.atime.getTime(), 3); - assert.equal(stats.uid, 42); - assert.equal(stats.gid, 43); - assert.equal(stats.nlink, 1); - assert.isNumber(stats.rdev); - done(); - }); + let err; + try { + fs.readFileSync('insecure/write-only'); + } catch (e) { + err = e; + } + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); }); - // TODO: unconditionally test this when this issue is addressed - // https://github.com/nodejs/node/issues/25913 - inVersion('<10').it('includes blocks and blksize in stats', function(done) { - fs.stat('/path/to/file.txt', function(err, stats) { - if (err) { - return done(err); + it('denies file write without write on file', function() { + mock({ + insecure: { + 'read-only': mock.file({ + mode: parseInt('0444', 8), + content: 'read only' + }) } - assert.isNumber(stats.blocks); - assert.isNumber(stats.blksize); - done(); }); - }); - it('provides directory stats', function(done) { - fs.stat('/path', function(err, stats) { - if (err) { - return done(err); - } - assert.instanceOf(stats.ctime, Date); - assert.instanceOf(stats.mtime, Date); - assert.instanceOf(stats.atime, Date); - if (process.getuid) { - assert.isNumber(stats.uid); - } else { - assert.isNaN(stats.uid); - } - if (process.getgid) { - assert.isNumber(stats.gid); - } else { - assert.isNaN(stats.gid); - } - assert.equal(stats.nlink, 3); - assert.isNumber(stats.rdev); - done(); - }); - }); - - // TODO: unconditionally test this when this issue is addressed - // https://github.com/nodejs/node/issues/25913 - inVersion('<10').it( - 'includes blocks and blksize in directory stats', - function(done) { - fs.stat('/path', function(err, stats) { - if (err) { - return done(err); - } - assert.isNumber(stats.blocks); - assert.isNumber(stats.blksize); - done(); - }); - } - ); - }); - - describe('fs.fstat(fd, callback)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content', - empty: {} - }); - }); - afterEach(mock.restore); - - it('accepts a file descriptor for a file (r)', function(done) { - const fd = fs.openSync('path/to/file.txt', 'r'); - fs.fstat(fd, function(err, stats) { - if (err) { - return done(err); - } - assert.isTrue(stats.isFile()); - assert.equal(stats.size, 12); - done(); - }); - }); - - it('accepts a file descriptor for a directory (r)', function(done) { - const fd = fs.openSync('path/to', 'r'); - fs.fstat(fd, function(err, stats) { - if (err) { - return done(err); - } - assert.isTrue(stats.isDirectory()); - assert.isTrue(stats.size > 0); - done(); - }); - }); - - it('fails for bad file descriptor', function(done) { - const fd = fs.openSync('path/to/file.txt', 'r'); - fs.closeSync(fd); - fs.fstat(fd, function(err, stats) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.fstatSync(fd)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content', - empty: {} - }); - }); - afterEach(mock.restore); - - it('accepts a file descriptor for a file (r)', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - const stats = fs.fstatSync(fd); - assert.isTrue(stats.isFile()); - assert.equal(stats.size, 12); - }); - - it('accepts a file descriptor for a directory (r)', function() { - const fd = fs.openSync('path/to', 'r'); - const stats = fs.fstatSync(fd); - assert.isTrue(stats.isDirectory()); - assert.isTrue(stats.size > 0); - }); - - it('fails for bad file descriptor', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - fs.closeSync(fd); - assert.throws(function() { - fs.fstatSync(fd); - }); - }); - }); - - describe('fs.exists(path, callback)', function() { - beforeEach(function() { - mock({ - 'path/to/a.bin': bufferFrom([1, 2, 3]), - empty: {}, - nested: { - dir: { - 'file.txt': '' - } - } - }); - }); - afterEach(mock.restore); - - it('calls with true if file exists', function(done) { - fs.exists(path.join('path', 'to', 'a.bin'), function(exists) { - assert.isTrue(exists); - done(); - }); - }); - - it('calls with true if directory exists', function(done) { - fs.exists('path', function(exists) { - assert.isTrue(exists); - done(); - }); - }); - - it('calls with true if empty directory exists', function(done) { - fs.exists('empty', function(exists) { - assert.isTrue(exists); - done(); - }); - }); - - it('calls with true if nested directory exists', function(done) { - fs.exists(path.join('nested', 'dir'), function(exists) { - assert.isTrue(exists); - done(); - }); - }); - - it('calls with true if file exists', function(done) { - fs.exists(path.join('path', 'to', 'a.bin'), function(exists) { - assert.isTrue(exists); - done(); - }); - }); - - it('calls with true if empty file exists', function(done) { - fs.exists(path.join('nested', 'dir', 'file.txt'), function(exists) { - assert.isTrue(exists); - done(); - }); - }); - - it('calls with false for bogus path', function(done) { - fs.exists(path.join('bogus', 'path'), function(exists) { - assert.isFalse(exists); - done(); - }); - }); - - it('calls with false for bogus path (II)', function(done) { - fs.exists(path.join('nested', 'dir', 'none'), function(exists) { - assert.isFalse(exists); - done(); - }); - }); - }); - - describe('fs.existsSync(path)', function() { - beforeEach(function() { - mock({ - 'path/to/a.bin': bufferFrom([1, 2, 3]), - empty: {}, - nested: { - dir: { - 'file.txt': '' - } - } - }); - }); - afterEach(mock.restore); - - it('returns true if file exists', function() { - assert.isTrue(fs.existsSync(path.join('path', 'to', 'a.bin'))); - }); - - it('returns true if directory exists', function() { - assert.isTrue(fs.existsSync('path')); - }); - - it('returns true if empty directory exists', function() { - assert.isTrue(fs.existsSync('empty')); - }); - - it('returns true if nested directory exists', function() { - assert.isTrue(fs.existsSync(path.join('nested', 'dir'))); - }); - - it('returns true if file exists', function() { - assert.isTrue(fs.existsSync(path.join('path', 'to', 'a.bin'))); - }); - - it('returns true if empty file exists', function() { - assert.isTrue(fs.existsSync(path.join('nested', 'dir', 'file.txt'))); - }); - - it('returns false for bogus path', function() { - assert.isFalse(fs.existsSync(path.join('bogus', 'path'))); - }); - - it('returns false for bogus path (II)', function() { - assert.isFalse(fs.existsSync(path.join('nested', 'dir', 'none'))); - }); - }); - - describe('fs.readdirSync(path)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content', - nested: { - sub: { - dir: { - 'one.txt': 'one content', - 'two.txt': 'two content', - empty: {} - } - } - } - }); - }); - afterEach(mock.restore); - - it('lists directory contents', function() { - const items = fs.readdirSync(path.join('path', 'to')); - assert.isArray(items); - assert.deepEqual(items, ['file.txt']); - }); - - it('lists nested directory contents', function() { - const items = fs.readdirSync(path.join('nested', 'sub', 'dir')); - assert.isArray(items); - assert.deepEqual(items, ['empty', 'one.txt', 'two.txt']); - }); - - it('throws for bogus path', function() { - assert.throws(function() { - fs.readdirSync('bogus'); - }); - }); - }); - - describe('fs.readdir(path, callback)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content', - nested: { - sub: { - dir: { - 'one.txt': 'one content', - 'two.txt': 'two content', - empty: {} - } - } - } - }); - }); - afterEach(mock.restore); - - it('lists directory contents', function(done) { - fs.readdir(path.join('path', 'to'), function(err, items) { - assert.isNull(err); - assert.isArray(items); - assert.deepEqual(items, ['file.txt']); - done(); - }); - }); - - it('lists nested directory contents', function(done) { - fs.readdir(path.join('nested', 'sub', 'dir'), function(err, items) { - assert.isNull(err); - assert.isArray(items); - assert.deepEqual(items, ['empty', 'one.txt', 'two.txt']); - done(); - }); - }); - - it('calls with an error for bogus path', function(done) { - fs.readdir('bogus', function(err, items) { - assert.instanceOf(err, Error); - assert.isUndefined(items); - done(); - }); - }); - }); - - describe('fs.readdirSync(path)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content', - nested: { - sub: { - dir: { - 'one.txt': 'one content', - 'two.txt': 'two content', - empty: {} - } - } - } - }); - }); - afterEach(mock.restore); - - it('lists directory contents', function() { - const items = fs.readdirSync(path.join('path', 'to')); - assert.isArray(items); - assert.deepEqual(items, ['file.txt']); - }); - - it('lists nested directory contents', function() { - const items = fs.readdirSync(path.join('nested', 'sub', 'dir')); - assert.isArray(items); - assert.deepEqual(items, ['empty', 'one.txt', 'two.txt']); - }); - - it('throws for bogus path', function() { - assert.throws(function() { - fs.readdirSync('bogus'); - }); - }); - }); - - describe('fs.open(path, flags, [mode], callback)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content', - nested: { - sub: { - dir: { - 'one.txt': 'one content', - 'two.txt': 'two content', - empty: {} - } - } - } - }); - }); - afterEach(mock.restore); - - it('opens an existing file for reading (r)', function(done) { - fs.open('nested/sub/dir/one.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - assert.isNumber(fd); - done(); - }); - }); - - it('fails if file does not exist (r)', function(done) { - fs.open('bogus.txt', 'r', function(err, fd) { - assert.instanceOf(err, Error); - done(); - }); - }); - - it('creates a new file for writing (w)', function(done) { - fs.open('path/to/new.txt', 'w', parseInt('0666', 8), function(err, fd) { - if (err) { - return done(err); - } - assert.isNumber(fd); - assert.isTrue(fs.existsSync('path/to/new.txt')); - done(); - }); - }); - - it('opens an existing file for writing (w)', function(done) { - fs.open('path/to/one.txt', 'w', parseInt('0666', 8), function(err, fd) { - if (err) { - return done(err); - } - assert.isNumber(fd); - done(); - }); - }); - - it('fails if file exists (wx)', function(done) { - fs.open('path/to/one.txt', 'wx', parseInt('0666', 8), function(err, fd) { - if (err) { - return done(err); - } - assert.isNumber(fd); - done(); - }); - }); - }); - - describe('fs.openSync(path, flags, [mode])', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content', - nested: { - sub: { - dir: { - 'one.txt': 'one content', - 'two.txt': 'two content', - empty: {} - } - } - } - }); - }); - afterEach(mock.restore); - - it('opens an existing file for reading (r)', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - assert.isNumber(fd); - }); - - it('fails if file does not exist (r)', function() { - assert.throws(function() { - fs.openSync('bogus.txt', 'r'); - }); - }); - - it('creates a new file for writing (w)', function() { - const fd = fs.openSync('nested/sub/new.txt', 'w', parseInt('0666', 8)); - assert.isNumber(fd); - assert.isTrue(fs.existsSync('nested/sub/new.txt')); - }); - - it('opens an existing file for writing (w)', function() { - const fd = fs.openSync('path/to/one.txt', 'w', parseInt('0666', 8)); - assert.isNumber(fd); - }); - - it('fails if file exists (wx)', function() { - assert.throws(function() { - fs.openSync('path/to/file.txt', 'wx', parseInt('0666', 8)); - }); - }); - }); - - describe('fs.close(fd, callback)', function() { - beforeEach(function() { - mock({dir: {}}); - }); - afterEach(mock.restore); - - it('closes a file descriptor', function(done) { - const fd = fs.openSync('dir/file.txt', 'w'); - fs.close(fd, function(err) { - done(err); - }); - }); - - it('fails for closed file descriptors', function(done) { - const fd = fs.openSync('dir/file.txt', 'w'); - fs.close(fd, function(err) { - if (err) { - return done(err); - } - fs.close(fd, function(err2) { - assert.instanceOf(err2, Error); - done(); - }); - }); - }); - }); - - describe('fs.closeSync(fd)', function() { - beforeEach(function() { - mock({dir: {}}); - }); - afterEach(mock.restore); - - it('closes a file descriptor', function() { - const fd = fs.openSync('dir/file.txt', 'w'); - fs.closeSync(fd); - }); - - it('fails for closed file descriptors', function() { - const fd = fs.openSync('dir/file.txt', 'w'); - fs.closeSync(fd); - assert.throws(function() { - fs.closeSync(fd); - }); - }); - }); - - describe('fs.read(fd, buffer, offset, length, position, callback)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('allows file contents to be read', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferAlloc(12); - fs.read(fd, buffer, 0, 12, 0, function(err2, bytesRead, buf) { - if (err2) { - return done(err2); - } - assert.equal(bytesRead, 12); - assert.equal(buf, buffer); - assert.equal(String(buffer), 'file content'); - done(); - }); - }); - }); - - it('allows file contents to be read w/ offset', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferAlloc(12); - fs.read(fd, buffer, 5, 7, 0, function(err2, bytesRead, buf) { - if (err2) { - return done(err2); - } - assert.equal(bytesRead, 7); - assert.equal(buf, buffer); - assert.equal(String(buffer.slice(5)), 'file co'); - done(); - }); - }); - }); - - it('allows file contents to be read w/ length', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferAlloc(12); - fs.read(fd, buffer, 0, 4, 0, function(err2, bytesRead, buf) { - if (err2) { - return done(err2); - } - assert.equal(bytesRead, 4); - assert.equal(buf, buffer); - assert.equal(String(buffer.slice(0, 4)), 'file'); - done(); - }); - }); - }); - - it('allows file contents to be read w/ offset & length', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferAlloc(12); - fs.read(fd, buffer, 2, 4, 0, function(err2, bytesRead, buf) { - if (err2) { - return done(err2); - } - assert.equal(bytesRead, 4); - assert.equal(buf, buffer); - assert.equal(String(buffer.slice(2, 6)), 'file'); - done(); - }); - }); - }); - - it('allows file contents to be read w/ position', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferAlloc(7); - fs.read(fd, buffer, 0, 7, 5, function(err2, bytesRead, buf) { - if (err2) { - return done(err2); - } - assert.equal(bytesRead, 7); - assert.equal(buf, buffer); - assert.equal(String(buffer), 'content'); - done(); - }); - }); - }); - - it('allows read w/ offset, length, & position', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferAlloc(12); - fs.read(fd, buffer, 2, 7, 5, function(err2, bytesRead, buf) { - if (err2) { - return done(err2); - } - assert.equal(bytesRead, 7); - assert.equal(buf, buffer); - assert.equal(String(buffer.slice(2, 9)), 'content'); - done(); - }); - }); - }); - - it('fails for closed file descriptor', function(done) { - const fd = fs.openSync('path/to/file.txt', 'r'); - fs.closeSync(fd); - fs.read(fd, bufferAlloc(12), 0, 12, 0, function(err, bytesRead, buf) { - assert.instanceOf(err, Error); - assert.equal(0, bytesRead); - done(); - }); - }); - - it('fails if not open for reading', function(done) { - const fd = fs.openSync('path/to/file.txt', 'w'); - fs.read(fd, bufferAlloc(12), 0, 12, 0, function(err, bytesRead, buf) { - assert.instanceOf(err, Error); - assert.equal(0, bytesRead); - done(); - }); - }); - }); - - describe('fs.readSync(fd, buffer, offset, length, position)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('allows a file to be read synchronously', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - const buffer = bufferAlloc(12); - const read = fs.readSync(fd, buffer, 0, 12, 0); - assert.equal(read, 12); - assert.equal(String(buffer), 'file content'); - }); - - it('allows a file to be read in two parts', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - const first = bufferAlloc(4); - fs.readSync(fd, first, 0, 4, 0); - assert.equal(String(first), 'file'); - - const second = bufferAlloc(7); - fs.readSync(fd, second, 0, 7, 5); - assert.equal(String(second), 'content'); - }); - - it('treats null position as current position', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - const first = bufferAlloc(4); - fs.readSync(fd, first, 0, 4, null); - assert.equal(String(first), 'file'); - - // consume the space - assert.equal(fs.readSync(fd, bufferAlloc(1), 0, 1, null), 1); - - const second = bufferAlloc(7); - fs.readSync(fd, second, 0, 7, null); - assert.equal(String(second), 'content'); - }); - }); - - describe('fs.readFile(filename, [options], callback)', function() { - // this is provided by fs.open, fs.fstat, and fs.read - // so more heavily tested elsewhere - - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('allows a file to be read asynchronously', function(done) { - fs.readFile('path/to/file.txt', function(err, data) { - if (err) { - return done(err); - } - assert.isTrue(Buffer.isBuffer(data)); - assert.equal(String(data), 'file content'); - done(); - }); - }); - - if (fs.promises) { - it('allows a file to be read asynchronously in promise', function(done) { - fs.promises.readFile('path/to/file.txt').then( - function(data) { - assert.isTrue(Buffer.isBuffer(data)); - assert.equal(String(data), 'file content'); - done(); - }, - function(err) { - done(err); - } - ); - }); - } - - it('fails for directory', function(done) { - fs.readFile('path/to', function(err, data) { - assert.instanceOf(err, Error); - done(); - }); - }); - - it('fails for bad path', function(done) { - fs.readFile('path/to/bogus', function(err, data) { - assert.instanceOf(err, Error); - // windows has different errno for ENOENT - assert.equal(err.code, 'ENOENT'); - done(); - }); - }); - }); - - describe('fs.readFileSync(filename, [options])', function() { - // this is provided by fs.openSync, fs.fstatSync, and fs.readSync - // so more heavily tested elsewhere - - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('allows a file to be read synchronously', function() { - const data = fs.readFileSync('path/to/file.txt'); - assert.isTrue(Buffer.isBuffer(data)); - assert.equal(String(data), 'file content'); - }); - - it('fails for directory', function() { - assert.throws(function() { - fs.readFileSync('path/to'); - }); - }); - - it('fails for bad path', function() { - assert.throws(function() { - fs.readFileSync('path/to/bogus'); - }); - }); - }); - - describe('fs.write(fd, buffer, offset, length, position, callback)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('writes a buffer to a file', function(done) { - const fd = fs.openSync('path/new-file.txt', 'w'); - const buffer = bufferFrom('new file'); - fs.write(fd, buffer, 0, buffer.length, null, function(err, written, buf) { - if (err) { - return done(err); - } - assert.equal(written, 8); - assert.equal(buf, buffer); - assert.equal(String(fs.readFileSync('path/new-file.txt')), 'new file'); - done(); - }); - }); - - it('can write a portion of a buffer to a file', function(done) { - fs.open('path/new-file.txt', 'w', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferFrom('new file'); - fs.write(fd, buffer, 1, 5, null, function(err2, written, buf) { - if (err2) { - return done(err2); - } - assert.equal(written, 5); - assert.equal(buf, buffer); - assert.equal(String(fs.readFileSync('path/new-file.txt')), 'ew fi'); - done(); - }); - }); - }); - - it('can append to a file', function(done) { - fs.open('path/to/file.txt', 'a', function(err, fd) { - if (err) { - return done(err); - } - const buffer = bufferFrom(' more'); - fs.write(fd, buffer, 0, 5, null, function(err2, written, buf) { - if (err2) { - return done(err2); - } - assert.equal(written, 5); - assert.equal(buf, buffer); - assert.equal( - String(fs.readFileSync('path/to/file.txt')), - 'file content more' - ); - done(); - }); - }); - }); - - it('fails if file not open for writing', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - fs.write(fd, bufferFrom('oops'), 0, 4, null, function(err2) { - assert.instanceOf(err2, Error); - done(); - }); - }); - }); - }); - - describe('fs.writeSync(fd, buffer, offset, length, position)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('writes a buffer to a file', function() { - const buffer = bufferFrom('new file'); - const fd = fs.openSync('path/new-file.txt', 'w'); - const written = fs.writeSync(fd, buffer, 0, buffer.length); - assert.equal(written, 8); - assert.equal(String(fs.readFileSync('path/new-file.txt')), 'new file'); - }); - - it('can write a portion of a buffer to a file', function() { - const buffer = bufferFrom('new file'); - const fd = fs.openSync('path/new-file.txt', 'w'); - const written = fs.writeSync(fd, buffer, 1, 5); - assert.equal(written, 5); - assert.equal(String(fs.readFileSync('path/new-file.txt')), 'ew fi'); - }); - - it('can append to a file', function() { - const buffer = bufferFrom(' more'); - const fd = fs.openSync('path/to/file.txt', 'a'); - const written = fs.writeSync(fd, buffer, 0, 5); - assert.equal(written, 5); - assert.equal( - String(fs.readFileSync('path/to/file.txt')), - 'file content more' - ); - }); - - it('fails if file not open for writing', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - assert.throws(function() { - fs.writeSync(fd, bufferFrom('oops'), 0, 4); - }); - }); - }); - - describe('fs.write(fd, data[, position[, encoding]], callback)', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('writes a string to a file', function(done) { - fs.open('path/new-file.txt', 'w', function(err, fd) { - if (err) { - return done(err); - } - const string = 'new file'; - fs.write(fd, string, null, 'utf-8', function(err2, written, str) { - if (err2) { - return done(err2); - } - assert.equal(written, 8); - assert.equal(str, string); - assert.equal(fs.readFileSync('path/new-file.txt'), 'new file'); - done(); - }); - }); - }); - - it('can append to a file', function(done) { - fs.open('path/to/file.txt', 'a', function(err, fd) { - if (err) { - return done(err); - } - const string = ' more'; - fs.write(fd, string, null, 'utf-8', function(err2, written, str) { - if (err2) { - return done(err2); - } - assert.equal(written, 5); - assert.equal(str, string); - assert.equal( - fs.readFileSync('path/to/file.txt'), - 'file content more' - ); - done(); - }); - }); - }); - - it('fails if file not open for writing', function(done) { - fs.open('path/to/file.txt', 'r', function(err, fd) { - if (err) { - return done(err); - } - fs.write(fd, 'oops', null, 'utf-8', function(err2) { - assert.instanceOf(err2, Error); - done(); - }); - }); - }); - }); - - describe('fs.writeSync(fd, data[, position[, encoding]])', function() { - beforeEach(function() { - mock({ - 'path/to/file.txt': 'file content' - }); - }); - afterEach(mock.restore); - - it('writes a string to a file', function() { - const fd = fs.openSync('path/new-file.txt', 'w'); - const string = 'new file'; - const written = fs.writeSync(fd, string, null, 'utf-8'); - assert.equal(written, 8); - assert.equal(fs.readFileSync('path/new-file.txt'), 'new file'); - }); - - it('can append to a file', function() { - const fd = fs.openSync('path/to/file.txt', 'a'); - const string = ' more'; - const written = fs.writeSync(fd, string, null, 'utf-8'); - assert.equal(written, 5); - assert.equal(fs.readFileSync('path/to/file.txt'), 'file content more'); - }); - - it('fails if file not open for writing', function() { - const fd = fs.openSync('path/to/file.txt', 'r'); - assert.throws(function() { - fs.writeSync(fd, 'oops', null, 'utf-8'); - }); - }); - }); - - describe('fs.writeFile(filename, data, [options], callback)', function() { - beforeEach(function() { - mock({ - dir: mock.directory({ - mtime: new Date(1) - }) - }); - }); - afterEach(mock.restore); - - it('writes a string to a file', function(done) { - fs.writeFile('dir/foo', 'bar', function(err) { - if (err) { - return done(err); - } - assert.equal(String(fs.readFileSync('dir/foo')), 'bar'); - done(); - }); - }); - - if (fs.promises) { - it('writes a string to a file in promise', function(done) { - fs.promises.writeFile('dir/foo', 'bar').then( - function() { - assert.equal(String(fs.readFileSync('dir/foo')), 'bar'); - done(); - }, - function(err) { - done(err); - } - ); - }); - } - - it('updates mtime of parent directory', function(done) { - const oldTime = fs.statSync('dir').mtime; - fs.writeFile('dir/foo', 'bar', function(err) { - if (err) { - return done(err); - } - const newTime = fs.statSync('dir').mtime; - assert.isTrue(newTime > oldTime); - done(); - }); - }); - - it('writes a buffer to a file', function(done) { - fs.writeFile('dir/foo', bufferFrom('bar'), function(err) { - if (err) { - return done(err); - } - assert.equal(String(fs.readFileSync('dir/foo')), 'bar'); - done(); - }); - }); - - it('fails if directory does not exist', function(done) { - fs.writeFile('foo/bar', 'baz', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.writeFileSync(filename, data, [options]', function() { - beforeEach(function() { - mock({ - '.': {} - }); - }); - afterEach(mock.restore); - - it('writes a string to a file', function() { - fs.writeFileSync('foo', 'bar'); - assert.equal(String(fs.readFileSync('foo')), 'bar'); - }); - - it('writes a buffer to a file', function() { - fs.writeFileSync('foo', bufferFrom('bar')); - assert.equal(String(fs.readFileSync('foo')), 'bar'); - }); - - it('fails if directory does not exist', function() { - assert.throws(function() { - fs.writeFileSync('foo/bar', 'baz'); - }); - }); - }); - - describe('fs.appendFile(filename, data, [options], callback)', function() { - beforeEach(function() { - mock({ - 'dir/file.txt': 'file content', - 'link.txt': mock.symlink({path: 'dir/file.txt'}) - }); - }); - afterEach(mock.restore); - - it('writes a string to a new file', function(done) { - fs.appendFile('foo', 'bar', function(err) { - if (err) { - return done(err); - } - assert.equal(String(fs.readFileSync('foo')), 'bar'); - done(); - }); - }); - - it('appends a string to an existing file', function(done) { - fs.appendFile('dir/file.txt', ' bar', function(err) { - if (err) { - return done(err); - } - assert.equal( - String(fs.readFileSync('dir/file.txt')), - 'file content bar' - ); - done(); - }); - }); - - it('appends a buffer to a file', function(done) { - fs.appendFile('dir/file.txt', bufferFrom(' bar'), function(err) { - if (err) { - return done(err); - } - assert.equal( - String(fs.readFileSync('dir/file.txt')), - 'file content bar' - ); - done(); - }); - }); - - it('appends via a symbolic link file', function(done) { - fs.appendFile('link.txt', ' bar', function(err) { - if (err) { - return done(err); - } - assert.equal( - String(fs.readFileSync('dir/file.txt')), - 'file content bar' - ); - done(); - }); - }); - - it('fails if directory does not exist', function(done) { - fs.appendFile('foo/bar', 'baz', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.appendFileSync(filename, data, [options]', function() { - beforeEach(function() { - mock({ - 'path/to/file': 'content' - }); - }); - afterEach(mock.restore); - - it('writes a string to a new file', function() { - fs.appendFileSync('foo', 'bar'); - assert.equal(String(fs.readFileSync('foo')), 'bar'); - }); - - it('appends a string to an existing file', function() { - fs.appendFileSync('path/to/file', ' bar'); - assert.equal(String(fs.readFileSync('path/to/file')), 'content bar'); - }); - - it('fails if directory does not exist', function() { - assert.throws(function() { - fs.appendFileSync('foo/bar', 'baz'); - }); - }); - }); - - describe('fs.mkdir(path, [mode], callback)', function() { - beforeEach(function() { - mock({ - parent: { - 'file.md': '', - child: {} - }, - 'file.txt': '', - unwriteable: mock.directory({mode: parseInt('0555', 8)}) - }); - }); - afterEach(mock.restore); - - it('creates a new directory', function(done) { - fs.mkdir('parent/dir', function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('parent/dir'); - assert.isTrue(stats.isDirectory()); - done(); - }); - }); - - inVersion('>=10.12').it('creates a new directory recursively', function( - done - ) { - fs.mkdir('parent/foo/bar/dir', {recursive: true}, function(err) { - if (err) { - return done(err); - } - let stats = fs.statSync('parent/foo/bar/dir'); - assert.isTrue(stats.isDirectory()); - stats = fs.statSync('parent/foo/bar'); - assert.isTrue(stats.isDirectory()); - stats = fs.statSync('parent/foo'); - assert.isTrue(stats.isDirectory()); - done(); - }); - }); - - it('accepts dir mode', function(done) { - fs.mkdir('parent/dir', parseInt('0755', 8), function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('parent/dir'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - done(); - }); - }); - - inVersion('>=10.12').it('accepts dir mode recursively', function(done) { - fs.mkdir( - 'parent/foo/bar/dir', - {recursive: true, mode: parseInt('0755', 8)}, - function(err) { - if (err) { - return done(err); - } - let stats = fs.statSync('parent/foo/bar/dir'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - - stats = fs.statSync('parent/foo/bar'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - - stats = fs.statSync('parent/foo'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - done(); - } - ); - }); - - it('fails if parent does not exist', function(done) { - fs.mkdir('parent/bogus/dir', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - - inVersion('>=10.12').it( - 'fails if one parent is not a folder in recursive creation', - function(done) { - fs.mkdir('file.txt/bogus/dir', {recursive: true}, function(err) { - assert.instanceOf(err, Error); - done(); - }); - } - ); - - inVersion('>=10.12').it( - 'fails if permission does not allow recursive creation', - function(done) { - fs.mkdir( - 'parent/foo/bar/dir', - {recursive: true, mode: parseInt('0400', 8)}, - function(err) { - assert.instanceOf(err, Error); - done(); - } - ); - } - ); - - it('fails if directory already exists', function(done) { - fs.mkdir('parent', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - - it('fails if file already exists', function(done) { - fs.mkdir('file.txt', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - - inVersion('>=10.12').it( - 'fails in recursive mode if file already exists', - function(done) { - fs.mkdir('parent/file.md', {recursive: true}, function(err) { - assert.instanceOf(err, Error); - done(); - }); - } - ); - - inVersion('>=10.12').it( - 'passes in recursive mode if directory already exists', - function(done) { - fs.mkdir('parent/child', {recursive: true}, function(err) { - assert.isNotOk(err, Error); - done(); - }); - } - ); - - if (testParentPerms) { - it('fails if parent is not writeable', function(done) { - fs.mkdir('unwriteable/child', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - } - - it('calls callback with a single argument on success', function(done) { - fs.mkdir('parent/arity', function(_) { - assert.equal(arguments.length, 1); - done(); - }); - }); - - it('calls callback with a single argument on failure', function(done) { - fs.mkdir('parent', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.mkdirSync(path, [mode])', function() { - beforeEach(function() { - mock({ - parent: { - 'file.md': '', - child: {} - }, - 'file.txt': 'content', - unwriteable: mock.directory({mode: parseInt('0555', 8)}) - }); - }); - afterEach(mock.restore); - - it('creates a new directory', function() { - fs.mkdirSync('parent/dir'); - const stats = fs.statSync('parent/dir'); - assert.isTrue(stats.isDirectory()); - }); - - inVersion('>=10.12').it('creates a new directory recursively', function() { - fs.mkdirSync('parent/foo/bar/dir', {recursive: true}); - let stats = fs.statSync('parent/foo/bar/dir'); - assert.isTrue(stats.isDirectory()); - stats = fs.statSync('parent/foo/bar'); - assert.isTrue(stats.isDirectory()); - stats = fs.statSync('parent/foo'); - assert.isTrue(stats.isDirectory()); - }); - - it('accepts dir mode', function() { - fs.mkdirSync('parent/dir', parseInt('0755', 8)); - const stats = fs.statSync('parent/dir'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - }); - - inVersion('>=10.12').it('accepts dir mode recursively', function() { - fs.mkdirSync('parent/foo/bar/dir', { - recursive: true, - mode: parseInt('0755', 8) - }); - let stats = fs.statSync('parent/foo/bar/dir'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - - stats = fs.statSync('parent/foo/bar'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - - stats = fs.statSync('parent/foo'); - assert.isTrue(stats.isDirectory()); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0755', 8)); - }); - - it('fails if parent does not exist', function() { - assert.throws(function() { - fs.mkdirSync('parent/bogus/dir'); - }); - }); - - inVersion('>=10.12').it( - 'fails if one parent is not a folder in recursive creation', - function() { - assert.throws(function() { - fs.mkdirSync('file.txt/bogus/dir', {recursive: true}); - }); - } - ); - - inVersion('>=10.12').it( - 'fails if permission does not allow recursive creation', - function() { - assert.throws(function() { - fs.mkdirSync('parent/foo/bar/dir', { - recursive: true, - mode: parseInt('0400', 8) - }); - }); - } - ); - - it('fails if directory already exists', function() { - assert.throws(function() { - fs.mkdirSync('parent'); - }); - }); - - it('fails if file already exists', function() { - assert.throws(function() { - fs.mkdirSync('file.txt'); - }); - }); - - inVersion('>=10.12').it( - 'fails in recursive mode if file already exists', - function() { - assert.throws(function() { - fs.mkdirSync('parent/file.md', {recursive: true}); - }); - } - ); - - inVersion('>=10.12').it( - 'passes in recursive mode if directory already exists', - function() { - assert.doesNotThrow(function() { - fs.mkdirSync('parent/child', {recursive: true}); - }); - } - ); - - if (testParentPerms) { - it('fails if parent is not writeable', function() { - assert.throws(function() { - fs.mkdirSync('unwriteable/child'); - }); - }); - } - }); - - if (fs.mkdtemp) { - describe('fs.mkdtemp(prefix[, options], callback)', function() { - beforeEach(function() { - mock({ - parent: {}, - file: 'contents', - unwriteable: mock.directory({mode: parseInt('0555', 8)}) - }); - }); - afterEach(mock.restore); - - it('creates a new directory', function(done) { - fs.mkdtemp('parent/dir', function(err, dirPath) { - if (err) { - return done(err); - } - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - done(); - }); - }); - - inVersion('>=6').it('accepts a "utf8" encoding argument', function(done) { - fs.mkdtemp('parent/dir', 'utf8', function(err, dirPath) { - if (err) { - return done(err); - } - assert.isString(dirPath); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - done(); - }); - }); - - inVersion('>=6').it('accepts a "buffer" encoding argument', function( - done - ) { - fs.mkdtemp('parent/dir', 'buffer', function(err, buffer) { - if (err) { - return done(err); - } - assert.instanceOf(buffer, Buffer); - const dirPath = buffer.toString(); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - done(); - }); - }); - - inVersion('>=6').it( - 'accepts an options argument with "utf8" encoding', - function(done) { - fs.mkdtemp('parent/dir', {encoding: 'utf8'}, function(err, dirPath) { - if (err) { - return done(err); - } - assert.isString(dirPath); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - done(); - }); - } - ); - - inVersion('>=6').it( - 'accepts an options argument with "buffer" encoding', - function(done) { - fs.mkdtemp('parent/dir', {encoding: 'buffer'}, function(err, buffer) { - if (err) { - return done(err); - } - assert.instanceOf(buffer, Buffer); - const dirPath = buffer.toString(); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - done(); - }); - } - ); - - it('fails if parent does not exist', function(done) { - fs.mkdtemp('unknown/child', function(err, dirPath) { - if (!err || dirPath) { - done(new Error('Expected failure')); - } else { - assert.isTrue(!dirPath); - assert.instanceOf(err, Error); - assert.equal(err.code, 'ENOENT'); - done(); - } - }); - }); - - it('fails if parent is a file', function(done) { - fs.mkdtemp('file/child', function(err, dirPath) { - if (!err || dirPath) { - done(new Error('Expected failure')); - } else { - assert.isTrue(!dirPath); - assert.instanceOf(err, Error); - assert.equal(err.code, 'ENOTDIR'); - done(); - } - }); - }); - - if (testParentPerms) { - it('fails if parent is not writeable', function(done) { - fs.mkdtemp('unwriteable/child', function(err, dirPath) { - if (!err || dirPath) { - done(new Error('Expected failure')); - } else { - assert.isTrue(!dirPath); - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - done(); - } - }); - }); - } - }); - } - - if (fs.mkdtempSync) { - describe('fs.mkdtempSync(prefix[, options])', function() { - beforeEach(function() { - mock({ - parent: {}, - file: 'contents', - unwriteable: mock.directory({mode: parseInt('0555', 8)}) - }); - }); - afterEach(mock.restore); - - it('creates a new directory', function() { - const dirPath = fs.mkdtempSync('parent/dir'); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - }); - - inVersion('>=6').it('accepts a "utf8" encoding argument', function() { - const dirPath = fs.mkdtempSync('parent/dir', 'utf8'); - assert.isString(dirPath); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - }); - - inVersion('>=6').it('accepts a "buffer" encoding argument', function() { - const buffer = fs.mkdtempSync('parent/dir', 'buffer'); - assert.instanceOf(buffer, Buffer); - const dirPath = buffer.toString(); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - }); - - inVersion('>=6').it( - 'accepts an options argument with "utf8" encoding', - function() { - const dirPath = fs.mkdtempSync('parent/dir', {encoding: 'utf8'}); - assert.isString(dirPath); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - } - ); - - inVersion('>=6').it( - 'accepts an options argument with "buffer" encoding', - function() { - const buffer = fs.mkdtempSync('parent/dir', {encoding: 'buffer'}); - assert.instanceOf(buffer, Buffer); - const dirPath = buffer.toString(); - const parentPath = path.dirname(dirPath); - assert.equal(parentPath, 'parent'); - const stats = fs.statSync(dirPath); - assert.isTrue(stats.isDirectory()); - } - ); - - it('fails if parent does not exist', function() { - assert.throws(function() { - fs.mkdtempSync('unknown/child'); - }); - }); - - it('fails if parent is a file', function() { - assert.throws(function() { - fs.mkdtempSync('file/child'); - }); - }); - - if (testParentPerms) { - it('fails if parent is not writeable', function() { - assert.throws(function() { - fs.mkdtempSync('unwriteable/child'); - }); - }); - } - }); - } - - describe('fs.rmdir(path, callback)', function() { - beforeEach(function() { - mock({ - 'path/to/empty': {}, - unwriteable: mock.directory({ - mode: parseInt('0555', 8), - items: {child: {}} - }) - }); - }); - afterEach(mock.restore); - - it('removes an empty directory', function(done) { - assert.equal(fs.statSync('path/to').nlink, 3); - - fs.rmdir('path/to/empty', function(err) { - if (err) { - return done(err); - } - assert.isFalse(fs.existsSync('path/to/empty')); - assert.equal(fs.statSync('path/to').nlink, 2); - done(); - }); - }); - - it('fails if not empty', function(done) { - fs.rmdir('path/to', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - - if (testParentPerms) { - it('fails if parent is not writeable', function(done) { - fs.rmdir('unwriteable/child', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - } - }); - - describe('fs.rmdirSync(path)', function() { - beforeEach(function() { - mock({ - 'path/empty': {}, - 'file.txt': 'content', - unwriteable: mock.directory({ - mode: parseInt('0555', 8), - items: {child: {}} - }) - }); - }); - afterEach(mock.restore); - - it('removes an empty directory', function() { - fs.rmdirSync('path/empty'); - assert.isFalse(fs.existsSync('path/empty')); - }); - - it('fails if directory does not exist', function() { - assert.throws(function() { - fs.rmdirSync('path/bogus'); - }); - }); - - it('fails if not empty', function() { - assert.throws(function() { - fs.rmdirSync('path'); - }); - }); - - it('fails if file', function() { - assert.throws(function() { - fs.rmdirSync('file.txt'); - }); - }); - - if (testParentPerms) { - it('fails if parent is not writeable', function() { - assert.throws(function() { - fs.rmdirSync('unwriteable/child'); - }); - }); - } - }); - - describe('fs.chown(path, uid, gid, callback)', function() { - beforeEach(function() { - mock({ - 'path/empty': {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('changes ownership of a file', function(done) { - fs.chown('file.txt', 42, 43, done); - }); - - it('fails if file does not exist', function(done) { - fs.chown('bogus.txt', 42, 43, function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.chownSync(path, uid, gid)', function() { - beforeEach(function() { - mock({ - 'path/empty': {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('changes ownership of a file', function() { - fs.chownSync('file.txt', 42, 43); - }); - - it('fails if file does not exist', function() { - assert.throws(function() { - fs.chownSync('bogus.txt', 42, 43); - }); - }); - }); - - describe('fs.fchown(fd, uid, gid, callback)', function() { - beforeEach(function() { - mock({ - 'path/empty': {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('changes ownership of a file', function(done) { - const fd = fs.openSync('file.txt', 'r'); - fs.fchown(fd, 42, 43, done); - }); - }); - - describe('fs.fchownSync(fd, uid, gid)', function() { - beforeEach(function() { - mock({ - 'path/empty': {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('changes ownership of a file', function() { - const fd = fs.openSync('file.txt', 'r'); - fs.fchownSync(fd, 42, 43); - }); - }); - - describe('fs.chmod(path, mode, callback)', function() { - beforeEach(function() { - mock({ - 'file.txt': mock.file({mode: parseInt('0644', 8)}) - }); - }); - afterEach(mock.restore); - - it('changes permissions of a file', function(done) { - fs.chmod('file.txt', parseInt('0664', 8), function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('file.txt'); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0664', 8)); - done(); - }); - }); - - it('fails if file does not exist', function(done) { - fs.chmod('bogus.txt', parseInt('0664', 8), function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.chmodSync(path, mode)', function() { - beforeEach(function() { - mock({ - 'file.txt': mock.file({mode: parseInt('0666', 8)}) - }); - }); - afterEach(mock.restore); - - it('changes permissions of a file', function() { - fs.chmodSync('file.txt', parseInt('0644', 8)); - const stats = fs.statSync('file.txt'); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0644', 8)); - }); - - it('fails if file does not exist', function() { - assert.throws(function() { - fs.chmodSync('bogus.txt', parseInt('0644', 8)); - }); - }); - }); - - describe('fs.fchmod(fd, mode, callback)', function() { - beforeEach(function() { - mock({ - 'file.txt': mock.file({mode: parseInt('0666', 8)}) - }); - }); - afterEach(mock.restore); - - it('changes permissions of a file', function(done) { - const fd = fs.openSync('file.txt', 'r'); - fs.fchmod(fd, parseInt('0644', 8), function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('file.txt'); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0644', 8)); - done(); - }); - }); - }); - - describe('fs.fchmodSync(fd, mode)', function() { - beforeEach(function() { - mock({ - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('changes permissions of a file', function() { - const fd = fs.openSync('file.txt', 'r'); - fs.fchmodSync(fd, parseInt('0444', 8)); - const stats = fs.statSync('file.txt'); - assert.equal(stats.mode & parseInt('0777', 8), parseInt('0444', 8)); - }); - }); - - describe('fs.unlink(path, callback)', function() { - beforeEach(function() { - mock({ - dir: {}, - dir2: mock.directory({ - mtime: new Date(1), - items: {file: 'content here'} - }), - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('deletes a file', function(done) { - fs.unlink('file.txt', function(err) { - if (err) { - return done(err); - } - assert.isFalse(fs.existsSync('file.txt')); - done(); - }); - }); - - it('updates mtime of parent', function(done) { - const oldTime = fs.statSync('dir2').mtime; - fs.unlink('dir2/file', function(err) { - if (err) { - return done(err); - } - assert.isFalse(fs.existsSync('dir2/file')); - const newTime = fs.statSync('dir2').mtime; - assert.isTrue(newTime > oldTime); - done(); - }); - }); - - it('fails for a directory', function(done) { - fs.unlink('dir', function(err) { - assert.instanceOf(err, Error); - assert.isTrue(fs.existsSync('dir')); - done(); - }); - }); - - it('respects previously opened file descriptors', function(done) { - const fd = fs.openSync('file.txt', 'r'); - fs.unlink('file.txt', function(err) { - if (err) { - return done(err); - } - assert.isFalse(fs.existsSync('file.txt')); - // but we can still use fd to read - const buffer = bufferAlloc(7); - const read = fs.readSync(fd, buffer, 0, 7); - assert.equal(read, 7); - assert.equal(String(buffer), 'content'); - done(); - }); - }); - }); - - describe('fs.unlinkSync(path)', function() { - beforeEach(function() { - mock({ - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('deletes a file', function() { - fs.unlinkSync('file.txt'); - assert.isFalse(fs.existsSync('file.txt')); - }); - - it('respects previously opened file descriptors', function() { - const fd = fs.openSync('file.txt', 'r'); - fs.unlinkSync('file.txt'); - assert.isFalse(fs.existsSync('file.txt')); - // but we can still use fd to read - const buffer = bufferAlloc(7); - const read = fs.readSync(fd, buffer, 0, 7); - assert.equal(read, 7); - assert.equal(String(buffer), 'content'); - }); - }); - - describe('fs.utimes(path, atime, mtime, callback)', function() { - beforeEach(function() { - mock({ - dir: {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('updates timestamps for a file', function(done) { - fs.utimes('file.txt', new Date(100), new Date(200), function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('file.txt'); - assert.equal(stats.atime.getTime(), 100); - assert.equal(stats.mtime.getTime(), 200); - done(); - }); - }); - - it('updates timestamps for a directory', function(done) { - fs.utimes('dir', new Date(300), new Date(400), function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('dir'); - assert.equal(stats.atime.getTime(), 300); - assert.equal(stats.mtime.getTime(), 400); - done(); - }); - }); - - it('fails for a bogus path', function(done) { - fs.utimes('bogus.txt', new Date(100), new Date(200), function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.utimesSync(path, atime, mtime)', function() { - beforeEach(function() { - mock({ - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('updates timestamps for a file', function() { - fs.utimesSync('file.txt', new Date(100), new Date(200)); - const stats = fs.statSync('file.txt'); - assert.equal(stats.atime.getTime(), 100); - assert.equal(stats.mtime.getTime(), 200); - }); - }); - - describe('fs.futimes(fd, atime, mtime, callback)', function() { - beforeEach(function() { - mock({ - dir: {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('updates timestamps for a file', function(done) { - const fd = fs.openSync('file.txt', 'r'); - fs.futimes(fd, new Date(100), new Date(200), function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('file.txt'); - assert.equal(stats.atime.getTime(), 100); - assert.equal(stats.mtime.getTime(), 200); - done(); - }); - }); - - it('updates timestamps for a directory', function(done) { - const fd = fs.openSync('dir', 'r'); - fs.futimes(fd, new Date(300), new Date(400), function(err) { - if (err) { - return done(err); - } - const stats = fs.statSync('dir'); - assert.equal(stats.atime.getTime(), 300); - assert.equal(stats.mtime.getTime(), 400); - done(); - }); - }); - }); - - describe('fs.futimesSync(path, atime, mtime)', function() { - beforeEach(function() { - mock({ - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('updates timestamps for a file', function() { - const fd = fs.openSync('file.txt', 'r'); - fs.futimesSync(fd, new Date(100), new Date(200)); - const stats = fs.statSync('file.txt'); - assert.equal(stats.atime.getTime(), 100); - assert.equal(stats.mtime.getTime(), 200); - }); - }); - - describe('fs.link(srcpath, dstpath, callback)', function() { - beforeEach(function() { - mock({ - dir: {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('creates a link to a file', function(done) { - assert.equal(fs.statSync('file.txt').nlink, 1); - - fs.link('file.txt', 'link.txt', function(err) { - if (err) { - return done(err); - } - assert.isTrue(fs.statSync('link.txt').isFile()); - assert.equal(fs.statSync('link.txt').nlink, 2); - assert.equal(fs.statSync('file.txt').nlink, 2); - assert.equal(String(fs.readFileSync('link.txt')), 'content'); - done(); - }); - }); - - it('works if original is renamed', function(done) { - fs.link('file.txt', 'link.txt', function(err) { - if (err) { - return done(err); - } - fs.renameSync('file.txt', 'renamed.txt'); - assert.isTrue(fs.statSync('link.txt').isFile()); - assert.equal(String(fs.readFileSync('link.txt')), 'content'); - done(); - }); - }); - - it('works if original is removed', function(done) { - assert.equal(fs.statSync('file.txt').nlink, 1); - - fs.link('file.txt', 'link.txt', function(err) { - if (err) { - return done(err); - } - assert.equal(fs.statSync('link.txt').nlink, 2); - assert.equal(fs.statSync('file.txt').nlink, 2); - fs.unlinkSync('file.txt'); - assert.isTrue(fs.statSync('link.txt').isFile()); - assert.equal(fs.statSync('link.txt').nlink, 1); - assert.equal(String(fs.readFileSync('link.txt')), 'content'); - done(); - }); - }); - - it('fails if original is a directory', function(done) { - fs.link('dir', 'link', function(err) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.linkSync(srcpath, dstpath)', function() { - beforeEach(function() { - mock({ - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('creates a link to a file', function() { - fs.linkSync('file.txt', 'link.txt'); - assert.isTrue(fs.statSync('link.txt').isFile()); - assert.equal(String(fs.readFileSync('link.txt')), 'content'); - }); - - it('works if original is renamed', function() { - fs.linkSync('file.txt', 'link.txt'); - fs.renameSync('file.txt', 'renamed.txt'); - assert.isTrue(fs.statSync('link.txt').isFile()); - assert.equal(String(fs.readFileSync('link.txt')), 'content'); - }); - - it('works if original is removed', function() { - fs.linkSync('file.txt', 'link.txt'); - fs.unlinkSync('file.txt'); - assert.isTrue(fs.statSync('link.txt').isFile()); - assert.equal(String(fs.readFileSync('link.txt')), 'content'); - }); - - it('fails if original is a directory', function() { - assert.throws(function() { - fs.linkSync('dir', 'link'); - }); - }); - }); - - describe('fs.symlink(srcpath, dstpath, [type], callback)', function() { - beforeEach(function() { - mock({ - dir: {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('creates a symbolic link to a file', function(done) { - fs.symlink('../file.txt', 'dir/link.txt', function(err) { - if (err) { - return done(err); - } - assert.isTrue(fs.statSync('dir/link.txt').isFile()); - assert.equal(String(fs.readFileSync('dir/link.txt')), 'content'); - done(); - }); - }); - - it('breaks if original is renamed', function(done) { - fs.symlink('file.txt', 'link.txt', function(err) { - if (err) { - return done(err); - } - assert.isTrue(fs.existsSync('link.txt')); - fs.renameSync('file.txt', 'renamed.txt'); - assert.isFalse(fs.existsSync('link.txt')); - done(); - }); - }); - - it('works if original is a directory', function(done) { - fs.symlink('dir', 'link', function(err) { - if (err) { - return done(err); - } - assert.isTrue(fs.statSync('link').isDirectory()); - done(); - }); - }); - }); - - describe('fs.symlinkSync(srcpath, dstpath, [type])', function() { - beforeEach(function() { - mock({ - dir: {}, - 'file.txt': 'content' - }); - }); - afterEach(mock.restore); - - it('creates a symbolic link to a file', function() { - fs.symlinkSync('../file.txt', 'dir/link.txt'); - assert.isTrue(fs.statSync('dir/link.txt').isFile()); - assert.equal(String(fs.readFileSync('dir/link.txt')), 'content'); - }); - - it('breaks if original is renamed', function() { - fs.symlinkSync('file.txt', 'link.txt'); - assert.isTrue(fs.existsSync('link.txt')); - fs.renameSync('file.txt', 'renamed.txt'); - assert.isFalse(fs.existsSync('link.txt')); - }); - - it('works if original is a directory', function() { - fs.symlinkSync('dir', 'link'); - assert.isTrue(fs.statSync('link').isDirectory()); - }); - }); - - describe('fs.readlink(path, callback)', function() { - beforeEach(function() { - mock({ - 'file.txt': 'content', - link: mock.symlink({path: './file.txt'}) - }); - }); - afterEach(mock.restore); - - it('reads a symbolic link', function(done) { - fs.readlink('link', function(err, srcPath) { - if (err) { - return done(err); - } - assert.equal(srcPath, './file.txt'); - done(); - }); - }); - - it('fails for regular files', function(done) { - fs.readlink('file.txt', function(err, srcPath) { - assert.instanceOf(err, Error); - done(); - }); - }); - }); - - describe('fs.readlinkSync(path)', function() { - beforeEach(function() { - mock({ - 'file.txt': 'content', - link: mock.symlink({path: './file.txt'}) - }); - }); - afterEach(mock.restore); - - it('reads a symbolic link', function() { - assert.equal(fs.readlinkSync('link'), './file.txt'); - }); - - it('fails for regular files', function() { - assert.throws(function() { - fs.readlinkSync('file.txt'); - }); - }); - }); - - describe('fs.lstat(path, callback)', function() { - beforeEach(function() { - mock({ - 'file.txt': mock.file({ - content: 'content', - mtime: new Date(1) - }), - link: mock.symlink({ - path: './file.txt', - mtime: new Date(2) - }) - }); - }); - afterEach(mock.restore); - - it('stats a symbolic link', function(done) { - fs.lstat('link', function(err, stats) { - if (err) { - return done(err); - } - assert.isTrue(stats.isSymbolicLink()); - assert.isFalse(stats.isFile()); - assert.equal(stats.mtime.getTime(), 2); - done(); - }); - }); - - it('stats a regular file', function(done) { - fs.lstat('file.txt', function(err, stats) { - if (err) { - return done(err); - } - assert.isTrue(stats.isFile()); - assert.isFalse(stats.isSymbolicLink()); - assert.equal(stats.mtime.getTime(), 1); - done(); - }); - }); - }); - - describe('fs.lstatSync(path)', function() { - beforeEach(function() { - mock({ - 'file.txt': mock.file({ - content: 'content', - mtime: new Date(1) - }), - link: mock.symlink({ - path: './file.txt', - mtime: new Date(2) - }) - }); - }); - afterEach(mock.restore); - - it('stats a symbolic link', function() { - const stats = fs.lstatSync('link'); - assert.isTrue(stats.isSymbolicLink()); - assert.isFalse(stats.isFile()); - assert.equal(stats.mtime.getTime(), 2); - }); - - it('stats a regular file', function() { - const stats = fs.lstatSync('file.txt'); - assert.isTrue(stats.isFile()); - assert.isFalse(stats.isSymbolicLink()); - assert.equal(stats.mtime.getTime(), 1); - }); - }); - - describe('fs.realpath(path, [cache], callback)', function() { - // based on binding.lstat and binding.readlink so tested elsewhere as well - - beforeEach(function() { - mock({ - 'dir/file.txt': 'content', - link: mock.symlink({path: './dir/file.txt'}) - }); - }); - afterEach(mock.restore); - - it('resolves the real path for a symbolic link', function(done) { - fs.realpath('link', function(err, resolved) { - if (err) { - return done(err); - } - assert.equal(resolved, path.resolve('dir/file.txt')); - done(); - }); - }); - - it('resolves the real path regular file', function(done) { - fs.realpath('dir/file.txt', function(err, resolved) { - if (err) { - return done(err); - } - assert.equal(resolved, path.resolve('dir/file.txt')); - done(); - }); - }); - }); - - describe('fs.createReadStream(path, [options])', function() { - beforeEach(function() { - mock({ - 'dir/source': 'source content' - }); - }); - afterEach(mock.restore); - - it('creates a readable stream', function() { - const stream = fs.createReadStream('dir/source'); - assert.isTrue(stream.readable); - }); - - it('allows piping to a writable stream', function(done) { - const input = fs.createReadStream('dir/source'); - const output = fs.createWriteStream('dir/dest'); - output.on('close', function() { - fs.readFile('dir/dest', function(err, data) { - if (err) { - return done(err); - } - assert.equal(String(data), 'source content'); - done(); - }); - }); - output.on('error', done); - - input.pipe(output); - }); - }); - - describe('fs.createWriteStream(path[, options])', function() { - beforeEach(function() { - mock(); - }); - afterEach(mock.restore); - - it('provides a write stream for a file in buffered mode', function(done) { - const output = fs.createWriteStream('test.txt'); - output.on('close', function() { - fs.readFile('test.txt', function(err, data) { - if (err) { - return done(err); - } - assert.equal(String(data), 'lots of source content'); - done(); - }); - }); - output.on('error', done); - - // if output._writev is available, buffered multiple writes will hit _writev. - // otherwise, hit multiple _write. - output.write(bufferFrom('lots ')); - output.write(bufferFrom('of ')); - output.write(bufferFrom('source ')); - output.end(bufferFrom('content')); - }); - - it('provides a write stream for a file', function(done) { - const output = fs.createWriteStream('test.txt'); - output.on('close', function() { - fs.readFile('test.txt', function(err, data) { - if (err) { - return done(err); - } - assert.equal(String(data), 'lots of source content'); - done(); - }); - }); - output.on('error', done); - - output.write(bufferFrom('lots ')); - setTimeout(function() { - output.write(bufferFrom('of ')); - setTimeout(function() { - output.write(bufferFrom('source ')); - setTimeout(function() { - output.end(bufferFrom('content')); - }, 50); - }, 50); - }, 50); - }); - - if (Writable && Writable.prototype.cork) { - it('works when write stream is corked', function(done) { - const output = fs.createWriteStream('test.txt'); - output.on('close', function() { - fs.readFile('test.txt', function(err, data) { - if (err) { - return done(err); - } - assert.equal(String(data), 'lots of source content'); - done(); - }); - }); - output.on('error', done); - - output.cork(); - output.write(bufferFrom('lots ')); - output.write(bufferFrom('of ')); - output.write(bufferFrom('source ')); - output.end(bufferFrom('content')); - output.uncork(); - }); - } - }); - - describe('process.cwd()', function() { - afterEach(mock.restore); - - it('maintains current working directory', function() { - const originalCwd = process.cwd(); - mock(); - - const cwd = process.cwd(); - assert.equal(cwd, originalCwd); - }); - - it('allows changing directory', function() { - const originalCwd = process.cwd(); - mock({ - dir: {} - }); - - process.chdir('dir'); - const cwd = process.cwd(); - assert.equal(cwd, path.join(originalCwd, 'dir')); - }); - - it('prevents changing directory to non-existent path', function() { - mock(); - - let err; - try { - process.chdir('dir'); - } catch (e) { - err = e; - } - assert.instanceOf(err, Error); - assert.equal(err.code, 'ENOENT'); - }); - - it('prevents changing directory to non-directory path', function() { - mock({ - file: '' - }); - - let err; - try { - process.chdir('file'); - } catch (e) { - err = e; - } - assert.instanceOf(err, Error); - assert.equal(err.code, 'ENOTDIR'); - }); - - it('restores original methods on restore', function() { - const originalCwd = process.cwd; - const originalChdir = process.chdir; - mock(); - - mock.restore(); - assert.equal(process.cwd, originalCwd); - assert.equal(process.chdir, originalChdir); - }); - - it('restores original working directory on restore', function() { - const originalCwd = process.cwd(); - mock({ - dir: {} - }); - - process.chdir('dir'); - mock.restore(); - - const cwd = process.cwd(); - assert.equal(cwd, originalCwd); + let err; + try { + fs.writeFileSync('insecure/read-only', 'denied'); + } catch (e) { + err = e; + } + assert.instanceOf(err, Error); + assert.equal(err.code, 'EACCES'); }); }); - - if (process.getuid && process.getgid) { - describe('security', function() { - afterEach(mock.restore); - - it('denies dir listing without execute on parent', function() { - mock({ - secure: mock.directory({ - mode: parseInt('0666', 8), - items: { - insecure: { - file: 'file content' - } - } - }) - }); - - let err; - try { - fs.readdirSync('secure/insecure'); - } catch (e) { - err = e; - } - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - }); - - it('denies file read without execute on parent', function() { - mock({ - secure: mock.directory({ - mode: parseInt('0666', 8), - items: { - insecure: { - file: 'file content' - } - } - }) - }); - - let err; - try { - fs.readFileSync('secure/insecure/file'); - } catch (e) { - err = e; - } - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - }); - - it('denies file read without read on file', function() { - mock({ - insecure: { - 'write-only': mock.file({ - mode: parseInt('0222', 8), - content: 'write only' - }) - } - }); - - let err; - try { - fs.readFileSync('insecure/write-only'); - } catch (e) { - err = e; - } - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - }); - - it('denies file write without write on file', function() { - mock({ - insecure: { - 'read-only': mock.file({ - mode: parseInt('0444', 8), - content: 'read only' - }) - } - }); - - let err; - try { - fs.writeFileSync('insecure/read-only', 'denied'); - } catch (e) { - err = e; - } - assert.instanceOf(err, Error); - assert.equal(err.code, 'EACCES'); - }); - }); - } -}); +}