Files
chimera-Public/test/server/backup-replication-service.test.js
T

82 lines
4.4 KiB
JavaScript

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { createBackupReplicationService } = require('../../src/services/backup-replication-service');
test('backup replication copies core files and verifies every hash', async t => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-replication-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const backups = path.join(root, 'source', 'backups');
const target = path.join(root, 'target');
const tmp = path.join(root, 'tmp');
fs.mkdirSync(path.join(backups, 'save-vault'), { recursive: true });
fs.mkdirSync(target, { recursive: true });
fs.mkdirSync(tmp, { recursive: true });
fs.writeFileSync(path.join(backups, 'save-vault', 'PPSA00001.zip'), 'encrypted-save-data');
const service = createBackupReplicationService({ paths: { backups, tmp }, targetRoot: target, gameBackupsRoot: path.join(root, 'games') });
const result = await service.replicate({ label: 'Veilige test' });
assert.equal(result.phase, 'complete');
assert.equal(result.copiedFiles, 1);
assert.equal(service.list()[0].label, 'Veilige test');
const verified = await service.verify({ id: result.id });
assert.equal(verified.ok, true);
assert.equal(verified.files, 1);
const drill = await service.restoreDrill({ id: result.id });
assert.equal(drill.ok, true);
assert.equal(drill.ps5Modified, false);
assert.equal(drill.temporaryTargetRemoved, true);
assert.equal(fs.readdirSync(path.join(tmp, 'restore-drills')).length, 0);
const protection = service.status().protection;
assert.equal(protection.threeTwoOneSatisfied, false);
assert.equal(protection.copiesObserved, 2);
assert.equal(protection.immutableObserved, false);
assert.ok(protection.lastVerifiedAt);
assert.ok(protection.lastRestoreDrillAt);
assert.ok(Number.isFinite(protection.lastRestoreDrillDurationMs));
assert.ok(service.list()[0].lastRestoreDrillAt);
const copied = path.join(target, result.id, 'data', 'chimera-core', 'save-vault', 'PPSA00001.zip');
fs.appendFileSync(copied, 'tampered');
await assert.rejects(() => service.verify({ id: result.id }), /Bestandsgrootte|Hash wijkt af/);
});
test('replication retention keeps at least two valid snapshots and exposes immutable-target truthfully', async t => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-replication-retention-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const backups = path.join(root, 'backups'); const target = path.join(root, 'target'); const tmp = path.join(root, 'tmp');
for (const directory of [backups, target, tmp]) fs.mkdirSync(directory, { recursive: true });
fs.writeFileSync(path.join(backups, 'settings.json'), '{}');
const values = new Map([['backup.replicationRetentionEnabled', true], ['backup.replicationRetention', 2], ['backup.replicationMaxAgeDays', 180]]);
const settings = { get: (key, fallback) => values.has(key) ? values.get(key) : fallback, set: (key, value) => values.set(key, value) };
const service = createBackupReplicationService({ paths: { backups, tmp }, settings, targetRoot: target, gameBackupsRoot: path.join(root, 'games') });
await service.replicate({ label: 'one' });
await new Promise(resolve => setTimeout(resolve, 5));
await service.replicate({ label: 'two' });
await new Promise(resolve => setTimeout(resolve, 5));
const third = await service.replicate({ label: 'three' });
assert.equal(third.retention.removed, 1);
assert.equal(service.list().length, 2);
assert.equal(service.status().targetContract.immutable, false);
assert.equal(service.retentionPreview().retainedMinimum, 2);
});
test('backup replication refuses a target nested in its source', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-replication-unsafe-'));
try {
const backups = path.join(root, 'backups');
const target = path.join(backups, 'replication');
fs.mkdirSync(target, { recursive: true });
const service = createBackupReplicationService({ paths: { backups }, targetRoot: target, gameBackupsRoot: path.join(root, 'games') });
const status = service.status();
assert.equal(status.available, false);
assert.match(status.error, /mogen niet in elkaar liggen/);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});