81 lines
4.6 KiB
JavaScript
81 lines
4.6 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
const database = require('../../src/database');
|
|
const { createStorageInventoryService } = require('../../src/services/storage-inventory-service');
|
|
|
|
async function fixture(t) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-storage-inventory-'));
|
|
database.configure({ dataDir: root, logger: { info() {}, warn() {}, error() {} } });
|
|
await database.initialize();
|
|
t.after(() => {
|
|
database.close();
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
});
|
|
return createStorageInventoryService({ db: database, now: () => 1_721_584_800_000 });
|
|
}
|
|
|
|
test('disconnect and reconnect preserve stable device and volume identity', async t => {
|
|
const inventory = await fixture(t);
|
|
const snapshot = {
|
|
scope: 'server', adapterId: 'local-statfs', source: 'node-statfs', observedAt: 1000, completeCoverage: true,
|
|
devices: [{ stableFingerprint: 'disk-serial-a', displayName: 'Archive A', deviceType: 'usb', volumes: [{ stableVolumeKey: 'uuid-a', mountPath: '/mnt/a', availabilityState: 'available', capabilities: { canCopy: true, canMount: true } }] }]
|
|
};
|
|
const first = inventory.observe(snapshot);
|
|
const deviceId = first.devices[0].id;
|
|
const volumeId = first.devices[0].volumes[0].id;
|
|
const disconnected = inventory.observe({ ...snapshot, observedAt: 2000, devices: [] });
|
|
assert.equal(disconnected.devices[0].availabilityState, 'unavailable');
|
|
assert.equal(disconnected.devices[0].volumes[0].availabilityState, 'unavailable');
|
|
|
|
const reconnected = inventory.observe({ ...snapshot, observedAt: 3000, devices: [{ ...snapshot.devices[0], volumes: [{ ...snapshot.devices[0].volumes[0], mountPath: '/media/archive-a' }] }] });
|
|
assert.equal(reconnected.devices[0].id, deviceId);
|
|
assert.equal(reconnected.devices[0].volumes[0].id, volumeId);
|
|
assert.equal(reconnected.devices[0].volumes[0].mountPath, '/media/archive-a');
|
|
assert.equal(reconnected.devices[0].availabilityState, 'available');
|
|
});
|
|
|
|
test('unknown console capacity stays null and has no invented provenance', async t => {
|
|
const inventory = await fixture(t);
|
|
const result = inventory.observe({
|
|
scope: 'console', adapterId: 'sentinel', source: 'chimera-sentinel-0.2', observedAt: 4000, completeCoverage: true,
|
|
devices: [{ stableFingerprint: 'ps5-internal-a', displayName: 'PS5 Internal', deviceType: 'internal', volumes: [{ stableVolumeKey: 'internal', mountPath: '/user', capabilities: { canPlay: true } }] }]
|
|
});
|
|
const volume = result.devices[0].volumes[0];
|
|
assert.equal(volume.capacityBytes, null);
|
|
assert.equal(volume.freeBytes, null);
|
|
assert.deepEqual(volume.capacityEvidence, { source: null, observedAt: null });
|
|
assert.equal(volume.capabilities.canPlay, true);
|
|
assert.equal(volume.capabilities.canInstall, null);
|
|
assert.equal(volume.capabilityEvidence.source, 'chimera-sentinel-0.2');
|
|
});
|
|
|
|
test('multiple USB devices remain separate and capacity carries source and time', async t => {
|
|
const inventory = await fixture(t);
|
|
const result = inventory.observe({
|
|
scope: 'console', adapterId: 'sentinel', source: 'chimera-sentinel-0.2', observedAt: 5000, completeCoverage: true,
|
|
devices: [
|
|
{ stableFingerprint: 'usb-serial-a', displayName: 'USB A', deviceType: 'usb', volumes: [{ stableVolumeKey: 'usb-a', mountPath: '/mnt/usb0', capacityBytes: 1000, freeBytes: 400, capacitySource: 'sentinel-statfs' }] },
|
|
{ stableFingerprint: 'usb-serial-b', displayName: 'USB B', deviceType: 'usb', volumes: [{ stableVolumeKey: 'usb-b', mountPath: '/mnt/usb1', capacityBytes: 2000, freeBytes: 1500, capacitySource: 'sentinel-statfs' }] }
|
|
]
|
|
});
|
|
assert.equal(result.devices.length, 2);
|
|
assert.deepEqual(result.devices.map(device => device.stableFingerprint), ['usb-serial-a', 'usb-serial-b']);
|
|
assert.deepEqual(result.devices.map(device => device.volumes[0].capacityEvidence), [
|
|
{ source: 'sentinel-statfs', observedAt: 5000 },
|
|
{ source: 'sentinel-statfs', observedAt: 5000 }
|
|
]);
|
|
});
|
|
|
|
test('incomplete coverage never marks an unobserved device unavailable', async t => {
|
|
const inventory = await fixture(t);
|
|
const base = { scope: 'server', adapterId: 'local-statfs', source: 'node-statfs', observedAt: 6000, completeCoverage: true, devices: [{ stableFingerprint: 'disk-a', volumes: [{ stableVolumeKey: 'vol-a', mountPath: '/a' }] }] };
|
|
inventory.observe(base);
|
|
const result = inventory.observe({ ...base, observedAt: 7000, completeCoverage: false, devices: [] });
|
|
assert.equal(result.devices[0].availabilityState, 'available');
|
|
});
|