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,345 @@
|
||||
'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 database = require('../../src/database');
|
||||
|
||||
const reconcilerPath = path.resolve(__dirname, '../../src/services/console-scan-reconciler.js');
|
||||
function loadReconciler() {
|
||||
delete require.cache[reconcilerPath];
|
||||
return require(reconcilerPath).reconcileConsoleScan;
|
||||
}
|
||||
|
||||
function withTempDatabase(fn) {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-reconciler-test-'));
|
||||
const reconcileConsoleScan = loadReconciler();
|
||||
database.configure({
|
||||
dataDir: root,
|
||||
logger: { info() {}, warn() {}, error() {} },
|
||||
});
|
||||
return database.initialize()
|
||||
.then(() => fn(reconcileConsoleScan))
|
||||
.finally(() => {
|
||||
database.close();
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
});
|
||||
}
|
||||
|
||||
const outdatedSeenAt = Date.parse('2026-07-21T10:00:00.000Z');
|
||||
|
||||
function seedTitle(gameId) {
|
||||
const id = gameId.startsWith('CUSA') ? 'ps4' : 'ps5';
|
||||
database.runMutatingSqlWithParams(`
|
||||
INSERT INTO titles (platform, title_id, content_id, name, sort_name, region, metadata_source)
|
||||
VALUES ('${id}', @game_id, @content_id, @name, @name, 'unknown', 'tests')
|
||||
`, {
|
||||
game_id: gameId,
|
||||
content_id: `${gameId}-CONTENT`,
|
||||
name: `Test ${gameId}`,
|
||||
});
|
||||
return Number(database.runQuery('SELECT last_insert_rowid() AS id')[0]?.id || 0);
|
||||
}
|
||||
|
||||
function ensureDeviceAndVolume(rootPath) {
|
||||
const parts = String(rootPath || '').replace(/\\/g, '/').replace(/^\/+/, '').split('/').filter(Boolean);
|
||||
const fingerprint = `legacy:${parts[0] || 'unknown-device'}`;
|
||||
const volumeKey = parts.length > 1 ? `legacy:${parts.slice(0, 2).join('/')}` : `legacy:${parts[0] || 'unknown'}`;
|
||||
|
||||
let row = database.runQuery(`
|
||||
SELECT id FROM storage_devices
|
||||
WHERE scope = 'console' AND stable_fingerprint = '${fingerprint}'
|
||||
`)[0];
|
||||
if (!row?.id) {
|
||||
database.runMutatingSqlWithParams(`
|
||||
INSERT INTO storage_devices (scope, stable_fingerprint, display_name, device_type, availability_state)
|
||||
VALUES ('console', @fingerprint, @displayName, 'console', 'available')
|
||||
`, { fingerprint, displayName: rootPath || 'legacy-device' });
|
||||
row = database.runQuery('SELECT last_insert_rowid() AS id')[0];
|
||||
}
|
||||
const deviceId = Number(row?.id || 0);
|
||||
|
||||
row = database.runQuery(`
|
||||
SELECT id FROM storage_volumes
|
||||
WHERE device_id = ${deviceId} AND stable_volume_key = '${volumeKey}'
|
||||
`)[0];
|
||||
if (!row?.id) {
|
||||
database.runMutatingSqlWithParams(`
|
||||
INSERT INTO storage_volumes (device_id, stable_volume_key, mount_path, can_copy, can_play, can_install, can_mount, availability_state)
|
||||
VALUES (@deviceId, @volumeKey, @mountPath, 1, 1, 1, 1, 'available')
|
||||
`, {
|
||||
deviceId,
|
||||
volumeKey,
|
||||
mountPath: rootPath || '',
|
||||
});
|
||||
row = database.runQuery('SELECT last_insert_rowid() AS id')[0];
|
||||
}
|
||||
return { deviceId, volumeId: Number(row?.id || 0) };
|
||||
}
|
||||
|
||||
function seedConsoleInstall({
|
||||
gameId,
|
||||
rootPath,
|
||||
installState = 'observed',
|
||||
installedVersion = '1.00',
|
||||
lastSeenAt,
|
||||
consoleProfileId = 'legacy-console',
|
||||
}) {
|
||||
const titleId = seedTitle(gameId);
|
||||
const { volumeId } = ensureDeviceAndVolume(rootPath);
|
||||
const remote = `legacy:${String(rootPath).replace(/\/+$/, '')}`;
|
||||
const observed = Number(lastSeenAt || 0) || Date.now();
|
||||
database.runMutatingSqlWithParams(`
|
||||
INSERT INTO console_installs (
|
||||
title_id, console_profile_id, volume_id, remote_location_key, installed_version, install_state,
|
||||
first_seen_at, last_seen_at, created_at, updated_at
|
||||
) VALUES (
|
||||
@titleId, @consoleProfileId, @volumeId, @remote, @installedVersion, @installState,
|
||||
@observed, @observed, datetime('now'), datetime('now')
|
||||
)
|
||||
`, {
|
||||
titleId,
|
||||
consoleProfileId,
|
||||
volumeId,
|
||||
remote,
|
||||
installedVersion,
|
||||
installState,
|
||||
observed,
|
||||
});
|
||||
return {
|
||||
titleId,
|
||||
volumeId,
|
||||
remote,
|
||||
};
|
||||
}
|
||||
|
||||
function consoleInstallRows() {
|
||||
return database.runQuery(`
|
||||
SELECT t.title_id AS title_id, ci.id, ci.console_profile_id, ci.install_state,
|
||||
ci.remote_location_key, ci.last_seen_at
|
||||
FROM console_installs ci
|
||||
JOIN titles t ON t.id = ci.title_id
|
||||
ORDER BY ci.id
|
||||
`);
|
||||
}
|
||||
|
||||
function scanResultFixture(overrides) {
|
||||
return Object.assign({
|
||||
generation: 1,
|
||||
scan_id: 'test-scan',
|
||||
provider: 'ftp',
|
||||
started_at: '2026-07-21T12:00:00.000Z',
|
||||
completed_at: '2026-07-21T12:00:01.000Z',
|
||||
root_coverage: [
|
||||
{ root: '/mnt/sandbox/pfsmnt/', coverage_state: 'complete' },
|
||||
],
|
||||
games: [],
|
||||
count: 0,
|
||||
}, overrides);
|
||||
}
|
||||
|
||||
test('complete scan marks missing installs absent only for completed roots', () => {
|
||||
return withTempDatabase((reconcileConsoleScan) => {
|
||||
seedConsoleInstall({
|
||||
gameId: 'PPSA10001',
|
||||
rootPath: '/mnt/sandbox/pfsmnt/G001',
|
||||
lastSeenAt: outdatedSeenAt,
|
||||
});
|
||||
seedConsoleInstall({
|
||||
gameId: 'PPSA10002',
|
||||
rootPath: '/mnt/sandbox/pfsmnt/G002',
|
||||
lastSeenAt: outdatedSeenAt,
|
||||
});
|
||||
seedConsoleInstall({
|
||||
gameId: 'PPSA10003',
|
||||
rootPath: '/mnt/data/app/G003',
|
||||
lastSeenAt: outdatedSeenAt,
|
||||
});
|
||||
|
||||
const result = reconcileConsoleScan(database, scanResultFixture({
|
||||
generation: 1,
|
||||
games: [{ game_id: 'PPSA10001', ftp_path: '/mnt/sandbox/pfsmnt/G001', scan_root: '/mnt/sandbox/pfsmnt/', version: '1.00' }],
|
||||
count: 1,
|
||||
root_coverage: [
|
||||
{ root: '/mnt/sandbox/pfsmnt/', coverage_state: 'complete' },
|
||||
],
|
||||
}));
|
||||
|
||||
const byTitle = Object.fromEntries(consoleInstallRows().map(row => [row.title_id, row]));
|
||||
assert.equal(result.applied, true);
|
||||
assert.equal(result.upserted, 1);
|
||||
assert.equal(result.cleared, 1);
|
||||
assert.equal(byTitle.PPSA10001.install_state, 'observed');
|
||||
assert.equal(byTitle.PPSA10002.install_state, 'absent');
|
||||
assert.equal(byTitle.PPSA10003.install_state, 'observed');
|
||||
assert.equal(database.runQuery('SELECT COUNT(*) AS c FROM scan_runs')[0]?.c || 0, 1);
|
||||
assert.equal(database.runQuery('SELECT COUNT(*) AS c FROM scan_root_coverage')[0]?.c || 0, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('partial/inaccessible roots preserve existing observed installs', () => {
|
||||
return withTempDatabase((reconcileConsoleScan) => {
|
||||
seedConsoleInstall({
|
||||
gameId: 'PPSA20001',
|
||||
rootPath: '/mnt/sandbox/pfsmnt/G001',
|
||||
lastSeenAt: outdatedSeenAt,
|
||||
});
|
||||
seedConsoleInstall({
|
||||
gameId: 'PPSA20002',
|
||||
rootPath: '/mnt/data/app/G002',
|
||||
lastSeenAt: outdatedSeenAt,
|
||||
});
|
||||
|
||||
const result = reconcileConsoleScan(database, scanResultFixture({
|
||||
generation: 1,
|
||||
started_at: '2026-07-21T12:10:00.000Z',
|
||||
completed_at: '2026-07-21T12:10:01.000Z',
|
||||
root_coverage: [
|
||||
{ root: '/mnt/sandbox/pfsmnt/', coverage_state: 'complete' },
|
||||
{ root: '/mnt/data/app/', coverage_state: 'partial' },
|
||||
{ root: '/user/app/', coverage_state: 'inaccessible' },
|
||||
],
|
||||
games: [{ game_id: 'PPSA20001', ftp_path: '/mnt/sandbox/pfsmnt/G001', scan_root: '/mnt/sandbox/pfsmnt/', version: '1.00' }],
|
||||
count: 1,
|
||||
}));
|
||||
|
||||
const byTitle = Object.fromEntries(consoleInstallRows().map(row => [row.title_id, row]));
|
||||
assert.equal(result.applied, true);
|
||||
assert.equal(result.cleared, 0);
|
||||
assert.equal(byTitle.PPSA20001.install_state, 'observed');
|
||||
assert.equal(byTitle.PPSA20002.install_state, 'observed');
|
||||
});
|
||||
});
|
||||
|
||||
test('empty complete root clears all matching installs in that root', () => {
|
||||
return withTempDatabase((reconcileConsoleScan) => {
|
||||
seedConsoleInstall({
|
||||
gameId: 'PPSA30001',
|
||||
rootPath: '/mnt/sandbox/pfsmnt/G001',
|
||||
lastSeenAt: outdatedSeenAt,
|
||||
});
|
||||
|
||||
const result = reconcileConsoleScan(database, scanResultFixture({
|
||||
generation: 1,
|
||||
started_at: '2026-07-21T12:20:00.000Z',
|
||||
completed_at: '2026-07-21T12:20:01.000Z',
|
||||
root_coverage: [{ root: '/mnt/sandbox/pfsmnt/', coverage_state: 'complete' }],
|
||||
games: [],
|
||||
count: 0,
|
||||
}));
|
||||
|
||||
const row = database.runQuery(`
|
||||
SELECT install_state FROM console_installs ci
|
||||
JOIN titles t ON t.id = ci.title_id
|
||||
WHERE t.title_id = 'PPSA30001'
|
||||
`)[0];
|
||||
assert.equal(result.applied, true);
|
||||
assert.equal(result.cleared, 1);
|
||||
assert.equal(row.install_state, 'absent');
|
||||
});
|
||||
});
|
||||
|
||||
test('multiple observed locations are kept as separate local copies', () => {
|
||||
return withTempDatabase((reconcileConsoleScan) => {
|
||||
const result = reconcileConsoleScan(database, scanResultFixture({
|
||||
generation: 1,
|
||||
started_at: '2026-07-21T12:30:00.000Z',
|
||||
completed_at: '2026-07-21T12:30:01.000Z',
|
||||
root_coverage: [
|
||||
{ root: '/mnt/sandbox/pfsmnt/', coverage_state: 'complete' },
|
||||
{ root: '/user/app/', coverage_state: 'complete' },
|
||||
],
|
||||
games: [
|
||||
{ game_id: 'PPSA40001', ftp_path: '/mnt/sandbox/pfsmnt/GameOne', scan_root: '/mnt/sandbox/pfsmnt/', version: '1.00' },
|
||||
{ game_id: 'PPSA40001', ftp_path: '/user/app/GameOne', scan_root: '/user/app/', version: '1.00' },
|
||||
],
|
||||
count: 2,
|
||||
}));
|
||||
const titleId = Number(database.runQuery(`SELECT id FROM titles WHERE title_id = 'PPSA40001'`)[0]?.id || 0);
|
||||
const copies = database.runQuery(`
|
||||
SELECT COUNT(*) AS c FROM console_installs
|
||||
WHERE title_id = ${titleId}
|
||||
`)[0]?.c || 0;
|
||||
|
||||
assert.equal(result.applied, true);
|
||||
assert.equal(result.upserted, 2);
|
||||
assert.equal(copies, 2);
|
||||
assert.equal(result.cleared, 0);
|
||||
});
|
||||
});
|
||||
|
||||
test('registered managed homebrew IDs are reconciled while unknown nonstandard IDs remain ignored', () => {
|
||||
return withTempDatabase((reconcileConsoleScan) => {
|
||||
seedTitle('FAKE00000');
|
||||
const result = reconcileConsoleScan(database, scanResultFixture({
|
||||
generation: 1,
|
||||
root_coverage: [{ root: '/user/app/', coverage_state: 'complete' }],
|
||||
games: [
|
||||
{ game_id: 'FAKE00000', title: 'Managed launcher', ftp_path: '/user/app/FAKE00000', scan_root: '/user/app/', version: '01.000.000' },
|
||||
{ game_id: 'EVIL00001', title: 'Unregistered', ftp_path: '/user/app/EVIL00001', scan_root: '/user/app/', version: '1.00' },
|
||||
],
|
||||
count: 2,
|
||||
}));
|
||||
const rows = consoleInstallRows();
|
||||
assert.equal(result.upserted, 1);
|
||||
assert.equal(rows.some(row => row.title_id === 'FAKE00000' && row.install_state === 'observed'), true);
|
||||
assert.equal(database.runQuery("SELECT COUNT(*) AS c FROM titles WHERE title_id='EVIL00001'")[0].c, 0);
|
||||
});
|
||||
});
|
||||
|
||||
test('outdated scan generation cannot override newer evidence', () => {
|
||||
return withTempDatabase((reconcileConsoleScan) => {
|
||||
const olderPath = '/user/app/OldPath';
|
||||
const newerPath = '/mnt/sandbox/pfsmnt/GamePath';
|
||||
seedConsoleInstall({
|
||||
gameId: 'PPSA50001',
|
||||
rootPath: olderPath,
|
||||
installState: 'observed',
|
||||
installedVersion: '0.90',
|
||||
lastSeenAt: Date.parse('2026-07-21T12:40:00.000Z'),
|
||||
});
|
||||
|
||||
const latest = reconcileConsoleScan(database, scanResultFixture({
|
||||
generation: 2,
|
||||
started_at: '2026-07-21T12:40:10.000Z',
|
||||
completed_at: '2026-07-21T12:40:11.000Z',
|
||||
root_coverage: [{ root: '/mnt/sandbox/pfsmnt/', coverage_state: 'complete' }],
|
||||
games: [{ game_id: 'PPSA50001', ftp_path: newerPath, scan_root: '/mnt/sandbox/pfsmnt/', version: '1.00' }],
|
||||
count: 1,
|
||||
}));
|
||||
|
||||
const afterLatest = database.runQuery(`
|
||||
SELECT install_state, remote_location_key FROM console_installs ci
|
||||
JOIN titles t ON t.id = ci.title_id
|
||||
WHERE t.title_id = 'PPSA50001'
|
||||
ORDER BY ci.id DESC
|
||||
LIMIT 1
|
||||
`)[0];
|
||||
assert.equal(latest.applied, true);
|
||||
assert.equal(afterLatest.remote_location_key, `legacy:${newerPath}`);
|
||||
|
||||
const stale = reconcileConsoleScan(database, scanResultFixture({
|
||||
generation: 1,
|
||||
started_at: '2026-07-21T12:41:00.000Z',
|
||||
completed_at: '2026-07-21T12:41:01.000Z',
|
||||
root_coverage: [{ root: '/user/app/', coverage_state: 'complete' }],
|
||||
games: [{ game_id: 'PPSA50001', ftp_path: '/user/app/OldPath', scan_root: '/user/app/', version: '0.50' }],
|
||||
count: 1,
|
||||
}));
|
||||
|
||||
const afterStale = database.runQuery(`
|
||||
SELECT remote_location_key, install_state FROM console_installs ci
|
||||
JOIN titles t ON t.id = ci.title_id
|
||||
WHERE t.title_id = 'PPSA50001'
|
||||
ORDER BY ci.id DESC
|
||||
LIMIT 1
|
||||
`)[0];
|
||||
assert.equal(stale.applied, false);
|
||||
assert.equal(stale.reason, 'outdated-generation');
|
||||
assert.equal(afterStale.remote_location_key, `legacy:${newerPath}`);
|
||||
assert.equal(afterStale.install_state, 'observed');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user