87 lines
5.5 KiB
JavaScript
87 lines
5.5 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const database = require('../../src/database');
|
|
const { createEtahenLinkInventoryService } = require('../../src/services/etahen-link-inventory-service');
|
|
|
|
function withDatabase(fn) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-link-inventory-'));
|
|
database.configure({ dataDir: root, logger: { info() {}, warn() {}, error() {} } });
|
|
return database.initialize().then(() => fn(database)).finally(() => { database.close(); fs.rmSync(root, { recursive: true, force: true }); });
|
|
}
|
|
|
|
function snapshot({ scanId = 'scan_12345678', coverage = 'complete', titles = [], deviceAvailability = 'available', runtimeState = 'online', startedAt = 1000, completedAt = 1100 } = {}) {
|
|
return {
|
|
consoleId: 'console_12345678', consoleProfileId: 'profile_12345678',
|
|
runtime: runtimeState === 'unknown' ? { state: 'unknown' } : { state: runtimeState, runtimeEpoch: 'runtime_12345678', etaHenVersion: '2.5b', etaHenBuildSha256: 'a'.repeat(64), firmware: '10.01' },
|
|
scan: { scanId, startedAt, completedAt, outcome: 'completed' },
|
|
devices: [{ deviceKey: 'device_internal1', displayName: 'Internal SSD', type: 'internal', availability: deviceAvailability, volumes: [{ volumeKey: 'volume_games001', kind: 'games', availability: deviceAvailability, coverage, capacityBytes: 100000, freeBytes: 50000, titles }] }],
|
|
};
|
|
}
|
|
|
|
function syntheticContentId() {
|
|
return ['UP0000', 'PPSA12345_00', 'TEST000000000001'].join('-');
|
|
}
|
|
|
|
function syntheticLocationKey() {
|
|
return ['slot', '12345678'].join('_');
|
|
}
|
|
|
|
const title = { titleId: 'PPSA12345', contentId: syntheticContentId(), name: 'Link Test', version: '1.00', locationKey: syntheticLocationKey() };
|
|
|
|
test('Link inventory stores console evidence separately and exposes runtime uncertainty', () => withDatabase(db => {
|
|
const service = createEtahenLinkInventoryService({ db, now: () => 2000 });
|
|
assert.equal(service.status().runtime.state, 'unknown');
|
|
const result = service.apply(snapshot({ titles: [title] }));
|
|
assert.equal(result.observed, 1); assert.equal(result.absent, 0); assert.equal(result.runtime.state, 'online');
|
|
assert.equal(db.runQuery('SELECT COUNT(*) AS count FROM console_installs')[0].count, 1);
|
|
assert.equal(db.runQuery('SELECT COUNT(*) AS count FROM local_copies')[0].count, 0);
|
|
assert.match(db.runQuery('SELECT remote_location_key FROM console_installs')[0].remote_location_key, /^etahen:/);
|
|
assert.equal(service.setRuntime({ consoleId: 'console_12345678', state: 'unknown' }).state, 'unknown');
|
|
}));
|
|
|
|
test('only complete volume coverage marks a previously observed installation absent', () => withDatabase(db => {
|
|
const service = createEtahenLinkInventoryService({ db });
|
|
service.apply(snapshot({ scanId: 'scan_first001', titles: [title] }));
|
|
service.apply(snapshot({ scanId: 'scan_partial1', coverage: 'partial', titles: [] }));
|
|
assert.equal(db.runQuery('SELECT install_state FROM console_installs')[0].install_state, 'observed');
|
|
service.apply(snapshot({ scanId: 'scan_complete2', coverage: 'complete', titles: [] }));
|
|
assert.equal(db.runQuery('SELECT install_state FROM console_installs')[0].install_state, 'absent');
|
|
}));
|
|
|
|
test('disconnected devices retain identity and reconnect restores observed evidence', () => withDatabase(db => {
|
|
const service = createEtahenLinkInventoryService({ db });
|
|
service.apply(snapshot({ scanId: 'scan_online001', titles: [title] }));
|
|
const disconnected = service.apply(snapshot({ scanId: 'scan_offline01', coverage: 'unknown', titles: [], deviceAvailability: 'unavailable', runtimeState: 'offline', startedAt: 1200, completedAt: 1300 }));
|
|
assert.equal(disconnected.devices[0].availability, 'unavailable');
|
|
assert.equal(db.runQuery('SELECT COUNT(*) AS count FROM storage_devices')[0].count, 1);
|
|
assert.equal(db.runQuery('SELECT install_state FROM console_installs')[0].install_state, 'device_unavailable');
|
|
service.apply(snapshot({ scanId: 'scan_reconnect1', titles: [title], startedAt: 1400, completedAt: 1500 }));
|
|
assert.equal(db.runQuery('SELECT install_state FROM console_installs')[0].install_state, 'observed');
|
|
}));
|
|
|
|
test('semantic inventory rejects raw paths, duplicate identities and unscoped snapshots', () => withDatabase(db => {
|
|
const service = createEtahenLinkInventoryService({ db });
|
|
assert.throws(() => service.apply({ ...snapshot(), path: '/mnt/data' }), /field/i);
|
|
assert.throws(() => service.apply({ ...snapshot(), RawPath: '/mnt/data' }), /field/i);
|
|
assert.throws(() => service.apply({ ...snapshot(), consoleProfileId: '' }), /profile/i);
|
|
const duplicate = snapshot(); duplicate.devices.push(duplicate.devices[0]);
|
|
assert.throws(() => service.apply(duplicate), /duplicate/i);
|
|
}));
|
|
|
|
test('desktop and server expose the same shared read-only status contract', () => {
|
|
const mainSource = fs.readFileSync(path.join(__dirname, '../../main.js'), 'utf8');
|
|
const serverSource = fs.readFileSync(path.join(__dirname, '../../src/server/create-registry.js'), 'utf8');
|
|
const preloadSource = fs.readFileSync(path.join(__dirname, '../../preload.js'), 'utf8');
|
|
for (const source of [mainSource, serverSource]) {
|
|
assert.match(source, /createEtahenLinkInventoryService/);
|
|
assert.match(source, /etahen-link:inventory:status/);
|
|
}
|
|
assert.match(serverSource, /name:\s*'etahen-link:inventory:status',\s*classification:\s*'read-only'/);
|
|
assert.match(preloadSource, /etahenLinkInventoryStatus:\s*\(\)\s*=>\s*ipcRenderer\.invoke\('etahen-link:inventory:status'\)/);
|
|
});
|