UNPKG

memfs

Version:

In-memory file-system with Node's fs API.

882 lines 43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const __1 = require("../.."); const node_to_fsa_1 = require("../../node-to-fsa"); const FsaNodeFs_1 = require("../FsaNodeFs"); const thingies_1 = require("thingies"); const util_1 = require("../../__tests__/util"); const setup = (json = null, mode = 'readwrite') => { const { fs: mfs, vol } = (0, __1.memfs)({ mountpoint: json }); const dir = (0, node_to_fsa_1.nodeToFsa)(mfs, '/mountpoint', { mode, syncHandleAllowed: true }); const fs = new FsaNodeFs_1.FsaNodeFs(dir); return { fs, mfs, vol, dir }; }; (0, util_1.onlyOnNode20)('FsaNodeFs', () => { describe('.mkdir()', () => { test('can create a sub-folder', async () => { const { fs, mfs } = setup(); await new Promise((resolve, reject) => fs.mkdir('/test', err => { if (err) return reject(err); return resolve(); })); expect(mfs.statSync('/mountpoint/test').isDirectory()).toBe(true); }); test('can create a sub-folder with trailing slash', async () => { const { fs, mfs } = setup(); await new Promise((resolve, reject) => fs.mkdir('/test/', err => { if (err) return reject(err); return resolve(); })); expect(mfs.statSync('/mountpoint/test').isDirectory()).toBe(true); }); test('throws when creating sub-sub-folder', async () => { const { fs } = setup(); try { await new Promise((resolve, reject) => fs.mkdir('/test/subtest', err => { if (err) return reject(err); return resolve(); })); throw new Error('Expected error'); } catch (error) { expect(error.code).toBe('ENOENT'); } }); test('can create sub-sub-folder with "recursive" flag', async () => { const { fs, mfs } = setup(); await new Promise((resolve, reject) => fs.mkdir('/test/subtest', { recursive: true }, err => { if (err) return reject(err); return resolve(); })); expect(mfs.statSync('/mountpoint/test/subtest').isDirectory()).toBe(true); }); test('can create sub-sub-folder with "recursive" flag with Promises API', async () => { const { fs, mfs } = setup(); await fs.promises.mkdir('/test/subtest', { recursive: true }); expect(mfs.statSync('/mountpoint/test/subtest').isDirectory()).toBe(true); }); test('cannot create a folder over a file', async () => { const { fs } = setup({ file: 'test' }); try { await fs.promises.mkdir('/file/folder', { recursive: true }); throw new Error('Expected error'); } catch (error) { expect(error.code).toBe('ENOTDIR'); } }); }); describe('.mkdtemp()', () => { test('can create a temporary folder', async () => { const { fs, mfs } = setup(); const dirname = (await fs.promises.mkdtemp('prefix--')); expect(dirname.startsWith('prefix--')).toBe(true); expect(mfs.statSync('/mountpoint/' + dirname).isDirectory()).toBe(true); }); }); describe('.rmdir()', () => { test('can remove an empty folder', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null }); await fs.promises.rmdir('/empty-folder'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test' }); }); test('throws when attempts to remove non-empty folder', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null }); try { await fs.promises.rmdir('/folder'); throw new Error('Expected error'); } catch (error) { expect(error.code).toBe('ENOTEMPTY'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/empty-folder': null, }); } }); test('can remove non-empty directory recursively', async () => { const { fs, vol } = setup({ folder: { subfolder: { file: 'test' } }, 'empty-folder': null }); await fs.promises.rmdir('/folder', { recursive: true }); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/empty-folder': null, }); }); test('can remove starting from root folder', async () => { const { fs, vol } = setup({ folder: { subfolder: { file: 'test' } }, 'empty-folder': null }); await fs.promises.rmdir('/', { recursive: true }); expect(vol.toJSON()).toStrictEqual({ '/mountpoint': null, }); }); }); describe('.rm()', () => { test('can remove an empty folder', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null }); await fs.promises.rm('/empty-folder'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test' }); }); test('throws when attempts to remove non-empty folder', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null }); try { await fs.promises.rm('/folder'); throw new Error('Expected error'); } catch (error) { expect(error.code).toBe('ENOTEMPTY'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/empty-folder': null, }); } }); test('can remove non-empty directory recursively', async () => { const { fs, vol } = setup({ folder: { subfolder: { file: 'test' } }, 'empty-folder': null }); await fs.promises.rm('/folder', { recursive: true }); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/empty-folder': null, }); }); test('throws if path does not exist', async () => { const { fs, vol } = setup({ folder: { subfolder: { file: 'test' } }, 'empty-folder': null }); try { await fs.promises.rm('/lala/lulu', { recursive: true }); throw new Error('Expected error'); } catch (error) { expect(error.code).toBe('ENOENT'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/subfolder/file': 'test', '/mountpoint/empty-folder': null, }); } }); test('does not throw, if path does not exist, but "force" flag set', async () => { const { fs, vol } = setup({ folder: { subfolder: { file: 'test' } }, 'empty-folder': null }); await fs.promises.rm('/lala/lulu', { recursive: true, force: true }); }); test('can remove a file', async () => { const { fs, vol } = setup({ folder: { subfolder: { file: 'test' } }, 'empty-folder': null }); await fs.promises.rm('/folder/subfolder/file'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/subfolder': null, '/mountpoint/empty-folder': null, }); }); test('can remove starting from root folder', async () => { const { fs, vol } = setup({ folder: { subfolder: { file: 'test' } }, 'empty-folder': null }); await fs.promises.rm('/', { recursive: true }); expect(vol.toJSON()).toStrictEqual({ '/mountpoint': null, }); }); }); describe('.unlink()', () => { test('can remove a file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null }); const res = await fs.promises.unlink('/folder/file'); expect(res).toBe(undefined); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder': null, '/mountpoint/empty-folder': null, }); }); test('cannot delete a folder', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null }); try { await fs.promises.unlink('/folder'); throw new Error('Expected error'); } catch (error) { expect(error.code).toBe('EISDIR'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/empty-folder': null, }); } }); test('throws when deleting non-existing file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null }); try { await fs.promises.unlink('/folder/not-a-file'); throw new Error('Expected error'); } catch (error) { expect(error.code).toBe('ENOENT'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/empty-folder': null, }); } }); }); describe('.readFile()', () => { test('can read file contents', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null }); const data = await fs.promises.readFile('/folder/file'); expect(data.toString()).toBe('test'); }); test('can read file by file handle', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null }); const handle = await fs.promises.open('/folder/file'); expect(typeof handle).toBe('object'); const data = await fs.promises.readFile(handle); expect(data.toString()).toBe('test'); }); test('can read file by file descriptor', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null }); const fd = await new Promise(resolve => { fs.open('/folder/file', 'r', (err, fd) => resolve(fd)); }); expect(typeof fd).toBe('number'); const data = await new Promise(resolve => { fs.readFile(fd, { encoding: 'utf8' }, (err, data) => resolve(data)); }); expect(data).toBe('test'); }); test('cannot read from closed file descriptor', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null }); const fd = await new Promise(resolve => { fs.open('/folder/file', 'r', (err, fd) => resolve(fd)); }); expect(typeof fd).toBe('number'); await new Promise(resolve => { fs.close(fd, () => resolve()); }); try { await new Promise((resolve, reject) => { fs.readFile(fd, { encoding: 'utf8' }, (err, data) => reject(err)); }); throw new Error('Expected error'); } catch (error) { expect(error).toBeInstanceOf(Error); expect(error.code).toBe('EBADF'); } }); }); describe('.truncate()', () => { test('can truncate a file', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null }); const res = await new Promise((resolve, reject) => { fs.truncate('/folder/file', 2, (err, res) => (err ? reject(err) : resolve(res))); }); expect(res).toBe(undefined); expect(mfs.readFileSync('/mountpoint/folder/file', 'utf8')).toBe('te'); }); }); describe('.ftruncate()', () => { test('can truncate a file', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null }); const handle = await fs.promises.open('/folder/file'); const res = await new Promise((resolve, reject) => { fs.ftruncate(handle.fd, 3, (err, res) => (err ? reject(err) : resolve(res))); }); expect(res).toBe(undefined); expect(mfs.readFileSync('/mountpoint/folder/file', 'utf8')).toBe('tes'); }); }); describe('.readdir()', () => { test('can read directory contents as strings', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const res = (await fs.promises.readdir('/')); expect(res.length).toBe(3); expect(res.includes('folder')).toBe(true); expect(res.includes('empty-folder')).toBe(true); expect(res.includes('f.html')).toBe(true); }); test('can read directory contents with "withFileTypes" flag set', async () => { var _a, _b, _c, _d; const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const list = (await fs.promises.readdir('/', { withFileTypes: true })); expect(list.length).toBe(3); const names = list.map(item => item.name); expect(names).toStrictEqual(['empty-folder', 'f.html', 'folder']); expect((_a = list.find(item => item.name === 'folder')) === null || _a === void 0 ? void 0 : _a.isDirectory()).toBe(true); expect((_b = list.find(item => item.name === 'empty-folder')) === null || _b === void 0 ? void 0 : _b.isDirectory()).toBe(true); expect((_c = list.find(item => item.name === 'f.html')) === null || _c === void 0 ? void 0 : _c.isFile()).toBe(true); expect((_d = list.find(item => item.name === 'f.html')) === null || _d === void 0 ? void 0 : _d.isDirectory()).toBe(false); }); }); describe('.appendFile()', () => { test('can create a file', async () => { const { fs, mfs } = setup({}); await fs.promises.appendFile('/test.txt', 'a'); expect(mfs.readFileSync('/mountpoint/test.txt', 'utf8')).toBe('a'); }); test('can append to a file', async () => { const { fs, mfs } = setup({}); await fs.promises.appendFile('/test.txt', 'a'); await fs.promises.appendFile('/test.txt', 'b'); expect(mfs.readFileSync('/mountpoint/test.txt', 'utf8')).toBe('ab'); }); test('can append to a file - 2', async () => { const { fs, mfs } = setup({ file: '123' }); await fs.promises.appendFile('file', 'x'); expect(mfs.readFileSync('/mountpoint/file', 'utf8')).toBe('123x'); }); test('can append to a file - 2', async () => { const { fs, mfs } = setup({ file: '123' }); await fs.promises.writeFile('cool.txt', 'worlds'); await fs.promises.appendFile('cool.txt', '!'); expect(mfs.readFileSync('/mountpoint/cool.txt', 'utf8')).toBe('worlds!'); }); }); describe('.write()', () => { test('can write to a file', async () => { const { fs, mfs } = setup({}); const fd = await new Promise((resolve, reject) => fs.open('/test.txt', 'w', (err, fd) => { if (err) reject(err); else resolve(fd); })); const [bytesWritten, data] = await new Promise((resolve, reject) => { fs.write(fd, 'a', (err, bytesWritten, data) => { if (err) reject(err); else resolve([bytesWritten, data]); }); }); expect(bytesWritten).toBe(1); expect(data).toBe('a'); expect(mfs.readFileSync('/mountpoint/test.txt', 'utf8')).toBe('a'); }); test('can write to a file twice sequentially', async () => { const { fs, mfs } = setup({}); const fd = await new Promise((resolve, reject) => fs.open('/test.txt', 'w', (err, fd) => { if (err) reject(err); else resolve(fd); })); const res1 = await new Promise((resolve, reject) => { fs.write(fd, 'a', (err, bytesWritten, data) => { if (err) reject(err); else resolve([bytesWritten, data]); }); }); expect(res1[0]).toBe(1); expect(res1[1]).toBe('a'); const res2 = await new Promise((resolve, reject) => { fs.write(fd, 'bc', (err, bytesWritten, data) => { if (err) reject(err); else resolve([bytesWritten, data]); }); }); expect(res2[0]).toBe(2); expect(res2[1]).toBe('bc'); expect(mfs.readFileSync('/mountpoint/test.txt', 'utf8')).toBe('abc'); }); }); describe('.writev()', () => { test('can write to a file two buffers', async () => { const { fs, mfs } = setup({}); const fd = await new Promise((resolve, reject) => fs.open('/test.txt', 'w', (err, fd) => { if (err) reject(err); else resolve(fd); })); const [bytesWritten, data] = await new Promise((resolve, reject) => { const buffers = [Buffer.from('a'), Buffer.from('b')]; fs.writev(fd, buffers, (err, bytesWritten, data) => { if (err) reject(err); else resolve([bytesWritten, data]); }); }); expect(bytesWritten).toBe(2); expect(mfs.readFileSync('/mountpoint/test.txt', 'utf8')).toBe('ab'); }); }); describe('.exists()', () => { test('can works for folders and files', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const exists = async (path) => { return new Promise(resolve => { fs.exists(path, exists => resolve(exists)); }); }; expect(await exists('/folder')).toBe(true); expect(await exists('/folder/file')).toBe(true); expect(await exists('/folder/not-a-file')).toBe(false); expect(await exists('/f.html')).toBe(true); expect(await exists('/empty-folder')).toBe(true); expect(await exists('/')).toBe(true); expect(await exists('/asdf')).toBe(false); expect(await exists('asdf')).toBe(false); }); }); describe('.access()', () => { describe('files', () => { test('succeeds on file existence check', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.access('/folder/file', 0 /* AMODE.F_OK */); }); test('succeeds on file "read" check', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.access('/folder/file', 4 /* AMODE.R_OK */); }); test('succeeds on file "write" check, on writable file system', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.access('/folder/file', 2 /* AMODE.W_OK */); }); test('fails on file "write" check, on read-only file system', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }, 'read'); try { await fs.promises.access('/folder/file', 2 /* AMODE.W_OK */); throw new Error('should not be here'); } catch (error) { expect(error.code).toBe('EACCESS'); } }); test('fails on file "execute" check', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); try { await fs.promises.access('/folder/file', 1 /* AMODE.X_OK */); throw new Error('should not be here'); } catch (error) { expect(error.code).toBe('EACCESS'); } }); }); describe('directories', () => { test('succeeds on folder existence check', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.access('/folder', 0 /* AMODE.F_OK */); }); test('succeeds on folder "read" check', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.access('/folder', 4 /* AMODE.R_OK */); }); test('succeeds on folder "write" check, on writable file system', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.access('/folder', 2 /* AMODE.W_OK */); }); test('fails on folder "write" check, on read-only file system', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }, 'read'); try { await fs.promises.access('/folder', 2 /* AMODE.W_OK */); throw new Error('should not be here'); } catch (error) { expect(error.code).toBe('EACCESS'); } }); test('fails on folder "execute" check', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); try { await fs.promises.access('/folder', 1 /* AMODE.X_OK */); throw new Error('should not be here'); } catch (error) { expect(error.code).toBe('EACCESS'); } }); }); }); describe('.rename()', () => { test('can rename a file', async () => { const { fs, mfs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.rename('/folder/file', '/folder/file2'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file2': 'test', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); }); describe('.stat()', () => { test('can stat a file', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stats = await fs.promises.stat('/folder/file'); expect(stats.isFile()).toBe(true); }); test('throws "ENOENT" when path is not found', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const [, error] = await (0, thingies_1.of)(fs.promises.stat('/folder/repo/.git')); expect(error.code).toBe('ENOENT'); }); test('throws "ENOTDIR" when sub-folder is a file', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const [, error] = await (0, thingies_1.of)(fs.promises.stat('/folder/file/repo/.git')); expect(error.code).toBe('ENOTDIR'); }); test('can retrieve file size', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stats = await fs.promises.stat('/folder/file'); expect(stats.size).toBe(4); }); test('can stat a folder', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stats = await fs.promises.stat('/folder'); expect(stats.isFile()).toBe(false); expect(stats.isDirectory()).toBe(true); }); test('throws on non-existing path', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); try { const stats = await fs.promises.stat('/folder/abc'); throw new Error('should not be here'); } catch (error) { expect(error.code).toBe('ENOENT'); } }); }); describe('.lstat()', () => { test('can stat a file', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stats = await fs.promises.lstat('/folder/file'); expect(stats.isFile()).toBe(true); }); test('can retrieve file size', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stats = await fs.promises.lstat('/folder/file'); expect(stats.size).toBe(4); }); test('can stat a folder', async () => { const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stats = await fs.promises.lstat('/folder'); expect(stats.isFile()).toBe(false); expect(stats.isDirectory()).toBe(true); }); test('throws on non-existing path', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); try { await fs.promises.lstat('/folder/abc'); throw new Error('should not be here'); } catch (error) { expect(error.code).toBe('ENOENT'); } }); }); describe('.fstat()', () => { test('can stat a file', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const handle = await fs.promises.open('/folder/file', 'r'); const stats = await new Promise((resolve, reject) => { fs.fstat(handle.fd, (error, stats) => { if (error) reject(error); else resolve(stats); }); }); expect(stats.isFile()).toBe(true); }); }); describe('.realpath()', () => { test('returns file path', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const path = await fs.promises.realpath('folder/file'); expect(path).toBe('/folder/file'); }); }); describe('.realpathSync()', () => { test('returns file path', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const path = fs.realpathSync('folder/file'); expect(path).toBe('/folder/file'); }); }); describe('.copyFile()', () => { test('can copy a file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); await fs.promises.copyFile('/folder/file', '/folder/file2'); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/folder/file2': 'test', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); }); describe('.writeFile()', () => { test('can create a new file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const res = await new Promise((resolve, reject) => { fs.writeFile('/folder/foo', 'bar', error => { if (error) reject(error); else resolve(); }); }); expect(res).toBe(undefined); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/folder/foo': 'bar', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('throws "EEXIST", if file already exists and O_EXCL flag set', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const [, err] = await (0, thingies_1.of)(fs.promises.writeFile('/folder/file', 'bar', { flag: 'wx' })); expect(err).toBeInstanceOf(Error); expect(err.code).toBe('EEXIST'); }); test('throws "ENOENT", if file does not exist and O_CREAT flag not set', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const [, err] = await (0, thingies_1.of)(fs.promises.writeFile('/folder/file2', 'bar', { flag: 2 /* FLAG.O_RDWR */ })); expect(err).toBeInstanceOf(Error); expect(err.code).toBe('ENOENT'); }); }); describe('.read()', () => { test('can read from a file at offset into Buffer', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const handle = await fs.promises.open('/folder/file', 'r'); const [buffer, length] = await new Promise((resolve, reject) => { const buffer = Buffer.alloc(4); fs.read(handle.fd, buffer, 0, 2, 1, (error, bytesRead, buffer) => { if (error) reject(error); else resolve([buffer, bytesRead]); }); }); expect(length).toBe(2); expect(buffer.slice(0, 2).toString()).toBe('es'); }); test('can read from a file at offset into Uint8Array', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const handle = await fs.promises.open('/folder/file', 'r'); const [buffer, length] = await new Promise((resolve, reject) => { const buffer = new Uint8Array(4); fs.read(handle.fd, buffer, 0, 2, 1, (error, bytesRead, buffer) => { if (error) reject(error); else resolve([buffer, bytesRead]); }); }); expect(length).toBe(2); expect(buffer[0]).toBe(101); expect(buffer[1]).toBe(115); }); }); describe('.createWriteStream()', () => { test('can use stream to write to a new file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stream = fs.createWriteStream('/folder/file2'); stream.write(Buffer.from('A')); stream.write(Buffer.from('BC')); stream.write(Buffer.from('DEF')); stream.end(); await new Promise(resolve => stream.once('close', resolve)); expect(stream.bytesWritten).toBe(6); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/folder/file2': 'ABCDEF', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('can use stream to write to a new file using strings', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stream = fs.createWriteStream('/folder/file2'); stream.write('A'); stream.write('BC'); stream.write('DEF'); stream.end(); await new Promise(resolve => stream.once('close', resolve)); expect(stream.bytesWritten).toBe(6); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/folder/file2': 'ABCDEF', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('can use stream to overwrite existing file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stream = fs.createWriteStream('/folder/file'); stream.write(Buffer.from('A')); stream.write(Buffer.from('BC')); stream.end(); await new Promise(resolve => stream.once('close', resolve)); expect(stream.bytesWritten).toBe(3); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'ABC', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('can write by file descriptor', async () => { const { fs, mfs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const handle = await fs.promises.open('/folder/file', 'a'); const stream = fs.createWriteStream('', { fd: handle.fd, start: 1, flags: 'a' }); stream.write(Buffer.from('BC')); stream.end(); await new Promise(resolve => stream.once('close', resolve)); expect(stream.bytesWritten).toBe(2); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'tBCt', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('closes file once stream ends', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const handle = await fs.promises.open('/folder/file', 'a'); const stream = fs.createWriteStream('', { fd: handle.fd, start: 1, flags: 'a' }); stream.write(Buffer.from('BC')); const stat = async () => await new Promise((resolve, reject) => fs.fstat(handle.fd, (err, stats) => { if (err) reject(err); else resolve(stats); })); await stat(); stream.end(); await (0, thingies_1.until)(async () => { const [, error] = await (0, thingies_1.of)(stat()); return !!error; }); const [, error] = await (0, thingies_1.of)(stat()); expect(error.code).toBe('EBADF'); }); test('does not close file if "autoClose" is false', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const handle = await fs.promises.open('/folder/file', 'a'); const stream = fs.createWriteStream('', { fd: handle.fd, start: 1, flags: 'a', autoClose: false }); stream.write(Buffer.from('BC')); const stat = async () => await new Promise((resolve, reject) => fs.fstat(handle.fd, (err, stats) => { if (err) reject(err); else resolve(stats); })); await stat(); stream.end(); await (0, thingies_1.tick)(200); await stat(); }); test('can use stream to add to existing file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stream = fs.createWriteStream('/folder/file', { flags: 'a' }); stream.write(Buffer.from('A')); stream.write(Buffer.from('BC')); stream.end(); await new Promise(resolve => stream.once('close', resolve)); expect(stream.bytesWritten).toBe(3); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'ABCt', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('can use stream to add to existing file at specified offset', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const stream = fs.createWriteStream('/folder/file', { flags: 'a', start: 1 }); stream.write(Buffer.from('A')); stream.write(Buffer.from('B')); stream.end(); await new Promise(resolve => stream.once('close', resolve)); expect(stream.bytesWritten).toBe(2); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'tABt', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('throws if "start" option is not a number', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); try { fs.createWriteStream('/folder/file', { flags: 'a', start: '1' }); throw new Error('should have thrown'); } catch (error) { expect(error).toBeInstanceOf(TypeError); expect(error.message).toBe('"start" option must be a Number'); } }); test('throws if "start" option is negative', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); try { fs.createWriteStream('/folder/file', { flags: 'a', start: -1 }); throw new Error('should have thrown'); } catch (error) { expect(error).toBeInstanceOf(TypeError); expect(error.message).toBe('"start" must be >= zero'); } }); }); describe('.createReadStream()', () => { test('can pipe fs.ReadStream to fs.WriteStream', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const readStream = fs.createReadStream('/folder/file'); const writeStream = fs.createWriteStream('/folder/file2'); readStream.pipe(writeStream); await new Promise(resolve => writeStream.once('close', resolve)); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/folder/file2': 'test', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('emits "open" event', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const readStream = fs.createReadStream('/folder/file'); const fd = await new Promise(resolve => readStream.once('open', resolve)); expect(typeof fd).toBe('number'); }); test('emits "ready" event', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const readStream = fs.createReadStream('/folder/file'); await new Promise(resolve => readStream.once('ready', resolve)); }); test('emits "close" event', async () => { const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const readStream = fs.createReadStream('/folder/file', { emitClose: true }); const writeStream = fs.createWriteStream('/folder/file2'); readStream.pipe(writeStream); await new Promise(resolve => readStream.once('close', resolve)); }); test('can write to already open file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const handle = await fs.promises.open('/folder/file'); const readStream = fs.createReadStream('xyz', { fd: handle.fd }); const writeStream = fs.createWriteStream('/folder/file2'); readStream.pipe(writeStream); await new Promise(resolve => writeStream.once('close', resolve)); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/folder/file2': 'test', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); test('can read a specified slice of a file', async () => { const { fs, vol } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); const readStream = fs.createReadStream('/folder/file', { start: 1, end: 2 }); const writeStream = fs.createWriteStream('/folder/file2'); readStream.pipe(writeStream); await new Promise(resolve => writeStream.once('close', resolve)); expect(vol.toJSON()).toStrictEqual({ '/mountpoint/folder/file': 'test', '/mountpoint/folder/file2': 'es', '/mountpoint/empty-folder': null, '/mountpoint/f.html': 'test', }); }); }); }); //# sourceMappingURL=FsaNodeFs.test.js.map