'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'); }); });