mirror of
https://github.com/jcreek/OpenNetworkDiagram.git
synced 2026-09-10 23:23:43 +00:00
feat(#14): Add zero-setup Docker data initialization
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
|
||||
let moduleSequence = 0;
|
||||
|
||||
function restoreEnvironment(name, value) {
|
||||
if (value === undefined) {
|
||||
delete process.env[name];
|
||||
return;
|
||||
}
|
||||
process.env[name] = value;
|
||||
}
|
||||
|
||||
async function withPersistenceEnvironment(run, options = {}) {
|
||||
const root = await mkdtemp(path.join(os.tmpdir(), 'ond-persistence-'));
|
||||
const source = path.join(root, 'data', 'network.json');
|
||||
const backupDirectory = path.join(root, 'backups');
|
||||
const previousDataFile = process.env.NETWORK_DATA_FILE;
|
||||
const previousBackupDirectory = process.env.NETWORK_BACKUP_DIR;
|
||||
const previousReadOnly = process.env.NETWORK_READ_ONLY;
|
||||
|
||||
process.env.NETWORK_DATA_FILE = source;
|
||||
process.env.NETWORK_BACKUP_DIR = backupDirectory;
|
||||
process.env.NETWORK_READ_ONLY = options.readOnly ? 'true' : 'false';
|
||||
moduleSequence += 1;
|
||||
|
||||
try {
|
||||
const persistence = await import(
|
||||
`../src/lib/shared/networkPersistenceCore.mjs?test=${moduleSequence}`
|
||||
);
|
||||
await run({ persistence, root, source, backupDirectory });
|
||||
} finally {
|
||||
restoreEnvironment('NETWORK_DATA_FILE', previousDataFile);
|
||||
restoreEnvironment('NETWORK_BACKUP_DIR', previousBackupDirectory);
|
||||
restoreEnvironment('NETWORK_READ_ONLY', previousReadOnly);
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
test('initializes a missing writable data file with a blank network', async () => {
|
||||
await withPersistenceEnvironment(async ({ persistence, source, backupDirectory }) => {
|
||||
const result = await persistence.readOrInitializeNetworkFile();
|
||||
|
||||
assert.equal(result.initialized, true);
|
||||
assert.deepEqual(result.data, { machines: [], devices: [] });
|
||||
assert.equal(result.source, source);
|
||||
assert.deepEqual(JSON.parse(await readFile(source, 'utf8')), {
|
||||
machines: [],
|
||||
devices: []
|
||||
});
|
||||
assert.deepEqual(await readdir(backupDirectory), []);
|
||||
});
|
||||
});
|
||||
|
||||
async function assertInitializesBlankFile(contents) {
|
||||
await withPersistenceEnvironment(async ({ persistence, source }) => {
|
||||
await persistence.writeNetworkFile({ machines: [], devices: [] }, { createBackup: false });
|
||||
await writeFile(source, contents, 'utf8');
|
||||
|
||||
const result = await persistence.readOrInitializeNetworkFile();
|
||||
|
||||
assert.equal(result.initialized, true);
|
||||
assert.deepEqual(JSON.parse(await readFile(source, 'utf8')), {
|
||||
machines: [],
|
||||
devices: []
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('initializes zero-byte and whitespace-only writable data files', async () => {
|
||||
await assertInitializesBlankFile('');
|
||||
await assertInitializesBlankFile(' \n\t');
|
||||
});
|
||||
|
||||
test('does not rewrite an existing valid empty network', async () => {
|
||||
await withPersistenceEnvironment(async ({ persistence, source }) => {
|
||||
await persistence.writeNetworkFile({ machines: [], devices: [] }, { createBackup: false });
|
||||
const before = await stat(source);
|
||||
|
||||
const result = await persistence.readOrInitializeNetworkFile();
|
||||
const after = await stat(source);
|
||||
|
||||
assert.equal(result.initialized, false);
|
||||
assert.deepEqual(result.data, { machines: [], devices: [] });
|
||||
assert.equal(after.mtimeMs, before.mtimeMs);
|
||||
});
|
||||
});
|
||||
|
||||
test('does not replace malformed or schema-invalid non-empty JSON', async () => {
|
||||
await withPersistenceEnvironment(async ({ persistence, source }) => {
|
||||
await persistence.writeNetworkFile({ machines: [], devices: [] }, { createBackup: false });
|
||||
await writeFile(source, '{not json', 'utf8');
|
||||
|
||||
await assert.rejects(persistence.readOrInitializeNetworkFile(), SyntaxError);
|
||||
assert.equal(await readFile(source, 'utf8'), '{not json');
|
||||
});
|
||||
|
||||
await withPersistenceEnvironment(async ({ persistence, source }) => {
|
||||
await persistence.writeNetworkFile({ machines: [], devices: [] }, { createBackup: false });
|
||||
await writeFile(source, '{}\n', 'utf8');
|
||||
|
||||
const result = await persistence.readOrInitializeNetworkFile();
|
||||
|
||||
assert.equal(result.initialized, false);
|
||||
assert.deepEqual(result.data, {});
|
||||
assert.equal(await readFile(source, 'utf8'), '{}\n');
|
||||
});
|
||||
});
|
||||
|
||||
test('never initializes missing or blank files in explicit read-only mode', async () => {
|
||||
await withPersistenceEnvironment(
|
||||
async ({ persistence, source }) => {
|
||||
await assert.rejects(
|
||||
persistence.readOrInitializeNetworkFile(),
|
||||
/Writes disabled by NETWORK_READ_ONLY=true/
|
||||
);
|
||||
await assert.rejects(stat(source), { code: 'ENOENT' });
|
||||
},
|
||||
{ readOnly: true }
|
||||
);
|
||||
|
||||
await withPersistenceEnvironment(
|
||||
async ({ persistence, source }) => {
|
||||
await mkdir(path.dirname(source), { recursive: true });
|
||||
await writeFile(source, ' \n', 'utf8');
|
||||
|
||||
await assert.rejects(
|
||||
persistence.readOrInitializeNetworkFile(),
|
||||
/Writes disabled by NETWORK_READ_ONLY=true/
|
||||
);
|
||||
assert.equal(await readFile(source, 'utf8'), ' \n');
|
||||
},
|
||||
{ readOnly: true }
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'unwritable blank files remain untouched and report the permission failure',
|
||||
{ skip: process.platform === 'win32' },
|
||||
async () => {
|
||||
await withPersistenceEnvironment(async ({ persistence, source }) => {
|
||||
await persistence.writeNetworkFile({ machines: [], devices: [] }, { createBackup: false });
|
||||
await writeFile(source, '', 'utf8');
|
||||
await chmod(source, 0o400);
|
||||
|
||||
try {
|
||||
const writableState = await persistence.getWritableState();
|
||||
assert.equal(writableState.writable, false);
|
||||
assert.match(writableState.reason, /not readable and writable/);
|
||||
await assert.rejects(
|
||||
persistence.readOrInitializeNetworkFile(),
|
||||
/not readable and writable/
|
||||
);
|
||||
assert.equal(await readFile(source, 'utf8'), '');
|
||||
} finally {
|
||||
await chmod(source, 0o600);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user