Initial curated chimera source from a8ae5b1db07fb49926c94585e2718e5130330974
This commit is contained in:
commit
c737a37f71
531 files changed
+119270
No files matched your search
@@ -0,0 +1,256 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { registerFunctionalModules } = require('../../src/server/register-functional-modules');
|
||||
const github = require('../../src/github');
|
||||
|
||||
test('autoloader snapshots can be taken, exported, imported and restored', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-snapshots-'));
|
||||
const paths = {
|
||||
root,
|
||||
backups: path.join(root, 'backups'),
|
||||
cache: path.join(root, 'cache'),
|
||||
tmp: path.join(root, 'tmp'),
|
||||
};
|
||||
for (const dir of Object.values(paths)) fs.mkdirSync(dir, { recursive: true });
|
||||
const remote = new Map([
|
||||
['/data/ps5_autoloader/etaHEN.elf', Buffer.from('payload')],
|
||||
['/data/ps5_autoloader/autoload.txt', Buffer.from('etaHEN.elf\n')],
|
||||
]);
|
||||
const ftp = {
|
||||
isConnected: () => true,
|
||||
hasCredentials: () => true,
|
||||
listDir: async () => [{ name: 'etaHEN.elf', isFile: true, isDir: false, size: 7 }, { name: 'autoload.txt', isFile: true, isDir: false, size: 11 }],
|
||||
downloadFile: async (remotePath, localPath) => {
|
||||
if (!remote.has(remotePath)) throw new Error('missing remote file');
|
||||
fs.writeFileSync(localPath, remote.get(remotePath));
|
||||
},
|
||||
uploadFile: async (localPath, remotePath) => { remote.set(remotePath, fs.readFileSync(localPath)); },
|
||||
};
|
||||
const definitions = new Map();
|
||||
registerFunctionalModules({
|
||||
register: definition => definitions.set(definition.name, definition),
|
||||
db: {}, ftp,
|
||||
settings: { get: (key, fallback) => key === 'ftp.host' ? '192.168.1.105' : fallback },
|
||||
secrets: { get: () => '', has: () => false },
|
||||
paths,
|
||||
logger: { warn() {}, info() {}, error() {} },
|
||||
broadcast() {},
|
||||
});
|
||||
|
||||
assert.equal(definitions.get('payload-manager:autoload-repair-plan').classification, 'sensitive');
|
||||
assert.equal(definitions.get('payload-manager:autoload-repair-apply').classification, 'destructive');
|
||||
assert.equal(definitions.get('payload-manager:autoload-repair-apply').confirmation, 'PERSIST PS5 PAYLOAD MANAGER 0.5.1');
|
||||
assert.equal(definitions.get('payload-manager:autoload-repair-rollback-plan').classification, 'sensitive');
|
||||
assert.equal(definitions.get('payload-manager:autoload-repair-rollback').classification, 'destructive');
|
||||
assert.equal(definitions.get('payload-manager:autoload-repair-rollback').confirmation, 'HERSTEL ORIGINELE BOOTKETEN');
|
||||
assert.equal(definitions.get('etahen:autoload-persistence-plan').classification, 'sensitive');
|
||||
assert.equal(definitions.get('etahen:autoload-persistence-apply').confirmation, 'PERSIST DIRECT ETAHEN 2.6B');
|
||||
assert.equal(definitions.get('etahen:direct-start').classification, 'destructive');
|
||||
assert.equal(definitions.get('etahen:direct-start').confirmation, 'START DIRECT ETAHEN 2.6B');
|
||||
assert.equal(definitions.get('payload-manager:verified-start').classification, 'destructive');
|
||||
assert.equal(definitions.get('payload-manager:verified-start').confirmation, 'START VERIFIED PS5 PAYLOAD MANAGER 0.5.1');
|
||||
assert.equal(definitions.get('etahen:autoload-persistence-rollback-plan').classification, 'sensitive');
|
||||
assert.equal(definitions.get('etahen:autoload-persistence-rollback').confirmation, 'HERSTEL VOOR ETAHEN 2.6B');
|
||||
|
||||
const taken = await definitions.get('snapshot:autoloader:take').handler({ label: 'Werkend' });
|
||||
assert.equal(taken.meta.label, 'Werkend');
|
||||
assert.equal(taken.meta.files.length, 1);
|
||||
assert.equal(taken.meta.hasAutoload, true);
|
||||
|
||||
const listed = await definitions.get('snapshot:autoloader:list').handler();
|
||||
assert.equal(listed.length, 1);
|
||||
assert.equal(listed[0].id, taken.meta.id);
|
||||
|
||||
const exported = await definitions.get('snapshot:autoloader:export').handler({ id: taken.meta.id });
|
||||
const archivePath = path.join(paths.cache, decodeURIComponent(exported.downloadUrl.replace('/pork-cache/', '')));
|
||||
assert.equal(fs.existsSync(archivePath), true);
|
||||
|
||||
const imported = await definitions.get('snapshot:autoloader:import').handler({ data: fs.readFileSync(archivePath), filename: exported.filename, label: 'Import' });
|
||||
assert.equal(imported.meta.label, 'Import');
|
||||
assert.equal(imported.meta.files[0].name, 'etaHEN.elf');
|
||||
|
||||
remote.clear();
|
||||
const restored = await definitions.get('snapshot:autoloader:restore').handler({ id: imported.meta.id });
|
||||
assert.equal(restored.ok, true);
|
||||
assert.equal(restored.fileCount, 1);
|
||||
assert.equal(remote.get('/data/ps5_autoloader/etaHEN.elf').toString(), 'payload');
|
||||
assert.equal(remote.get('/data/ps5_autoloader/autoload.txt').toString(), 'etaHEN.elf\n');
|
||||
});
|
||||
|
||||
test('Y2JB generator can use verified payloads from the Chimera catalog without a browser upload', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-y2jb-catalog-'));
|
||||
const paths = { root, backups: path.join(root, 'backups'), cache: path.join(root, 'cache'), tmp: path.join(root, 'tmp') };
|
||||
const payloadRoot = path.join(root, 'payloads');
|
||||
for (const dir of [...Object.values(paths), payloadRoot]) fs.mkdirSync(dir, { recursive: true });
|
||||
const payloadBytes = Buffer.from('verified catalog payload');
|
||||
const payloadPath = path.join(payloadRoot, 'catalog-test.elf');
|
||||
fs.writeFileSync(payloadPath, payloadBytes);
|
||||
const payloadHash = crypto.createHash('sha256').update(payloadBytes).digest('hex');
|
||||
const definitions = new Map();
|
||||
registerFunctionalModules({
|
||||
register: definition => definitions.set(definition.name, definition),
|
||||
db: {
|
||||
getPayloadCatalog: () => ({
|
||||
files: [{
|
||||
id: 42,
|
||||
asset_name: 'catalog-test.elf',
|
||||
local_path: payloadPath,
|
||||
local_size: payloadBytes.length,
|
||||
local_hash: payloadHash,
|
||||
local_version: '1.2.3',
|
||||
source_name: 'Fixture source',
|
||||
}],
|
||||
}),
|
||||
},
|
||||
ftp: { isConnected: () => false, hasCredentials: () => false },
|
||||
settings: { get: (key, fallback) => key === 'payload.localPath' ? payloadRoot : fallback },
|
||||
secrets: { get: () => '', has: () => false },
|
||||
paths,
|
||||
logger: { warn() {}, info() {}, error() {} },
|
||||
broadcast() {},
|
||||
});
|
||||
try {
|
||||
const catalog = definitions.get('y2jbgen:catalog').handler();
|
||||
assert.deepEqual(catalog.items, [{
|
||||
id: '42',
|
||||
name: 'catalog-test.elf',
|
||||
size: payloadBytes.length,
|
||||
sha256: payloadHash,
|
||||
version: '1.2.3',
|
||||
source: 'Fixture source',
|
||||
}]);
|
||||
const result = await definitions.get('y2jbgen:build').handler({
|
||||
catalogPayloadIds: ['42'],
|
||||
entries: [{ type: 'payload', value: 'catalog-test.elf' }],
|
||||
});
|
||||
assert.equal(result.outputZip, 'y2jb_update.zip');
|
||||
assert.match(result.updateInfo, /ps5_autoloader\/catalog-test\.elf\|24/);
|
||||
|
||||
fs.writeFileSync(payloadPath, 'tampered');
|
||||
await assert.rejects(
|
||||
() => definitions.get('y2jbgen:build').handler({
|
||||
catalogPayloadIds: ['42'],
|
||||
entries: [{ type: 'payload', value: 'catalog-test.elf' }],
|
||||
}),
|
||||
/Checksum/
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('media catalog persists deltas and reuses unchanged PS5 directories', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-media-catalog-'));
|
||||
const paths = { root, backups: path.join(root, 'backups'), cache: path.join(root, 'cache'), tmp: path.join(root, 'tmp') };
|
||||
for (const dir of Object.values(paths)) fs.mkdirSync(dir, { recursive: true });
|
||||
let marker = new Date('2026-07-16T10:00:00Z');
|
||||
let files = [{ name: '20260716_120000.jpg', isDir: false, size: 123, modifiedAt: marker }];
|
||||
let failChild = false;
|
||||
const calls = [];
|
||||
const ftp = {
|
||||
isConnected: () => true, hasCredentials: () => true,
|
||||
listDir: async remotePath => {
|
||||
calls.push(remotePath);
|
||||
if (remotePath === '/user/av_contents/photo') return [{ name: 'CUSA12345', isDir: true, size: 0, modifiedAt: marker }];
|
||||
if (remotePath === '/user/av_contents/photo/CUSA12345') { if (failChild) throw new Error('temporary FTP failure'); return files; }
|
||||
if (remotePath === '/user/av_contents/video') return [];
|
||||
return [];
|
||||
},
|
||||
};
|
||||
const definitions = new Map();
|
||||
registerFunctionalModules({
|
||||
register: definition => definitions.set(definition.name, definition), db: {}, ftp,
|
||||
settings: { get: (key, fallback) => key === 'ftp.host' ? '127.0.0.1' : fallback },
|
||||
secrets: { get: () => '', has: () => false }, paths,
|
||||
logger: { warn() {}, info() {}, error() {} }, broadcast() {},
|
||||
});
|
||||
try {
|
||||
const scan = definitions.get('media:scan');
|
||||
const first = await scan.handler({ force: true });
|
||||
assert.equal(first.stats.total, 1);
|
||||
assert.equal(first.delta.added, 1);
|
||||
assert.equal(first.scan.mode, 'full');
|
||||
const catalogFile = path.join(paths.cache, 'media', 'catalog.json');
|
||||
const cached = JSON.parse(fs.readFileSync(catalogFile, 'utf8'));
|
||||
cached.scannedAt = 1;
|
||||
fs.writeFileSync(catalogFile, JSON.stringify(cached));
|
||||
calls.length = 0;
|
||||
const second = await scan.handler({ force: false });
|
||||
assert.equal(second.scan.mode, 'incremental');
|
||||
assert.equal(second.scan.reusedDirectories, 1);
|
||||
assert.deepEqual(calls, ['/user/av_contents/photo', '/user/av_contents/video']);
|
||||
marker = new Date('2026-07-16T10:05:00Z');
|
||||
files = [...files, { name: '20260716_120500.png', isDir: false, size: 456, modifiedAt: marker }];
|
||||
const stale = JSON.parse(fs.readFileSync(catalogFile, 'utf8'));
|
||||
stale.scannedAt = 1;
|
||||
fs.writeFileSync(catalogFile, JSON.stringify(stale));
|
||||
const third = await scan.handler({ force: false });
|
||||
assert.equal(third.stats.total, 2);
|
||||
assert.equal(third.delta.added, 1);
|
||||
assert.equal(third.scan.mode, 'incremental');
|
||||
const loaded = definitions.get('media:catalog').handler();
|
||||
assert.equal(loaded.items.length, 2);
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(loaded, 'directories'), true);
|
||||
assert.equal(loaded.directories, undefined);
|
||||
marker = new Date('2026-07-16T10:10:00Z');
|
||||
failChild = true;
|
||||
const beforeFailure = JSON.parse(fs.readFileSync(catalogFile, 'utf8'));
|
||||
beforeFailure.scannedAt = 1;
|
||||
fs.writeFileSync(catalogFile, JSON.stringify(beforeFailure));
|
||||
const preserved = await scan.handler({ force: false });
|
||||
assert.equal(preserved.stats.total, 2);
|
||||
assert.equal(preserved.scan.errors.length, 1);
|
||||
assert.equal(preserved.scan.errors[0].preservedItems, 2);
|
||||
assert.equal(preserved.delta.removed, 0);
|
||||
} finally { fs.rmSync(root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test('payload synchronization stops API calls after a GitHub rate limit and preserves cached rows', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-payload-rate-limit-'));
|
||||
const paths = { root, backups: path.join(root, 'backups'), cache: path.join(root, 'cache'), tmp: path.join(root, 'tmp') };
|
||||
for (const dir of Object.values(paths)) fs.mkdirSync(dir, { recursive: true });
|
||||
const sources = [1, 2, 3].map(id => ({ id, name: `Source ${id}`, github_url: `https://github.com/example/repo-${id}`, enabled: 1, built_in: 0 }));
|
||||
const updates = [];
|
||||
const progress = [];
|
||||
let calls = 0;
|
||||
const originalFetchLatestRelease = github.fetchLatestRelease;
|
||||
github.fetchLatestRelease = async () => {
|
||||
calls += 1;
|
||||
const error = new Error('GitHub API-limiet bereikt; probeer later opnieuw.');
|
||||
error.code = 'GITHUB_RATE_LIMIT';
|
||||
error.retryAt = Date.now() + 60_000;
|
||||
throw error;
|
||||
};
|
||||
const definitions = new Map();
|
||||
try {
|
||||
registerFunctionalModules({
|
||||
register: definition => definitions.set(definition.name, definition),
|
||||
db: {
|
||||
upsertPayloadSource() {}, listPayloadSources: () => sources, removePayloadSource() {},
|
||||
getPayloadFiles: sourceId => [{ id: sourceId, asset_name: `cached-${sourceId}.elf` }],
|
||||
updatePayloadSourceError: (sourceId, error) => updates.push({ sourceId, error }),
|
||||
},
|
||||
ftp: { isConnected: () => false, hasCredentials: () => false },
|
||||
settings: { get: (key, fallback) => fallback }, secrets: { get: () => '', has: () => false }, paths,
|
||||
logger: { warn() {}, info() {}, error() {} }, broadcast: (name, value) => progress.push({ name, value }),
|
||||
});
|
||||
const result = await definitions.get('payload:check-updates').handler({});
|
||||
assert.equal(calls, 1);
|
||||
assert.equal(result.length, 3);
|
||||
assert.equal(result[0].deferred, undefined);
|
||||
assert.equal(result[1].deferred, true);
|
||||
assert.equal(result[2].deferred, true);
|
||||
assert.equal(updates.length, 1);
|
||||
assert.equal(result[1].files[0].asset_name, 'cached-2.elf');
|
||||
assert.equal(progress.filter(item => item.value.status === 'deferred').length, 2);
|
||||
} finally {
|
||||
github.fetchLatestRelease = originalFetchLatestRelease;
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user