Files
node/test/parallel/test-vfs-memory-provider-dynamic.js
Matteo Collina 49ad95045a vfs: unflag
Enable node:vfs by default. --no-experimental-vfs disables node:vfs
exposure and --vfs-load.

Assisted-by: pi
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66318
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
2026-10-15 07:53:03 +00:00

152 lines
5.0 KiB
JavaScript

// Flags: --disable-warning=ExperimentalWarning --expose-internals
'use strict';
// MemoryProvider supports dynamic content providers and lazily-populated
// directories internally. These features have no public construction API,
// so we drive them directly through MemoryEntry / MemoryProvider.
const common = require('../common');
const assert = require('assert');
const vfs = require('node:vfs');
const { MemoryProvider } = require('internal/vfs/providers/memory');
function getRoot(provider) {
const symbols = Object.getOwnPropertySymbols(provider);
const kRoot = symbols.find((s) => s.description === 'kRoot');
assert.ok(kRoot, 'kRoot symbol expected on MemoryProvider');
return provider[kRoot];
}
function makeFileEntry(prototypeFrom, contentProvider) {
const t = Date.now();
const fileEntry = { __proto__: Object.getPrototypeOf(prototypeFrom) };
Object.assign(fileEntry, {
type: 0, // TYPE_FILE
mode: 0o644,
content: Buffer.alloc(0),
contentProvider,
children: null,
target: null,
populate: null,
populated: true,
nlink: 1,
uid: 0,
gid: 0,
atime: t,
mtime: t,
ctime: t,
birthtime: t,
});
fileEntry.isFile = prototypeFrom.isFile.bind(fileEntry);
fileEntry.isDirectory = prototypeFrom.isDirectory.bind(fileEntry);
fileEntry.isSymbolicLink = prototypeFrom.isSymbolicLink.bind(fileEntry);
fileEntry.isDynamic = prototypeFrom.isDynamic.bind(fileEntry);
fileEntry.getContentSync = prototypeFrom.getContentSync.bind(fileEntry);
fileEntry.getContentAsync = prototypeFrom.getContentAsync.bind(fileEntry);
return fileEntry;
}
function makeDirEntry(prototypeFrom, populate) {
const t = Date.now();
const dirEntry = { __proto__: Object.getPrototypeOf(prototypeFrom) };
Object.assign(dirEntry, {
type: 1, // TYPE_DIR
mode: 0o755,
children: new Map(),
populate,
populated: false,
nlink: 1,
uid: 0,
gid: 0,
atime: t,
mtime: t,
ctime: t,
birthtime: t,
});
dirEntry.isFile = prototypeFrom.isFile.bind(dirEntry);
dirEntry.isDirectory = prototypeFrom.isDirectory.bind(dirEntry);
dirEntry.isSymbolicLink = prototypeFrom.isSymbolicLink.bind(dirEntry);
dirEntry.isDynamic = prototypeFrom.isDynamic.bind(dirEntry);
dirEntry.getContentSync = prototypeFrom.getContentSync.bind(dirEntry);
dirEntry.getContentAsync = prototypeFrom.getContentAsync.bind(dirEntry);
return dirEntry;
}
// ===== Lazy-populated directory =====
{
const provider = new MemoryProvider();
const root = getRoot(provider);
const dir = makeDirEntry(root, (scoped) => {
scoped.addFile('hello.txt', 'lazy hello');
scoped.addFile('dyn.txt', () => 'dynamic-string');
scoped.addDirectory('subdir', null);
scoped.addSymlink('link.txt', '/lazy/hello.txt');
});
root.children.set('lazy', dir);
const myVfs = vfs.create(provider);
// Reading the lazy directory triggers populate
const entries = myVfs.readdirSync('/lazy');
assert.deepStrictEqual(entries.sort(),
['dyn.txt', 'hello.txt', 'link.txt', 'subdir']);
// Static file in the lazy directory
assert.strictEqual(myVfs.readFileSync('/lazy/hello.txt', 'utf8'),
'lazy hello');
// Dynamic content provider returning a string (sync read)
assert.strictEqual(myVfs.readFileSync('/lazy/dyn.txt', 'utf8'),
'dynamic-string');
// Dynamic content provider via promises.readFile
myVfs.promises.readFile('/lazy/dyn.txt', 'utf8').then(common.mustCall((s) => {
assert.strictEqual(s, 'dynamic-string');
}));
}
// ===== Dynamic content provider returning a Buffer =====
{
const provider = new MemoryProvider();
const root = getRoot(provider);
root.children.set('buf-dyn.txt',
makeFileEntry(root, () => Buffer.from('buffer-content')));
const myVfs = vfs.create(provider);
assert.strictEqual(myVfs.readFileSync('/buf-dyn.txt', 'utf8'),
'buffer-content');
}
// ===== Async-only content provider: sync API throws ERR_INVALID_STATE =====
{
const provider = new MemoryProvider();
const root = getRoot(provider);
root.children.set('async-only.txt',
makeFileEntry(root, async () => 'async-only'));
const myVfs = vfs.create(provider);
assert.throws(() => myVfs.readFileSync('/async-only.txt'),
{ code: 'ERR_INVALID_STATE' });
myVfs.promises.readFile('/async-only.txt', 'utf8').then(common.mustCall((s) => {
assert.strictEqual(s, 'async-only');
}));
}
// ===== Renaming over a lazy directory does not discard its entries =====
{
const provider = new MemoryProvider();
const root = getRoot(provider);
root.children.set('lazy', makeDirEntry(root, (scoped) => {
scoped.addFile('keep.txt', 'keep');
}));
const myVfs = vfs.create(provider);
myVfs.mkdirSync('/src');
assert.throws(() => myVfs.renameSync('/src', '/lazy'), { code: 'ENOTEMPTY' });
assert.strictEqual(myVfs.existsSync('/src'), true);
assert.strictEqual(myVfs.readFileSync('/lazy/keep.txt', 'utf8'), 'keep');
}