'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 { createV3Repositories, sanitizePublicLocalCopy, sanitizePublicConsoleInstall, } = require('../../src/services/v3-repositories'); function withTempDatabase(fn) { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-v3-repositories-')); database.configure({ dataDir: root, logger: { info() {}, warn() {}, error() {}, }, }); return database.initialize() .then(() => fn()) .finally(() => { database.close(); fs.rmSync(root, { recursive: true, force: true }); }); } function seedCoreRows() { database.runMutatingSqlWithParams(` INSERT INTO titles (platform, title_id, content_id, name, sort_name, region, metadata_source) VALUES ('ps5', 'PPSA00001', 'content-01', 'Repo Test', 'Repo Test', 'US', 'tests') `); const title = Number(database.runQuery('SELECT last_insert_rowid() AS id')[0]?.id || 0); database.runMutatingSqlWithParams(` INSERT INTO storage_devices (scope, stable_fingerprint, display_name, device_type, availability_state) VALUES ('server', 'test-device', 'Test Device', 'server', 'available') `); const device = Number(database.runQuery('SELECT last_insert_rowid() AS id')[0]?.id || 0); database.runMutatingSqlWithParams(` INSERT INTO storage_volumes (device_id, stable_volume_key, mount_path, can_copy, can_play, can_install) VALUES (@deviceId, 'test-volume', '/test-volume', 1, 1, 1) `, { deviceId: device }); const volume = Number(database.runQuery('SELECT last_insert_rowid() AS id')[0]?.id || 0); return { title, volume }; } function rowCount(table) { return Number(database.runQuery(`SELECT COUNT(*) AS c FROM ${table}`)[0]?.c || 0); } test('local-copy repository writes only local copies and returns sanitized public model', () => { return withTempDatabase(() => { const { title, volume } = seedCoreRows(); const { localCopies } = createV3Repositories(database); const result = localCopies.upsertLocalCopy({ titleId: title, volumeId: volume, relativePath: 'games/TestFolder', copyType: 'folder', baseVersion: '1.00', versionSource: 'legacy', generation: 10, }); assert.equal(result.ok, true); assert.equal(result.reason, 'inserted'); assert.equal(sanitizePublicLocalCopy(result.copy).copyType, 'folder'); assert.equal(rowCount('console_installs'), 0); assert.equal(rowCount('local_copies'), 1); }); }); test('console-install repository writes only console installs and returns sanitized public model', () => { return withTempDatabase(() => { const { title, volume } = seedCoreRows(); const { consoleInstalls } = createV3Repositories(database); const result = consoleInstalls.upsertConsoleInstall({ titleId: title, volumeId: volume, consoleProfileId: 'profile-A', remoteLocationKey: 'ux0:/app0/PPSA00001', installedVersion: '1.00', installState: 'observed', versionSource: 'scan', evidenceConfidence: 'medium', generation: 5, }); assert.equal(result.ok, true); assert.equal(result.reason, 'inserted'); assert.equal(sanitizePublicConsoleInstall(result.install).installState, 'observed'); assert.equal(rowCount('local_copies'), 0); assert.equal(rowCount('console_installs'), 1); }); }); test('generation-aware upserts prevent stale local copy evidence overwrite', () => { return withTempDatabase(() => { const { title, volume } = seedCoreRows(); const { localCopies } = createV3Repositories(database); localCopies.upsertLocalCopy({ titleId: title, volumeId: volume, relativePath: 'games/TestFolder', copyType: 'folder', baseVersion: '1.00', generation: 10, }); const stale = localCopies.upsertLocalCopy({ titleId: title, volumeId: volume, relativePath: 'games/TestFolder', copyType: 'folder', baseVersion: '2.00', generation: 9, }); assert.equal(stale.ok, false); assert.equal(stale.reason, 'outdated-evidence'); const current = localCopies.upsertLocalCopy({ titleId: title, volumeId: volume, relativePath: 'games/TestFolder', copyType: 'folder', baseVersion: '2.00', generation: 20, }); assert.equal(current.ok, true); assert.equal(current.reason, 'updated'); assert.equal(current.copy.baseVersion, '2.00'); }); }); test('generation-aware upserts prevent stale console install overwrite', () => { return withTempDatabase(() => { const { title, volume } = seedCoreRows(); const { consoleInstalls } = createV3Repositories(database); consoleInstalls.upsertConsoleInstall({ titleId: title, volumeId: volume, consoleProfileId: 'profile-A', remoteLocationKey: 'ux0:/app0/PPSA00001', installedVersion: '1.00', installState: 'observed', generation: 10, }); const stale = consoleInstalls.upsertConsoleInstall({ titleId: title, volumeId: volume, consoleProfileId: 'profile-A', remoteLocationKey: 'ux0:/app0/PPSA00001', installedVersion: '0.99', installState: 'absent', generation: 9, }); assert.equal(stale.ok, false); assert.equal(stale.reason, 'outdated-evidence'); const fresh = consoleInstalls.upsertConsoleInstall({ titleId: title, volumeId: volume, consoleProfileId: 'profile-A', remoteLocationKey: 'ux0:/app0/PPSA00001', installedVersion: '1.01', installState: 'observed', generation: 20, }); assert.equal(fresh.ok, true); assert.equal(fresh.reason, 'updated'); assert.equal(fresh.install.installedVersion, '1.01'); }); }); test('evidence writes are persisted with source/time/confidence metadata', () => { return withTempDatabase(() => { const { title, volume } = seedCoreRows(); const { consoleInstalls } = createV3Repositories(database); const generation = Date.now(); const result = consoleInstalls.upsertConsoleInstall({ titleId: title, volumeId: volume, consoleProfileId: 'profile-A', remoteLocationKey: 'ux0:/app0/PPSA00001', installedVersion: '1.00', installState: 'observed', versionSource: 'legacy-console', evidenceConfidence: 'high', generation, }); const install = database.runQuery(` SELECT version_evidence_id, installed_version FROM console_installs WHERE id = ${result.install.id} `)[0]; assert.equal(install.installed_version, '1.00'); assert.ok(install.version_evidence_id > 0); const evidence = database.runQuery(` SELECT subject_type, subject_id, semantic_version, version_source, confidence, observed_at FROM version_evidence WHERE id = ${install.version_evidence_id} `)[0]; assert.equal(evidence.subject_type, 'console_install'); assert.equal(evidence.subject_id, title); assert.equal(evidence.semantic_version, '1.00'); assert.equal(evidence.version_source, 'legacy-console'); assert.equal(evidence.confidence, 'high'); assert.equal(Number(evidence.observed_at), generation); }); }); test('public models are sanitized for invalid external fields', () => { const sanitizedCopy = sanitizePublicLocalCopy({ id: 'bad-id', title_id: '42', volume_id: '100', relative_path: ' games/Test ', copy_type: 'not-a-type', base_version: 1.23, version_source: null, size_bytes: 'abc', scan_run_id: null, last_seen_at: 'bad-ts', availability_state: 'nonsense', }); const sanitizedInstall = sanitizePublicConsoleInstall({ id: 'bad-id', title_id: '42', console_profile_id: ' profile-legacy', volume_id: 3, remote_location_key: 'ux0:/app0', installed_version: 4, install_state: 'invalid-state', first_seen_at: 'bad', last_seen_at: 'bad', }); assert.equal(sanitizedCopy.copyType, 'unknown'); assert.equal(sanitizedCopy.availabilityState, 'unknown'); assert.equal(sanitizedCopy.relativePath, 'games/Test'); assert.equal(typeof sanitizedCopy.sizeBytes, 'number'); assert.equal(sanitizedCopy.availabilityState, 'unknown'); assert.equal(sanitizedInstall.installState, 'unknown'); assert.equal(typeof sanitizedInstall.firstSeenAt, 'number'); assert.equal(typeof sanitizedInstall.lastSeenAt, 'number'); });