Repository: streamich/memfs
Pull request #17 (2017-08-20 19:37:57)

Added missing .appendFile() and tests


Patch:
diff --git a/README.md b/README.md
index 7cb80701..caa0c27b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-# memfs 2.0 [![][npm-badge]][npm-url] [![][travis-badge]][travis-url]
+# memfs 2.0
+
+[![][npm-badge]][npm-url] [![][travis-badge]][travis-url]
 
 In-memory file-system with [Node's `fs` API](https://nodejs.org/api/fs.html).
 
@@ -16,10 +18,6 @@ Install:
 
     npm install --save memfs
 
-or
-
-    yarn add memfs
-
 Usage:
 
 ```js
diff --git a/lib/constants.js b/lib/constants.js
index 3d0ea46b..f6e0ecc7 100644
--- a/lib/constants.js
+++ b/lib/constants.js
@@ -20,7 +20,7 @@ exports.constants = {
     O_DIRECTORY: 65536,
     O_NOATIME: 262144,
     O_NOFOLLOW: 131072,
-    O_SYNC: 4096,
+    O_SYNC: 1052672,
     O_DIRECT: 16384,
     O_NONBLOCK: 2048,
     S_IRWXU: 448,
diff --git a/lib/volume.js b/lib/volume.js
index 8b0dded6..bce784fa 100644
--- a/lib/volume.js
+++ b/lib/volume.js
@@ -207,7 +207,8 @@ var appendFileDefaults = {
     mode: 438 /* DEFAULT */,
     flag: FLAGS[FLAGS.a],
 };
-var getAppendFileOptions = optsGenerator(appendFileDefaults);
+var getAppendFileOpts = optsGenerator(appendFileDefaults);
+var getAppendFileOptsAndCb = optsAndCbGenerator(getAppendFileOpts);
 var realpathDefaults = optsDefaults;
 var getRealpathOptions = optsGenerator(realpathDefaults);
 var getRealpathOptsAndCb = optsAndCbGenerator(getRealpathOptions);
@@ -1152,12 +1153,19 @@ var Volume = (function () {
     };
     Volume.prototype.appendFileSync = function (id, data, options) {
         if (options === void 0) { options = appendFileDefaults; }
-        var opts = getAppendFileOptions(options);
+        var opts = getAppendFileOpts(options);
         // force append behavior when using a supplied file descriptor
         if (!opts.flag || isFd(id))
             opts.flag = 'a';
         this.writeFileSync(id, data, opts);
     };
+    Volume.prototype.appendFile = function (id, data, a, b) {
+        var _a = getAppendFileOptsAndCb(a, b), opts = _a[0], callback = _a[1];
+        // force append behavior when using a supplied file descriptor
+        if (!opts.flag || isFd(id))
+            opts.flag = 'a';
+        this.writeFile(id, data, opts, callback);
+    };
     Volume.prototype.readdirBase = function (filename, encoding) {
         var steps = filenameToSteps(filename);
         var link = this.getLink(steps);
diff --git a/package.json b/package.json
index af44a2a6..6fd129e4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "memfs",
-  "version": "2.0.11",
+  "version": "2.1.0",
   "description": "In-memory file-system with Node's fs API.",
   "main": "lib/index.js",
   "keywords": [
diff --git a/src/constants.ts b/src/constants.ts
index 5ed266c2..346e723a 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -19,7 +19,7 @@ export const constants = {
     O_DIRECTORY: 65536,
     O_NOATIME: 262144,
     O_NOFOLLOW: 131072,
-    O_SYNC: 4096,
+    O_SYNC: 1052672,
     O_DIRECT: 16384,
     O_NONBLOCK: 2048,
     S_IRWXU: 448,
diff --git a/src/volume.ts b/src/volume.ts
index 16ac7f81..c2480097 100644
--- a/src/volume.ts
+++ b/src/volume.ts
@@ -270,7 +270,8 @@ const appendFileDefaults: IAppendFileOptions = {
     mode: MODE.DEFAULT,
     flag: FLAGS[FLAGS.a],
 };
-const getAppendFileOptions = optsGenerator<IAppendFileOptions>(appendFileDefaults);
+const getAppendFileOpts = optsGenerator<IAppendFileOptions>(appendFileDefaults);
+const getAppendFileOptsAndCb = optsAndCbGenerator<IAppendFileOptions, void>(getAppendFileOpts);
 
 
 // Options for `fs.realpath` and `fs.realpathSync`
@@ -1371,7 +1372,7 @@ export class Volume {
     }
 
     appendFileSync(id: TFileId, data: TData, options: IAppendFileOptions | string = appendFileDefaults) {
-        const opts = getAppendFileOptions(options);
+        const opts = getAppendFileOpts(options);
 
         // force append behavior when using a supplied file descriptor
         if (!opts.flag || isFd(id)) opts.flag = 'a';
@@ -1379,6 +1380,17 @@ export class Volume {
         this.writeFileSync(id, data, opts);
     }
 
+    appendFile(id: TFileId, data: TData, callback: TCallback<void>);
+    appendFile(id: TFileId, data: TData, options: IAppendFileOptions, callback: TCallback<void>);
+    appendFile(id: TFileId, data: TData, a, b?) {
+        const [opts, callback] = getAppendFileOptsAndCb(a, b);
+
+        // force append behavior when using a supplied file descriptor
+        if (!opts.flag || isFd(id)) opts.flag = 'a';
+
+        this.writeFile(id, data, opts, callback);
+    }
+
     private readdirBase(filename: string, encoding: TEncodingExtended): TDataOut[] {
         const steps = filenameToSteps(filename);
         const link: Link = this.getLink(steps);


Test patch:
diff --git a/lib/index.test.js b/lib/index.test.js
new file mode 100644
index 00000000..19089228
--- /dev/null
+++ b/lib/index.test.js
@@ -0,0 +1,40 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var volume_1 = require("./volume");
+var chai_1 = require("chai");
+var fs = require("fs");
+var memfs = require('./index');
+var lists_1 = require("fs-monkey/lib/util/lists");
+describe('memfs', function () {
+    it('Exports Volume constructor', function () {
+        chai_1.expect(typeof memfs.Volume).to.equal('function');
+        chai_1.expect(memfs.Volume).to.equal(volume_1.Volume);
+    });
+    it('Exports constants', function () {
+        chai_1.expect(memfs.F_OK).to.equal(fs.F_OK);
+        chai_1.expect(memfs.R_OK).to.equal(fs.R_OK);
+        chai_1.expect(memfs.W_OK).to.equal(fs.W_OK);
+        chai_1.expect(memfs.X_OK).to.equal(fs.X_OK);
+        chai_1.expect(memfs.constants).to.eql(fs.constants);
+    });
+    it('Exports constructors', function () {
+        chai_1.expect(typeof memfs.Stats).to.equal('function');
+        chai_1.expect(typeof memfs.ReadStream).to.equal('function');
+        chai_1.expect(typeof memfs.WriteStream).to.equal('function');
+        chai_1.expect(typeof memfs.FSWatcher).to.equal('function');
+        chai_1.expect(typeof memfs.StatWatcher).to.equal('function');
+    });
+    it('Exports _toUnixTimestamp', function () {
+        chai_1.expect(typeof memfs._toUnixTimestamp).to.equal('function');
+    });
+    it('Exports all Node\'s filesystem API methods', function () {
+        for (var _i = 0, fsSyncMethods_1 = lists_1.fsSyncMethods; _i < fsSyncMethods_1.length; _i++) {
+            var method = fsSyncMethods_1[_i];
+            chai_1.expect(typeof memfs[method]).to.equal('function');
+        }
+        for (var _a = 0, fsAsyncMethods_1 = lists_1.fsAsyncMethods; _a < fsAsyncMethods_1.length; _a++) {
+            var method = fsAsyncMethods_1[_a];
+            chai_1.expect(typeof memfs[method]).to.equal('function');
+        }
+    });
+});
diff --git a/lib/test/volume/appendFile.test.js b/lib/test/volume/appendFile.test.js
new file mode 100644
index 00000000..8db0c504
--- /dev/null
+++ b/lib/test/volume/appendFile.test.js
@@ -0,0 +1,20 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var chai_1 = require("chai");
+var util_1 = require("./util");
+describe('appendFile(file, data[, options], callback)', function () {
+    it('Simple write to non-existing file', function (done) {
+        var vol = util_1.create();
+        vol.appendFile('/test', 'hello', function (err, res) {
+            chai_1.expect(vol.readFileSync('/test', 'utf8')).to.equal('hello');
+            done();
+        });
+    });
+    it('Append to existing file', function (done) {
+        var vol = util_1.create({ '/a': 'b' });
+        vol.appendFile('/a', 'c', function (err, res) {
+            chai_1.expect(vol.readFileSync('/a', 'utf8')).to.equal('bc');
+            done();
+        });
+    });
+});
diff --git a/lib/test/volume/appendFileSync.test.js b/lib/test/volume/appendFileSync.test.js
new file mode 100644
index 00000000..ce9c270a
--- /dev/null
+++ b/lib/test/volume/appendFileSync.test.js
@@ -0,0 +1,16 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var chai_1 = require("chai");
+var util_1 = require("./util");
+describe('appendFileSync(file, data, options)', function () {
+    it('Simple write to non-existing file', function () {
+        var vol = util_1.create();
+        vol.appendFileSync('/test', 'hello');
+        chai_1.expect(vol.readFileSync('/test', 'utf8')).to.equal('hello');
+    });
+    it('Append to existing file', function () {
+        var vol = util_1.create({ '/a': 'b' });
+        vol.appendFileSync('/a', 'c');
+        chai_1.expect(vol.readFileSync('/a', 'utf8')).to.equal('bc');
+    });
+});
diff --git a/lib/test/volume/openSync.test.js b/lib/test/volume/openSync.test.js
new file mode 100644
index 00000000..9704fddd
--- /dev/null
+++ b/lib/test/volume/openSync.test.js
@@ -0,0 +1,10 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var __1 = require("../..");
+var chai_1 = require("chai");
+describe('openSync(path, mode[, flags])', function () {
+    it('should return a file descriptor', function () {
+        var fd = __1.fs.openSync('/foo', 'w');
+        chai_1.expect(typeof fd).to.equal('number');
+    });
+});
diff --git a/lib/test/volume/util.js b/lib/test/volume/util.js
new file mode 100644
index 00000000..e22f586f
--- /dev/null
+++ b/lib/test/volume/util.js
@@ -0,0 +1,8 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var __1 = require("../..");
+exports.create = function (json) {
+    if (json === void 0) { json = { '/foo': 'bar' }; }
+    var vol = __1.Volume.fromJSON(json);
+    return vol;
+};
diff --git a/lib/test/volume/write.test.js b/lib/test/volume/write.test.js
new file mode 100644
index 00000000..7cb0f5a1
--- /dev/null
+++ b/lib/test/volume/write.test.js
@@ -0,0 +1,22 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var __1 = require("../..");
+var chai_1 = require("chai");
+var create = function (json) {
+    if (json === void 0) { json = { '/foo': 'bar' }; }
+    var vol = __1.Volume.fromJSON(json);
+    return vol;
+};
+describe('write(fs, str, position, encoding, callback)', function () {
+    it('Simple write to file', function (done) {
+        var vol = create();
+        var fd = vol.openSync('/test', 'w');
+        vol.write(fd, 'lol', 0, 'utf8', function (err, bytes, str) {
+            chai_1.expect(err).to.equal(null);
+            chai_1.expect(bytes).to.equal(3);
+            chai_1.expect(str).to.equal('lol');
+            chai_1.expect(vol.readFileSync('/test', 'utf8')).to.equal('lol');
+            done();
+        });
+    });
+});
diff --git a/src/index.test.ts b/src/index.test.ts
new file mode 100644
index 00000000..fa3b0bb1
--- /dev/null
+++ b/src/index.test.ts
@@ -0,0 +1,38 @@
+import {Volume} from './volume';
+import {expect} from 'chai';
+import * as fs from 'fs';
+const memfs = require('./index');
+import {fsSyncMethods, fsAsyncMethods} from 'fs-monkey/lib/util/lists';
+
+
+describe('memfs', () => {
+    it('Exports Volume constructor', () => {
+        expect(typeof memfs.Volume).to.equal('function');
+        expect(memfs.Volume).to.equal(Volume);
+    });
+    it('Exports constants', () => {
+        expect(memfs.F_OK).to.equal((fs as any).F_OK);
+        expect(memfs.R_OK).to.equal((fs as any).R_OK);
+        expect(memfs.W_OK).to.equal((fs as any).W_OK);
+        expect(memfs.X_OK).to.equal((fs as any).X_OK);
+        expect(memfs.constants).to.eql(fs.constants);
+    });
+    it('Exports constructors', () => {
+        expect(typeof memfs.Stats).to.equal('function');
+        expect(typeof memfs.ReadStream).to.equal('function');
+        expect(typeof memfs.WriteStream).to.equal('function');
+        expect(typeof memfs.FSWatcher).to.equal('function');
+        expect(typeof memfs.StatWatcher).to.equal('function');
+    });
+    it('Exports _toUnixTimestamp', () => {
+        expect(typeof memfs._toUnixTimestamp).to.equal('function');
+    });
+    it('Exports all Node\'s filesystem API methods', () => {
+        for(let method of fsSyncMethods) {
+            expect(typeof memfs[method]).to.equal('function');
+        }
+        for(let method of fsAsyncMethods) {
+            expect(typeof memfs[method]).to.equal('function');
+        }
+    });
+});
\ No newline at end of file
diff --git a/src/test/volume/appendFile.test.ts b/src/test/volume/appendFile.test.ts
new file mode 100644
index 00000000..0780853e
--- /dev/null
+++ b/src/test/volume/appendFile.test.ts
@@ -0,0 +1,20 @@
+import {expect} from 'chai';
+import {create} from "./util";
+
+
+describe('appendFile(file, data[, options], callback)', () => {
+    it('Simple write to non-existing file', done => {
+        const vol = create();
+        vol.appendFile('/test', 'hello', (err, res) => {
+            expect(vol.readFileSync('/test', 'utf8')).to.equal('hello');
+            done();
+        });
+    });
+    it('Append to existing file', done => {
+        const vol = create({'/a': 'b'});
+        vol.appendFile('/a', 'c', (err, res) => {
+            expect(vol.readFileSync('/a', 'utf8')).to.equal('bc');
+            done();
+        });
+    });
+});
\ No newline at end of file
diff --git a/src/test/volume/appendFileSync.test.ts b/src/test/volume/appendFileSync.test.ts
new file mode 100644
index 00000000..de6c7abb
--- /dev/null
+++ b/src/test/volume/appendFileSync.test.ts
@@ -0,0 +1,17 @@
+import {Volume} from '../..';
+import {expect} from 'chai';
+import {create} from "./util";
+
+
+describe('appendFileSync(file, data, options)', () => {
+    it('Simple write to non-existing file', () => {
+        const vol = create();
+        vol.appendFileSync('/test', 'hello');
+        expect(vol.readFileSync('/test', 'utf8')).to.equal('hello');
+    });
+    it('Append to existing file', () => {
+        const vol = create({'/a': 'b'});
+        vol.appendFileSync('/a', 'c');
+        expect(vol.readFileSync('/a', 'utf8')).to.equal('bc');
+    });
+});
\ No newline at end of file
diff --git a/src/test/openSync.test.ts b/src/test/volume/openSync.test.ts
similarity index 88%
rename from src/test/openSync.test.ts
rename to src/test/volume/openSync.test.ts
index dc1f3210..ee6380f5 100644
--- a/src/test/openSync.test.ts
+++ b/src/test/volume/openSync.test.ts
@@ -1,4 +1,4 @@
-import {fs} from '..';
+import {fs} from '../..';
 import {expect} from 'chai';
 
 
diff --git a/src/test/volume/util.ts b/src/test/volume/util.ts
new file mode 100644
index 00000000..ba4ba565
--- /dev/null
+++ b/src/test/volume/util.ts
@@ -0,0 +1,7 @@
+import {Volume} from '../..';
+
+
+export const create = (json: {[s: string]: string} = {'/foo': 'bar'}) => {
+    const vol = Volume.fromJSON(json);
+    return vol;
+};
diff --git a/src/test/volume/write.test.ts b/src/test/volume/write.test.ts
new file mode 100644
index 00000000..cd86bfd5
--- /dev/null
+++ b/src/test/volume/write.test.ts
@@ -0,0 +1,23 @@
+import {Volume} from '../..';
+import {expect} from 'chai';
+
+
+const create = (json = {'/foo': 'bar'}) => {
+    const vol = Volume.fromJSON(json);
+    return vol;
+};
+
+
+describe('write(fs, str, position, encoding, callback)', () => {
+    it('Simple write to file', done => {
+        const vol = create();
+        const fd = vol.openSync('/test', 'w');
+        vol.write(fd, 'lol', 0, 'utf8', (err, bytes, str) => {
+            expect(err).to.equal(null);
+            expect(bytes).to.equal(3);
+            expect(str).to.equal('lol');
+            expect(vol.readFileSync('/test', 'utf8')).to.equal('lol');
+            done();
+        });
+    });
+});
\ No newline at end of file