aboutsummaryrefslogtreecommitdiff
path: root/modules/test
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-27 12:32:37 +0100
committerAgando <roduccomm@gmail.com>2026-10-27 12:32:37 +0100
commit669b66591b3efbb8fc8642d25a67c55d93cfe4e9 (patch)
tree032b044c840b0e3e5113f7ea0d06684cd5d1fa83 /modules/test
parentd2de622744212ea0ba720b3f4212b82940ffe84b (diff)
fix(newos4): never overwrite a directory node; ls honours a path after its flags
MAINT-010 and MAINT-016, the two high items the review left open. Both were re-probed before the change and the new test is red on HEAD (5 passed, 7 FAILED). - virtual-fs.js rename()/copy(): they assigned dst.dir.content[dst.name] with no destination type check, so `mv file /docs` replaced the /docs node and every child vanished while the command printed "Moved: …". Refuse when the destination is a real directory node (the virtual /sys entry is exempt). rename() also erased the file on `mv a a`, because assigning and then deleting the same key removes it; that path is now an explicit no-op. - module-fs.js mv/cp: a refusal used to surface as "No such file or directory", which sent the operator looking for a missing source. The destination is now inspected first and the message says "is a directory — refusing to overwrite it". - module-fs.js ls: the path was only read from argv[0], so `ls -l /work` listed the cwd. The first non-flag argument is now the target; the path-first form is unchanged. Tests: modules/test/test-fs-mv-cp-guard.js (12 asserts) — RED on HEAD (A1-A3, B1, B2, C2, E1), GREEN now; maintain --fix reports "node tests 7/7 pass". Browser on v4.6.104: "mv: /vtest: is a directory — refusing to overwrite it", child and source intact, "ls -l /vtest" lists /vtest (keep.txt), not the cwd.
Diffstat (limited to 'modules/test')
-rw-r--r--modules/test/test-fs-mv-cp-guard.js109
1 files changed, 109 insertions, 0 deletions
diff --git a/modules/test/test-fs-mv-cp-guard.js b/modules/test/test-fs-mv-cp-guard.js
new file mode 100644
index 0000000..369882d
--- /dev/null
+++ b/modules/test/test-fs-mv-cp-guard.js
@@ -0,0 +1,109 @@
+// modules/test/test-fs-mv-cp-guard.js — run: node
+// Proves MAINT-010 (mv/cp must not destroy a directory node) and MAINT-016 (ls -l <path>)
+// against the real classes, no browser.
+const fsMod = require('fs'), path = require('path');
+const ROOT = path.join(__dirname, '..', 'core');
+const ls = {};
+global.localStorage = { getItem: k => (k in ls ? ls[k] : null), setItem: (k, v) => { ls[k] = String(v); }, removeItem: k => { delete ls[k]; } };
+global.indexedDB = { open() { throw new Error('no idb'); }, deleteDatabase() { throw new Error('no idb'); } };
+global.window = global;
+
+function load(file, cls) {
+ return new Function(fsMod.readFileSync(path.join(ROOT, file), 'utf8') + '\nreturn ' + cls + ';')();
+}
+const VFS = load('virtual-fs.js', 'VirtualFileSystem');
+const FSModule = load('module-fs.js', 'FileSystemModule');
+
+function mk() {
+ const out = [];
+ const vfs = new VFS();
+ vfs.saveToStorage = () => true;
+ const term = { print: (m, t) => out.push({ text: String(m), type: t || '' }), fs: vfs };
+ const mod = new FSModule();
+ mod.terminal = term;
+ mod.registerCommands();
+ return { vfs, mod, out };
+}
+
+let fails = 0, passes = 0;
+function ok(name, cond, detail) {
+ if (cond) { passes++; return; }
+ fails++;
+ console.log('FAIL ' + name + (detail ? ' — ' + detail : ''));
+}
+
+// A. mv a file over a directory must refuse and leave the children alone
+{
+ const { vfs, mod, out } = mk();
+ vfs.mkdirRecursive('/docs');
+ vfs.writeFile('/docs/keep.txt', 'precious');
+ vfs.writeFile('/report.txt', 'x');
+ out.length = 0;
+ mod.commands['mv'](['/report.txt', '/docs']);
+ const msg = out.map(o => o.text).join(' ');
+ ok('A1 mv onto a directory is refused', /is a directory/.test(msg), msg);
+ ok('A2 destination children intact', vfs.readFile('/docs/keep.txt') === 'precious');
+ ok('A3 source still there', vfs.readFile('/report.txt') === 'x');
+}
+
+// B. cp over a directory must refuse too
+{
+ const { vfs, mod, out } = mk();
+ vfs.mkdirRecursive('/docs');
+ vfs.writeFile('/docs/keep.txt', 'precious');
+ vfs.writeFile('/report.txt', 'x');
+ out.length = 0;
+ mod.commands['cp'](['/report.txt', '/docs']);
+ const msg = out.map(o => o.text).join(' ');
+ ok('B1 cp onto a directory is refused', /is a directory/.test(msg), msg);
+ ok('B2 destination children intact', vfs.readFile('/docs/keep.txt') === 'precious');
+}
+
+// C. mv a a is a no-op, not a delete
+{
+ const { vfs } = mk();
+ vfs.writeFile('/a.txt', 'still here');
+ ok('C1 mv onto itself returns true', vfs.rename('/a.txt', '/a.txt') === true);
+ ok('C2 file survived', vfs.readFile('/a.txt') === 'still here');
+}
+
+// D. overwriting a file with a file still works (documented mv behaviour)
+{
+ const { vfs } = mk();
+ vfs.writeFile('/one.txt', '1');
+ vfs.writeFile('/two.txt', '2');
+ ok('D1 mv over a file succeeds', vfs.rename('/one.txt', '/two.txt') === true);
+ ok('D2 new content in place', vfs.readFile('/two.txt') === '1');
+ ok('D3 source gone', vfs.readFile('/one.txt') === null);
+}
+
+// E. ls -l <path> lists that path, not the cwd (MAINT-016)
+{
+ const { vfs, mod, out } = mk();
+ vfs.mkdirRecursive('/work');
+ vfs.writeFile('/work/inside.txt', 'x');
+ vfs.writeFile('/cwd-only.txt', 'y');
+ vfs.currentPath = [];
+ out.length = 0;
+ mod.commands['ls'](['-l', '/work']);
+ const text = out.map(o => o.text).join('\n');
+ ok('E1 flag-first form lists the given path', /inside\.txt/.test(text) && !/cwd-only\.txt/.test(text), text.replace(/\n/g, ' | '));
+}
+
+// F. path-first form keeps working
+{
+ const { vfs, mod, out } = mk();
+ vfs.mkdirRecursive('/work');
+ vfs.writeFile('/work/inside.txt', 'x');
+ out.length = 0;
+ mod.commands['ls'](['/work', '-l']);
+ const text = out.map(o => o.text).join('\n');
+ ok('F1 path-first form still lists the path', /inside\.txt/.test(text), text.replace(/\n/g, ' | '));
+}
+
+if (fails === 0) {
+ console.log('test-fs-mv-cp-guard: ' + passes + ' passed, 0 failed');
+} else {
+ console.log('test-fs-mv-cp-guard: ' + passes + ' passed, ' + fails + ' FAILED');
+ process.exit(1);
+}