Skip to content

Commit

Permalink
fix: synchronize patching binding/createWriteStream/cwd/chdir
Browse files Browse the repository at this point in the history
All the patches now check same realBinding._mockedBinding. This should
fix the edge cases around the new feature bypass().

follows up #306
  • Loading branch information
3cp committed Aug 15, 2020
1 parent 47a7979 commit 015b22b
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 79 deletions.
19 changes: 14 additions & 5 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ function overrideCreateWriteStream() {
fs.createWriteStream = function(path, options) {
const output = realCreateWriteStream(path, options);
// disable _writev, this will over shadow WriteStream.prototype._writev
output._writev = undefined;
if (realBinding._mockedBinding) {
output._writev = undefined;
}
return output;
};
}
Expand Down Expand Up @@ -126,13 +128,20 @@ exports = module.exports = function mock(config, options) {
let currentPath = process.cwd();
overrideProcess(
function cwd() {
return currentPath;
if (realBinding._mockedBinding) {
return currentPath;
}
return realProcessProps.cwd();
},
function chdir(directory) {
if (!binding.stat(toNamespacedPath(directory)).isDirectory()) {
throw new FSError('ENOTDIR');
if (realBinding._mockedBinding) {
if (!binding.stat(toNamespacedPath(directory)).isDirectory()) {
throw new FSError('ENOTDIR');
}
currentPath = path.resolve(currentPath, directory);
} else {
return realProcessProps.chdir(directory);
}
currentPath = path.resolve(currentPath, directory);
}
);

Expand Down
104 changes: 104 additions & 0 deletions test/lib/bypass.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use strict';

const helper = require('../helper');
const fs = require('fs');
const mock = require('../../lib/index');
const path = require('path');
const withPromise = helper.withPromise;

const assert = helper.assert;

describe('mock.bypass()', () => {
afterEach(mock.restore);

it('runs a synchronous function using the real filesystem', () => {
mock({'/path/to/file': 'content'});

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
assert.isNotOk(fs.existsSync(__filename));
assert.isOk(mock.bypass(() => fs.existsSync(__filename)));

assert.isNotOk(fs.existsSync(__filename));
});

it('handles functions that throw', () => {
mock({'/path/to/file': 'content'});

const error = new Error('oops');

assert.throws(() => {
mock.bypass(() => {
assert.isFalse(fs.existsSync('/path/to/file'));
throw error;
});
}, error);

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
});

it('bypasses patched process.cwd() and process.chdir()', () => {
const originalCwd = process.cwd();
mock({
dir: {}
});

process.chdir('dir');
assert.equal(process.cwd(), path.join(originalCwd, 'dir'));

mock.bypass(() => {
assert.equal(process.cwd(), originalCwd);
process.chdir('lib');
assert.equal(process.cwd(), path.join(originalCwd, 'lib'));
process.chdir('..');
assert.equal(process.cwd(), originalCwd);
});
assert.equal(process.cwd(), path.join(originalCwd, 'dir'));
mock.restore();

assert.equal(process.cwd(), originalCwd);
});

withPromise.it('runs an async function using the real filesystem', done => {
mock({'/path/to/file': 'content'});

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
assert.isFalse(fs.existsSync(__filename));

const promise = mock.bypass(() => fs.promises.stat(__filename));
assert.instanceOf(promise, Promise);

promise
.then(stat => {
assert.isTrue(stat.isFile());
assert.isFalse(fs.existsSync(__filename));
done();
})
.catch(done);
});

withPromise.it('handles promise rejection', done => {
mock({'/path/to/file': 'content'});

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
assert.isFalse(fs.existsSync(__filename));

const error = new Error('oops');

const promise = mock.bypass(() => {
assert.isTrue(fs.existsSync(__filename));
return Promise.reject(error);
});
assert.instanceOf(promise, Promise);

promise
.then(() => {
done(new Error('expected rejection'));
})
.catch(err => {
assert.equal(err, error);

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
done();
});
});
});
74 changes: 0 additions & 74 deletions test/lib/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const path = require('path');
const File = require('../../lib/file');
const {fixWin32Permissions} = require('../../lib/item');
const Directory = require('../../lib/directory');
const withPromise = helper.withPromise;

const assert = helper.assert;
const assetsPath = path.resolve(__dirname, '../assets');
Expand Down Expand Up @@ -185,79 +184,6 @@ describe('The API', function() {
});
});

describe(`mock.bypass()`, () => {
afterEach(mock.restore);

it('runs a synchronous function using the real filesystem', () => {
mock({'/path/to/file': 'content'});

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
assert.isNotOk(fs.existsSync(__filename));
assert.isOk(mock.bypass(() => fs.existsSync(__filename)));

assert.isNotOk(fs.existsSync(__filename));
});

it('handles functions that throw', () => {
mock({'/path/to/file': 'content'});

const error = new Error('oops');

assert.throws(() => {
mock.bypass(() => {
assert.isFalse(fs.existsSync('/path/to/file'));
throw error;
});
}, error);

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
});

withPromise.it('runs an async function using the real filesystem', done => {
mock({'/path/to/file': 'content'});

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
assert.isFalse(fs.existsSync(__filename));

const promise = mock.bypass(() => fs.promises.stat(__filename));
assert.instanceOf(promise, Promise);

promise
.then(stat => {
assert.isTrue(stat.isFile());
assert.isFalse(fs.existsSync(__filename));
done();
})
.catch(done);
});

withPromise.it('handles promise rejection', done => {
mock({'/path/to/file': 'content'});

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
assert.isFalse(fs.existsSync(__filename));

const error = new Error('oops');

const promise = mock.bypass(() => {
assert.isTrue(fs.existsSync(__filename));
return Promise.reject(error);
});
assert.instanceOf(promise, Promise);

promise
.then(() => {
done(new Error('expected rejection'));
})
.catch(err => {
assert.equal(err, error);

assert.equal(fs.readFileSync('/path/to/file', 'utf8'), 'content');
done();
});
});
});

describe(`mock.load()`, () => {
const statsCompareKeys = [
'birthtime',
Expand Down

0 comments on commit 015b22b

Please sign in to comment.