'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 } = require('../../src/services/v3-repositories'); const { createV3DerivedViews } = require('../../src/services/v3-derived-views'); function withTempDatabase(fn) { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-v3-derived-views-')); database.configure({ dataDir: root, logger: { info() {}, warn() {}, error() {}, }, }); return database.initialize() .then(() => fn()) .finally(() => { database.close(); fs.rmSync(root, { recursive: true, force: true }); }); } function createVolume({ deviceFingerprint = 'device-A', volumeKey = 'volume-A', mountPath = '/media/a', displayName = 'Device A', deviceState = 'available' }) { database.runMutatingSqlWithParams(` INSERT INTO storage_devices (scope, stable_fingerprint, display_name, device_type, availability_state) VALUES ('server', @fingerprint, @displayName, 'server', @state) ON CONFLICT(scope, stable_fingerprint) DO UPDATE SET display_name = excluded.display_name, availability_state = excluded.availability_state `, { fingerprint: deviceFingerprint, displayName, state: deviceState, }); const deviceId = Number(database.runQuery(`SELECT id FROM storage_devices WHERE scope = 'server' AND stable_fingerprint = '${deviceFingerprint}'`)[0]?.id || 0); database.runMutatingSqlWithParams(` INSERT INTO storage_volumes (device_id, stable_volume_key, mount_path, can_copy, can_play, can_install) VALUES (@deviceId, @volumeKey, @mountPath, 1, 1, 1) `, { deviceId, volumeKey, mountPath, }); const volumeId = Number(database.runQuery('SELECT last_insert_rowid() AS id')[0]?.id || 0); return { deviceId, volumeId }; } function createTitle({ titleId = 'PPSA00000', contentId = 'content-000', name = 'Title' }) { database.runMutatingSqlWithParams(` INSERT INTO titles (platform, title_id, content_id, name, sort_name, region, metadata_source) VALUES ('ps5', @titleId, @contentId, @name, @name, 'US', 'tests') `, { titleId, contentId, name, }); return Number(database.runQuery('SELECT last_insert_rowid() AS id')[0]?.id || 0); } function getFirst(rowSet, titleId) { return rowSet.find((row) => row.titleId === titleId) || null; } test('derived state matrix', () => { return withTempDatabase(() => { const { volumeId } = createVolume({ deviceFingerprint: 'device-1', volumeKey: 'volume-1', mountPath: '/device-1' }); const { consoleInstalls } = createV3Repositories(database); const { localCopies } = createV3Repositories(database); const views = createV3DerivedViews(database); const observedTitleId = createTitle({ titleId: 'PPSA10001', contentId: 'c1', name: 'Observed install' }); const staleTitleId = createTitle({ titleId: 'PPSA10002', contentId: 'c2', name: 'Stale install' }); const absentTitleId = createTitle({ titleId: 'PPSA10003', contentId: 'c3', name: 'Absent install' }); const unavailableTitleId = createTitle({ titleId: 'PPSA10004', contentId: 'c4', name: 'Unavailable device install' }); const unconfirmedTitleId = createTitle({ titleId: 'PPSA10005', contentId: 'c5', name: 'Unconfirmed install' }); const copyOnlyTitleId = createTitle({ titleId: 'PPSA10006', contentId: 'c6', name: 'Copy only' }); const observed = consoleInstalls.upsertConsoleInstall({ titleId: observedTitleId, volumeId, consoleProfileId: 'profile-observed', remoteLocationKey: 'ux0:/app0/PPSA10001', installedVersion: '1.00', installState: 'observed', generation: 10, }); assert.equal(observed.ok, true); const stale = consoleInstalls.upsertConsoleInstall({ titleId: staleTitleId, volumeId, consoleProfileId: 'profile-stale', remoteLocationKey: 'ux0:/app0/PPSA10002', installedVersion: '1.10', installState: 'stale', generation: 20, }); assert.equal(stale.ok, true); const absent = consoleInstalls.upsertConsoleInstall({ titleId: absentTitleId, volumeId, consoleProfileId: 'profile-absent', remoteLocationKey: 'ux0:/app0/PPSA10003', installedVersion: '', installState: 'absent', generation: 30, }); assert.equal(absent.ok, true); const unavailable = consoleInstalls.upsertConsoleInstall({ titleId: unavailableTitleId, volumeId, consoleProfileId: 'profile-unavailable', remoteLocationKey: 'ux0:/app0/PPSA10004', installedVersion: '', installState: 'device_unavailable', generation: 40, }); assert.equal(unavailable.ok, true); const unconfirmed = consoleInstalls.upsertConsoleInstall({ titleId: unconfirmedTitleId, volumeId, consoleProfileId: 'profile-unconfirmed', remoteLocationKey: 'ux0:/app0/PPSA10005', installedVersion: '', installState: 'legacy-unverified', generation: 50, }); assert.equal(unconfirmed.ok, true); const copyOnly = localCopies.upsertLocalCopy({ titleId: copyOnlyTitleId, volumeId, relativePath: 'copy/title-six', copyType: 'folder', baseVersion: '1.00', versionSource: 'scan', generation: 60, }); assert.equal(copyOnly.ok, true); const rows = views.listTitleAvailability().reduce((map, row) => { map[row.titleId] = row; return map; }, {}); assert.equal(rows[observedTitleId].derivedState, 'installed'); assert.equal(rows[observedTitleId].confirmedInstallCount, 1); assert.equal(rows[observedTitleId].consoleInstallCount, 1); assert.equal(rows[staleTitleId].derivedState, 'stale'); assert.equal(rows[staleTitleId].staleInstallCount, 1); assert.equal(rows[absentTitleId].derivedState, 'absent'); assert.equal(rows[absentTitleId].absentInstallCount, 1); assert.equal(rows[unavailableTitleId].derivedState, 'device_unavailable'); assert.equal(rows[unavailableTitleId].deviceUnavailableInstallCount, 1); assert.equal(rows[unconfirmedTitleId].derivedState, 'unknown_install_state'); assert.equal(rows[unconfirmedTitleId].confirmedInstallCount, 0); assert.equal(rows[copyOnlyTitleId].derivedState, 'copy_only'); assert.equal(rows[copyOnlyTitleId].localCopyCount, 1); }); }); test('protection diversity', () => { return withTempDatabase(() => { const { volumeId: sameDeviceVolumeA } = createVolume({ deviceFingerprint: 'device-same', volumeKey: 'volume-a', mountPath: '/device-same/a' }); const { volumeId: sameDeviceVolumeB } = createVolume({ deviceFingerprint: 'device-same', volumeKey: 'volume-b', mountPath: '/device-same/b' }); const { volumeId: differentDeviceVolumeA } = createVolume({ deviceFingerprint: 'device-diff-a', volumeKey: 'volume-a', mountPath: '/device-diff/a' }); const { localCopies } = createV3Repositories(database); const views = createV3DerivedViews(database); const sameDeviceTitle = createTitle({ titleId: 'PPSA11000', contentId: 'p1', name: 'Same device copies' }); localCopies.upsertLocalCopy({ titleId: sameDeviceTitle, volumeId: sameDeviceVolumeA, relativePath: 'copy/a1', copyType: 'folder', baseVersion: '1.00', generation: 1, }); localCopies.upsertLocalCopy({ titleId: sameDeviceTitle, volumeId: sameDeviceVolumeB, relativePath: 'copy/a2', copyType: 'folder', baseVersion: '1.00', generation: 2, }); const twoDeviceTitle = createTitle({ titleId: 'PPSA11001', contentId: 'p2', name: 'Different device copies' }); localCopies.upsertLocalCopy({ titleId: twoDeviceTitle, volumeId: sameDeviceVolumeA, relativePath: 'copy/b1', copyType: 'folder', baseVersion: '1.00', generation: 3, }); localCopies.upsertLocalCopy({ titleId: twoDeviceTitle, volumeId: differentDeviceVolumeA, relativePath: 'copy/c1', copyType: 'folder', baseVersion: '1.00', generation: 4, }); const sameDeviceRows = views.listProtectionHealth(sameDeviceTitle).map((row) => row.distinctCopyDeviceCount); const twoDeviceRows = views.listProtectionHealth(twoDeviceTitle).map((row) => row.distinctCopyDeviceCount); const sameDeviceHealth = views.listProtectionHealth(sameDeviceTitle); const twoDeviceHealth = views.listProtectionHealth(twoDeviceTitle); assert.equal(sameDeviceHealth[0].localCopyCount, 2); assert.equal(sameDeviceHealth[0].distinctCopyDeviceCount, 1); assert.equal(sameDeviceHealth[0].protectionHealthy, false); assert.equal(twoDeviceHealth[0].localCopyCount, 2); assert.equal(twoDeviceHealth[0].distinctCopyDeviceCount, 2); assert.equal(twoDeviceHealth[0].protectionHealthy, true); }); }); test('multiple version/location cases', () => { return withTempDatabase(() => { const { volumeId: volumeA } = createVolume({ deviceFingerprint: 'volume-device-a', volumeKey: 'vol-a', mountPath: '/device-a/vol-a' }); const { volumeId: volumeB } = createVolume({ deviceFingerprint: 'volume-device-b', volumeKey: 'vol-b', mountPath: '/device-b/vol-b' }); const { localCopies, consoleInstalls } = createV3Repositories(database); const views = createV3DerivedViews(database); const titleId = createTitle({ titleId: 'PPSA12000', contentId: 'p3', name: 'Multiple location title' }); localCopies.upsertLocalCopy({ titleId, volumeId: volumeA, relativePath: 'copies/main', copyType: 'folder', baseVersion: '1.00', generation: 10, }); localCopies.upsertLocalCopy({ titleId, volumeId: volumeB, relativePath: 'copies/secondary', copyType: 'folder', baseVersion: '1.10', generation: 11, }); const confirmedOld = consoleInstalls.upsertConsoleInstall({ titleId, volumeId: volumeA, consoleProfileId: 'profile-multi-1', remoteLocationKey: 'ux0:/app0/PPSA12000-main', installedVersion: '1.00', installState: 'observed', generation: 100, }); assert.equal(confirmedOld.ok, true); const staleModern = consoleInstalls.upsertConsoleInstall({ titleId, volumeId: volumeB, consoleProfileId: 'profile-multi-2', remoteLocationKey: 'ux0:/app0/PPSA12000-stale', installedVersion: '2.00', installState: 'stale', generation: 20, }); assert.equal(staleModern.ok, true); const confirmedNewer = consoleInstalls.upsertConsoleInstall({ titleId, volumeId: volumeB, consoleProfileId: 'profile-multi-3', remoteLocationKey: 'ux0:/app1/PPSA12000-main', installedVersion: '1.01', installState: 'observed', generation: 50, }); assert.equal(confirmedNewer.ok, true); const summary = views.listTitleInstallSummary(titleId); assert.equal(summary.length, 3); const observedCount = summary.filter((item) => item.isConfirmedInstall).length; assert.equal(observedCount, 2); const availability = views.getTitleAvailability(titleId); assert.equal(availability.consoleInstallCount, 3); assert.equal(availability.confirmedInstallCount, 2); assert.equal(availability.localCopyCount, 2); const updateStatus = views.getTitleUpdateStatus(titleId); assert.equal(updateStatus.confirmedInstallCount, 2); assert.equal(updateStatus.confirmedInstalledVersion, '1.00'); const decision = views.getUpdateDecision({ titleId, latestCompatibleVersion: '1.20', }); assert.equal(decision.updateAvailable, true); assert.equal(decision.comparison, -1); }); });