76 lines
3.6 KiB
JavaScript
76 lines
3.6 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
const { createInstallDestinationService } = require('../../src/services/install-destination-service');
|
|
|
|
const now = 10_000_000;
|
|
function volume(overrides = {}) {
|
|
return {
|
|
id: 10,
|
|
stableVolumeKey: 'internal',
|
|
mountPath: '/mnt/sandbox/pfsmnt',
|
|
capacityBytes: 1_000,
|
|
freeBytes: 600,
|
|
capacityEvidence: { source: 'sentinel', observedAt: now - 1_000 },
|
|
capabilities: { canInstall: true },
|
|
capabilityEvidence: { source: 'sentinel', observedAt: now - 1_000 },
|
|
availabilityState: 'available',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function service(current) {
|
|
return createInstallDestinationService({
|
|
now: () => now,
|
|
staleAfterMs: 60_000,
|
|
inventory: { list: () => ({ devices: [{ id: 1, displayName: 'PS5', deviceType: 'internal', availabilityState: 'available', volumes: [current.value] }] }) },
|
|
});
|
|
}
|
|
|
|
test('capacity policy reports sufficient and insufficient projected space', () => {
|
|
const current = { value: volume() };
|
|
const selector = service(current);
|
|
const sufficient = selector.listDestinations({ requiredBytes: 500, titleId: 'PPSA00001' }).destinations[0];
|
|
assert.equal(sufficient.eligible, true);
|
|
assert.equal(sufficient.projectedFreeBytes, 100);
|
|
const insufficient = selector.listDestinations({ requiredBytes: 700, titleId: 'PPSA00001' }).destinations[0];
|
|
assert.equal(insufficient.eligible, false);
|
|
assert.ok(insufficient.reasons.includes('insufficient-capacity'));
|
|
});
|
|
|
|
test('offline volume and disabled install capability explain ineligibility', () => {
|
|
const current = { value: volume({ availabilityState: 'unavailable', capabilities: { canInstall: false } }) };
|
|
const result = service(current).listDestinations({ requiredBytes: 100 }).destinations[0];
|
|
assert.ok(result.reasons.includes('offline-volume'));
|
|
assert.ok(result.reasons.includes('install-capability-disabled'));
|
|
});
|
|
|
|
test('stale telemetry blocks normal destination selection', () => {
|
|
const current = { value: volume({ capacityEvidence: { source: 'sentinel', observedAt: now - 120_000 } }) };
|
|
const result = service(current).listDestinations({ requiredBytes: 100 }).destinations[0];
|
|
assert.equal(result.eligible, false);
|
|
assert.ok(result.reasons.includes('stale-capacity-telemetry'));
|
|
});
|
|
|
|
test('execution resolves the selected volume again and rejects changed evidence', () => {
|
|
const current = { value: volume() };
|
|
const selector = service(current);
|
|
assert.equal(selector.resolveForExecution({ volumeId: 10, requiredBytes: 500, titleId: 'PPSA00001' }).suggestedRemotePath, '/mnt/sandbox/pfsmnt/PPSA00001-app');
|
|
current.value = volume({ freeBytes: 100 });
|
|
assert.throws(() => selector.resolveForExecution({ volumeId: 10, requiredBytes: 500, titleId: 'PPSA00001' }), /no longer eligible.*insufficient-capacity/i);
|
|
});
|
|
|
|
test('normal UI submits volume id and keeps raw path behind Expert details', () => {
|
|
const root = path.join(__dirname, '..', '..');
|
|
const html = fs.readFileSync(path.join(root, 'renderer', 'index.html'), 'utf8');
|
|
const modal = fs.readFileSync(path.join(root, 'renderer', 'modules', 'modal.js'), 'utf8');
|
|
const registry = fs.readFileSync(path.join(root, 'src', 'server', 'create-registry.js'), 'utf8');
|
|
assert.match(html, /<select id="install-destination-volume"/);
|
|
assert.match(html, /<details id="install-expert-destination"[^>]+data-expert-only="true"/);
|
|
assert.match(modal, /destinationVolumeId: destination\.destinationVolumeId/);
|
|
assert.match(registry, /resolveForExecution\(\{ volumeId: input\.destinationVolumeId, requiredBytes, titleId: id \}\)/);
|
|
});
|