diff options
| author | Agando <roduccomm@gmail.com> | 2026-10-27 08:32:39 +0100 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-10-27 08:32:39 +0100 |
| commit | f1734dfddd88aad22818911e6e5f00bd68675017 (patch) | |
| tree | b43d5002a2c1e6f5f646b7a02e0dab85dfad7b33 | |
| parent | 197ab31e1dc915a991b3d88697e0fc4c294563a8 (diff) | |
fix(newos4): regression test for checkpoint name round-trip (auto)
Task 3 of plan 2026-10-27_061724-newos4-checkpoint-name-mismatch.md.
New modules/test/test-vfs-checkpoint.js loads the real VirtualFileSystem and
SystemModule with guarded localStorage/indexedDB/window stubs and asserts:
A1-A4 checkpoint('') returns the resolved name; two calls differ; the file
exists under the returned name
B1-B2 checkpoint-list shows the returned name; rollback(<that name>) is true
C1-C4 the `checkpoint` command prints exactly the name it created (and an
explicit name verbatim)
RED before the fix (v4.6.101):
FAIL C2 printed name exists on disk — printed=2026-10-27T07:31:21.575Z
listed=["2026-10-27T07:31:21.574Z"]
test-vfs-checkpoint: 2 passed, 8 FAILED
GREEN after:
$ node modules/test/test-vfs-checkpoint.js
test-vfs-checkpoint: 10 passed, 0 failed
| -rw-r--r-- | modules/test/test-vfs-checkpoint.js | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/modules/test/test-vfs-checkpoint.js b/modules/test/test-vfs-checkpoint.js new file mode 100644 index 0000000..03a8c8a --- /dev/null +++ b/modules/test/test-vfs-checkpoint.js @@ -0,0 +1,92 @@ +// modules/test/test-vfs-checkpoint.js — run: node modules/test/test-vfs-checkpoint.js +// Proves the name a checkpoint is SAVED under is the name the UI prints and the name +// `rollback` resolves. RED on v4.6.101: two independent new Date().toISOString() calls +// made the printed name differ from the stored one, so `checkpoint` then +// `rollback <printed name>` failed with "checkpoint not found or invalid". +const fsMod = require('fs'), path = require('path'); +const ROOT = path.join(__dirname, '..', 'core'); + +// ── guarded stubs for the browser globals the VFS touches ────────────────── +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('indexedDB not available in test'); }, + deleteDatabase() { throw new Error('indexedDB not available in test'); }, +}; +global.window = global; + +function loadClass(file, clsName) { + const src = fsMod.readFileSync(path.join(ROOT, file), 'utf8'); + return new Function(src + '\nreturn ' + clsName + ';')(); +} +const VirtualFileSystem = loadClass('virtual-fs.js', 'VirtualFileSystem'); +const SystemModule = loadClass('module-system.js', 'SystemModule'); + +function mkFs() { + const fs = new VirtualFileSystem(); + fs.saveToStorage = () => true; // isolate from timers / compression + return fs; +} + +let fails = 0, passes = 0; +function ok(name, cond, detail) { + if (cond) { passes++; return; } + fails++; + console.log('FAIL ' + name + (detail ? ' — ' + detail : '')); +} + +// A. checkpoint('') must return the resolved name, and two calls must differ +{ + const fs = mkFs(); + const a = fs.checkpoint(''); + const b = fs.checkpoint(''); + ok('A1 checkpoint returns a name string', typeof a === 'string' && a.length > 0, 'got ' + typeof a + ' ' + a); + ok('A2 second name also a string', typeof b === 'string' && b.length > 0, 'got ' + typeof b); + ok('A3 two unnamed checkpoints get distinct names', a !== b, 'both=' + a); + ok('A4 stored under the returned name', !!fs.fsReadJSON('/_snapshots/checkpoint-' + a + '.json')); +} + +// B. rollback with the returned name must succeed +{ + const fs = mkFs(); + fs.mkdirRecursive('/marker'); + const name = fs.checkpoint(''); + ok('B1 checkpoint-list shows the returned name', fs.listCheckpoints().includes(name), 'name=' + name); + ok('B2 rollback(returned name) is true', fs.rollback(name) === true, 'name=' + name); +} + +// C. the `checkpoint` command prints exactly the name it created +{ + const fsv = mkFs(); + const out = []; + const sys = new SystemModule(); + sys.terminal = { print: m => out.push(String(m)), fs: fsv }; + sys.registerCommands(); + + out.length = 0; + sys.commands['checkpoint']([]); + const line = out.join('\n'); + const printed = (line.match(/Checkpoint created:\s*(.+)$/m) || [])[1]; + ok('C1 command prints a created name', !!printed, 'output=' + JSON.stringify(line)); + const listed = fsv.listCheckpoints(); + ok('C2 printed name exists on disk', !!printed && listed.includes(printed.trim()), + 'printed=' + printed + ' listed=' + JSON.stringify(listed)); + ok('C3 rollback of the printed name succeeds', !!printed && fsv.rollback(printed.trim()) === true); + + // C4: an explicit name is echoed verbatim + out.length = 0; + sys.commands['checkpoint'](['myname']); + ok('C4 explicit name echoed verbatim', /Checkpoint created:\s*myname\s*$/.test(out.join('\n')), + 'output=' + JSON.stringify(out.join('\n'))); +} + +if (fails === 0) { + console.log('test-vfs-checkpoint: ' + passes + ' passed, 0 failed'); +} else { + console.log('test-vfs-checkpoint: ' + passes + ' passed, ' + fails + ' FAILED'); + process.exit(1); +} |