Skip to content

Commit 0ec5dde

Browse files
committed
test: add VFS internals coverage tests
Add tests for internal VFS utility functions and provider base class to improve code coverage: - router.js: splitPath, getParentPath, getBaseName - provider.js: readonly EROFS checks for all write operations - provider.js: ERR_METHOD_NOT_IMPLEMENTED for unimplemented methods PR-URL: #61478
1 parent 306b825 commit 0ec5dde

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
4+
const common = require('../common');
5+
const assert = require('assert');
6+
7+
// Test internal VFS utility functions for coverage.
8+
9+
// === Router utility functions ===
10+
const {
11+
splitPath,
12+
getParentPath,
13+
getBaseName,
14+
isUnderMountPoint,
15+
getRelativePath,
16+
isAbsolutePath,
17+
} = require('internal/vfs/router');
18+
19+
// splitPath
20+
assert.deepStrictEqual(splitPath('/'), []);
21+
assert.deepStrictEqual(splitPath('/foo'), ['foo']);
22+
assert.deepStrictEqual(splitPath('/foo/bar'), ['foo', 'bar']);
23+
assert.deepStrictEqual(splitPath('/a/b/c/d'), ['a', 'b', 'c', 'd']);
24+
25+
// getParentPath
26+
assert.strictEqual(getParentPath('/'), null);
27+
assert.strictEqual(getParentPath('/foo'), '/');
28+
assert.strictEqual(getParentPath('/foo/bar'), '/foo');
29+
assert.strictEqual(getParentPath('/a/b/c'), '/a/b');
30+
31+
// getBaseName
32+
assert.strictEqual(getBaseName('/foo'), 'foo');
33+
assert.strictEqual(getBaseName('/foo/bar'), 'bar');
34+
assert.strictEqual(getBaseName('/a/b/c.txt'), 'c.txt');
35+
36+
// isAbsolutePath
37+
assert.strictEqual(isAbsolutePath('/foo'), true);
38+
assert.strictEqual(isAbsolutePath('foo'), false);
39+
assert.strictEqual(isAbsolutePath('./foo'), false);
40+
41+
// isUnderMountPoint (already tested indirectly but exercised here directly)
42+
assert.strictEqual(isUnderMountPoint('/mount', '/mount'), true);
43+
assert.strictEqual(isUnderMountPoint('/mount/file', '/mount'), true);
44+
assert.strictEqual(isUnderMountPoint('/mountx', '/mount'), false);
45+
assert.strictEqual(isUnderMountPoint('/other', '/mount'), false);
46+
// Root mount point
47+
assert.strictEqual(isUnderMountPoint('/anything', '/'), true);
48+
49+
// getRelativePath
50+
assert.strictEqual(getRelativePath('/mount', '/mount'), '/');
51+
assert.strictEqual(getRelativePath('/mount/file.js', '/mount'), '/file.js');
52+
assert.strictEqual(getRelativePath('/mount/a/b', '/mount'), '/a/b');
53+
// Root mount point
54+
assert.strictEqual(getRelativePath('/foo/bar', '/'), '/foo/bar');
55+
56+
// === Provider base class readonly checks ===
57+
const { VirtualProvider } = require('internal/vfs/provider');
58+
59+
class ReadonlyProvider extends VirtualProvider {
60+
get readonly() { return true; }
61+
}
62+
63+
const readonlyProvider = new ReadonlyProvider();
64+
65+
// All write operations should throw EROFS when readonly
66+
assert.throws(() => readonlyProvider.mkdirSync('/dir'), { code: 'EROFS' });
67+
assert.throws(() => readonlyProvider.rmdirSync('/dir'), { code: 'EROFS' });
68+
assert.throws(() => readonlyProvider.unlinkSync('/file'), { code: 'EROFS' });
69+
assert.throws(() => readonlyProvider.renameSync('/a', '/b'), { code: 'EROFS' });
70+
assert.throws(() => readonlyProvider.writeFileSync('/f', 'data'), { code: 'EROFS' });
71+
assert.throws(() => readonlyProvider.appendFileSync('/f', 'data'), { code: 'EROFS' });
72+
assert.throws(() => readonlyProvider.copyFileSync('/a', '/b'), { code: 'EROFS' });
73+
assert.throws(() => readonlyProvider.symlinkSync('/target', '/link'), { code: 'EROFS' });
74+
75+
// Async versions
76+
assert.rejects(readonlyProvider.mkdir('/dir'), { code: 'EROFS' }).then(common.mustCall());
77+
assert.rejects(readonlyProvider.rmdir('/dir'), { code: 'EROFS' }).then(common.mustCall());
78+
assert.rejects(readonlyProvider.unlink('/file'), { code: 'EROFS' }).then(common.mustCall());
79+
assert.rejects(readonlyProvider.rename('/a', '/b'), { code: 'EROFS' }).then(common.mustCall());
80+
assert.rejects(readonlyProvider.writeFile('/f', 'data'), { code: 'EROFS' }).then(common.mustCall());
81+
assert.rejects(readonlyProvider.appendFile('/f', 'data'), { code: 'EROFS' }).then(common.mustCall());
82+
assert.rejects(readonlyProvider.copyFile('/a', '/b'), { code: 'EROFS' }).then(common.mustCall());
83+
assert.rejects(readonlyProvider.symlink('/target', '/link'), { code: 'EROFS' }).then(common.mustCall());
84+
85+
// === Provider base class ERR_METHOD_NOT_IMPLEMENTED for non-readonly ===
86+
const baseProvider = new VirtualProvider();
87+
88+
// These should throw ERR_METHOD_NOT_IMPLEMENTED (not readonly, just unimplemented)
89+
assert.throws(() => baseProvider.mkdirSync('/dir'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
90+
assert.throws(() => baseProvider.rmdirSync('/dir'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
91+
assert.throws(() => baseProvider.unlinkSync('/file'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
92+
assert.throws(() => baseProvider.renameSync('/a', '/b'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
93+
assert.throws(() => baseProvider.symlinkSync('/t', '/l'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
94+
assert.throws(() => baseProvider.readdirSync('/dir'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
95+
assert.throws(() => baseProvider.readlinkSync('/link'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
96+
assert.throws(() => baseProvider.watch('/path'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
97+
assert.throws(() => baseProvider.watchAsync('/path'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
98+
assert.throws(() => baseProvider.watchFile('/path'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
99+
assert.throws(() => baseProvider.unwatchFile('/path'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' });
100+
101+
// Async unimplemented methods
102+
assert.rejects(baseProvider.mkdir('/dir'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' }).then(common.mustCall());
103+
assert.rejects(baseProvider.rmdir('/dir'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' }).then(common.mustCall());
104+
assert.rejects(baseProvider.unlink('/file'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' }).then(common.mustCall());
105+
assert.rejects(baseProvider.rename('/a', '/b'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' }).then(common.mustCall());
106+
assert.rejects(baseProvider.readlink('/link'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' }).then(common.mustCall());
107+
assert.rejects(baseProvider.symlink('/t', '/l'), { code: 'ERR_METHOD_NOT_IMPLEMENTED' }).then(common.mustCall());
108+
109+
// === Provider default implementations via VFS (covers realpath, access, exists, etc.) ===
110+
const vfs = require('node:vfs');
111+
112+
{
113+
const myVfs = vfs.create();
114+
myVfs.writeFileSync('/file.txt', 'hello');
115+
myVfs.mkdirSync('/dir');
116+
myVfs.mount('/internals-test');
117+
118+
// realpath (default provider impl returns path as-is after stat check)
119+
assert.strictEqual(myVfs.realpathSync('/internals-test/file.txt'), '/internals-test/file.txt');
120+
assert.strictEqual(myVfs.realpathSync('/internals-test/dir'), '/internals-test/dir');
121+
122+
// realpath for non-existent path should throw
123+
assert.throws(() => myVfs.realpathSync('/internals-test/nonexistent'), { code: 'ENOENT' });
124+
125+
// access (default provider impl just checks stat)
126+
myVfs.accessSync('/internals-test/file.txt');
127+
assert.throws(() => myVfs.accessSync('/internals-test/nonexistent'), { code: 'ENOENT' });
128+
129+
// existsSync
130+
assert.strictEqual(myVfs.existsSync('/internals-test/file.txt'), true);
131+
assert.strictEqual(myVfs.existsSync('/internals-test/nonexistent'), false);
132+
assert.strictEqual(myVfs.existsSync('/internals-test/dir'), true);
133+
134+
// internalModuleStat (0 = file, 1 = dir, -2 = not found)
135+
assert.strictEqual(myVfs.internalModuleStat('/internals-test/file.txt'), 0);
136+
assert.strictEqual(myVfs.internalModuleStat('/internals-test/dir'), 1);
137+
assert.strictEqual(myVfs.internalModuleStat('/internals-test/nope'), -2);
138+
139+
// Callback-based realpath (returns mounted path)
140+
myVfs.realpath('/internals-test/file.txt', common.mustCall((err, resolved) => {
141+
assert.ifError(err);
142+
assert.ok(resolved);
143+
}));
144+
145+
// Callback-based access
146+
myVfs.access('/internals-test/file.txt', common.mustCall((err) => {
147+
assert.ifError(err);
148+
}));
149+
150+
myVfs.unmount();
151+
}

0 commit comments

Comments
 (0)