Files
mdg-filius/packages/core/test/virtualFileSystem.test.ts
julian.kiedaischandClaude Opus 5.5 3d6655e736 feat(sim): F4.6f1 what a run leaves goes back into the document
The oracle SaveAfterRunRun saves Java's project after a run. The room writes
what a run leaves that Java keeps - files, running servers, virtual hosts,
mail servers' domains and accounts, mail programs' accounts and folders,
firewalls, DHCP clients' addresses - into the document as origin 'sim', at
most once a second, after each action and at the stop; writeRunState writes
only what differs. The command line now goes on in a folder made before a
restart, as Java's does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:05:11 +00:00

188 lines
6.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The virtual file system against the Swift core: every case in
// vfs.cases.json runs in Swift (vfs.swift.json, tools/swift-oracle --vfs);
// each step's result or error and the final entries must match. The cases
// include the three Swift tests on the file system, ported as cases.
import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import {
VirtualFileSystem,
VirtualFileSystemError,
normalizedAbsolutePath,
type VirtualFileContent,
type VirtualFileEntry,
} from '../src/index.js';
interface Step {
op: string;
path?: string;
to?: string;
name?: string;
text?: string;
size?: number;
byte?: number;
mediaType?: string;
recursive?: boolean;
overwrite?: boolean;
entries?: { path: string; kind: string; text?: string }[];
}
const here = dirname(fileURLToPath(import.meta.url));
const cases = JSON.parse(readFileSync(join(here, 'vfs.cases.json'), 'utf8')) as { name: string; steps: Step[] }[];
const swift = JSON.parse(readFileSync(join(here, 'vfs.swift.json'), 'utf8')) as {
name: string;
results: unknown[];
entries: unknown[];
}[];
function describeEntries(entries: VirtualFileEntry[]): unknown[] {
return entries.map(({ path, content }) => ({ path, ...describeContent(content) }));
}
function describeContent(content: VirtualFileContent): Record<string, unknown> {
switch (content.kind) {
case 'directory':
return { kind: 'directory' };
case 'text':
return { kind: 'text', text: content.text };
case 'binary':
case 'image':
return {
kind: content.kind,
bytes: content.data.length,
mediaType: content.mediaType ?? null,
sha256: createHash('sha256').update(content.data).digest('hex'),
};
}
}
function run(steps: Step[]) {
let fileSystem = new VirtualFileSystem();
const results: unknown[] = [];
for (const step of steps) {
const path = step.path ?? '/';
const bytes = () => new Uint8Array(step.size ?? 0).fill(step.byte ?? 0);
try {
switch (step.op) {
case 'createDirectory':
fileSystem.createDirectory(path, step.recursive ?? false);
break;
case 'writeText':
fileSystem.writeTextFile(path, step.text ?? '', step.overwrite ?? true);
break;
case 'writeBinary':
fileSystem.writeBinaryFile(path, bytes(), step.mediaType, step.overwrite ?? true);
break;
case 'writeImage':
fileSystem.writeImageFile(path, bytes(), step.mediaType ?? 'image/png', step.overwrite ?? true);
break;
case 'copy':
fileSystem.copyItem(path, step.to ?? '/', step.overwrite ?? false);
break;
case 'move':
fileSystem.moveItem(path, step.to ?? '/', step.overwrite ?? false);
break;
case 'rename':
results.push({ ok: fileSystem.renameItem(path, step.name ?? '') });
continue;
case 'delete':
fileSystem.deleteItem(path, step.recursive ?? false);
break;
case 'entry':
results.push({ ok: describeEntries([fileSystem.entry(path)])[0] });
continue;
case 'entries':
results.push({ ok: describeEntries(fileSystem.entries(path)) });
continue;
case 'text':
results.push({ ok: fileSystem.textFile(path) });
continue;
case 'contains':
results.push({ ok: fileSystem.contains(path) });
continue;
case 'normalize':
results.push({ ok: normalizedAbsolutePath(path) });
continue;
case 'default':
fileSystem = VirtualFileSystem.defaultForDevice();
break;
case 'fromEntries':
fileSystem = VirtualFileSystem.fromEntries(
(step.entries ?? []).map((entry) => ({
path: entry.path,
content: entry.kind === 'directory' ? { kind: 'directory' } : { kind: 'text', text: entry.text ?? '' },
})),
);
break;
default:
throw new Error(`unknown step ${step.op}`);
}
results.push({ ok: true });
} catch (error) {
if (!(error instanceof VirtualFileSystemError)) throw error;
results.push({ error: error.code, values: error.values });
}
}
return { results, entries: describeEntries(fileSystem.allEntries()) };
}
describe('virtual file system against Swift', () => {
it('covers every case', () => {
expect(swift.map((result) => result.name)).toEqual(cases.map((item) => item.name));
});
cases.forEach((item, index) => {
it(item.name, () => {
const { results, entries } = run(item.steps);
const expected = swift[index]!;
results.forEach((result, step) => {
expect(result, `step ${step + 1}: ${JSON.stringify(item.steps[step])}`).toEqual(expected.results[step]);
});
expect(entries).toEqual(expected.entries);
});
});
});
describe('virtual file system', () => {
it('leaves itself unchanged when a copy breaks a quota (Swift test)', () => {
const fileSystem = new VirtualFileSystem();
const maximum = new Uint8Array(8 * 1024 * 1024).fill(0x41);
for (const name of ['source', 'destination', 'third', 'fourth']) {
fileSystem.writeBinaryFile(`/${name}.bin`, maximum, 'application/octet-stream');
}
const before = fileSystem.clone();
expect(() => fileSystem.copyItem('/source.bin', '/new.bin', true)).toThrow(VirtualFileSystemError);
expect(fileSystem.equals(before)).toBe(true);
});
it('upgrades the 1×1 black images of older versions, and only those', () => {
const fileSystem = VirtualFileSystem.defaultForDevice();
expect(fileSystem.upgradeLegacyDefaultImages()).toBe(0);
});
});
describe("a file system's revision", () => {
it('changes with every change, a removal too, and not with a reading', () => {
const files = new VirtualFileSystem();
const seen = [files.revision];
files.createDirectory('/a');
seen.push(files.revision);
files.writeTextFile('/a/b.txt', 'x');
seen.push(files.revision);
files.renameInPlace('/a/b.txt', 'c.txt');
seen.push(files.revision);
files.deleteItem('/a/c.txt');
seen.push(files.revision);
files.allEntries();
files.contains('/a');
seen.push(files.revision);
expect(new Set(seen.slice(0, 5)).size).toBe(5);
expect(seen[5]).toBe(seen[4]);
});
});