Files
chimera-Public/test/server/ch-1401-p15-a8-distinct-control-install-gate.test.js
T

221 lines
13 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const test = require('node:test');
const { A8_DISTINCT_CONTROL } = require('../../src/services/ps5-native-pkg-builder-provider');
const { createPs5FpkgDistinctControlInstallGate } = require('../../src/services/ps5-fpkg-distinct-control-install-gate');
const ACTOR = 'authenticated-session-a';
function fixture(overrides = {}) {
let clock = Date.parse('2026-08-26T16:00:02.000Z');
let starts = 0;
const control = {
status: 'ready', artifactVerified: true, artifactSelection: 'frozen-distribution-artifact',
titleId: A8_DISTINCT_CONTROL.titleId, contentId: A8_DISTINCT_CONTROL.contentId,
absolutePath: '/managed/control.pkg',
size: A8_DISTINCT_CONTROL.selectedArtifact.size,
sha256: A8_DISTINCT_CONTROL.selectedArtifact.sha256,
packageSetId: 91, installed: false, launchVerified: false, productionRestorable: false,
...overrides.control,
};
const packageSet = {
id: 91, platform: 'ps5',
manifest: {
controlProfile: A8_DISTINCT_CONTROL.controlProfile,
title: { platform: 'ps5', titleId: A8_DISTINCT_CONTROL.titleId },
packageFormat: { container: 'FIH', debug: true, retail: false, licenseFree: true },
evidence: { immutableArtifact: { sha256: control.sha256, size: control.size, rebuildSubstitutionAllowed: false } },
},
components: [{ componentId: 'base', type: 'base', contentId: control.contentId, adapterHint: A8_DISTINCT_CONTROL.adapterHint, size: control.size, sha256: control.sha256, metadata: { absolutePath: '/managed/control.pkg' } }],
...overrides.packageSet,
};
const scan = overrides.scan || {
scan_id: 'ftp-12-10000', started_at: '2026-08-26T16:00:00.000Z', completed_at: '2026-08-26T16:00:01.000Z',
root_coverage: [
{ root: '/user/app/', coverage_state: 'complete', observed_count: 2 },
{ root: '/mnt/data/app/', coverage_state: 'complete', observed_count: 3 },
],
games: [{ game_id: 'PPSA00001' }, { game_id: 'FAKE00000' }], count: 2,
};
const gate = createPs5FpkgDistinctControlInstallGate({
descriptor: A8_DISTINCT_CONTROL,
now: () => clock,
ttlMs: 120_000,
controlStatus: () => control,
resolvePackageSet: id => Number(id) === packageSet.id ? packageSet : null,
scanConsole: overrides.scanConsole || (async () => scan),
startInstall: overrides.startInstall || (async input => {
starts += 1;
assert.equal(input.packageSet, packageSet);
assert.equal(input.titleId, A8_DISTINCT_CONTROL.titleId);
assert.equal(input.preflightEvidence.schema, A8_DISTINCT_CONTROL.planSchema);
assert.equal(input.preflightEvidence.titleId, A8_DISTINCT_CONTROL.titleId);
assert.equal(input.preflightEvidence.targetAbsent, true);
assert.equal(input.preflightEvidence.coverageComplete, true);
assert.match(input.preflightEvidence.planDigest, /^[a-f0-9]{64}$/);
assert.equal(input.preflightEvidence.scan.scanId, 'ftp-12-10000');
return { id: 'operation-a8-control', status: 'preparing', installed: false };
}),
});
return { gate, control, packageSet, starts: () => starts, advance: ms => { clock += ms; } };
}
test('complete target-absent scan freezes a bounded single-use control install plan', async () => {
const f = fixture();
const plan = await f.gate.plan(ACTOR);
await assert.rejects(() => f.gate.plan('authenticated-session-b'), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_BUSY');
assert.equal(plan.schema, A8_DISTINCT_CONTROL.planSchema);
assert.equal(plan.titleId, A8_DISTINCT_CONTROL.titleId);
assert.equal(plan.packageSetId, 91);
assert.equal(plan.targetAbsent, true);
assert.equal(plan.coverageComplete, true);
assert.equal(plan.grantsInstall, false);
assert.equal(plan.installed, false);
assert.equal(plan.launchVerified, false);
assert.equal(plan.productionRestorable, false);
assert.equal(plan.confirmation, A8_DISTINCT_CONTROL.confirmation);
assert.match(plan.planDigest, /^[a-f0-9]{64}$/);
assert.equal(f.starts(), 0);
assert.equal(Object.isFrozen(plan), true);
});
test('incomplete coverage and an observed target fail closed before authorization', async () => {
const partial = fixture({ scan: {
scan_id: 'ftp-partial', completed_at: '2026-08-26T16:00:00.000Z', games: [], count: 0,
root_coverage: [{ root: '/user/app/', coverage_state: 'complete' }, { root: '/mnt/data/app/', coverage_state: 'inaccessible' }],
} });
await assert.rejects(() => partial.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_SCAN_INCOMPLETE');
const present = fixture({ scan: {
scan_id: 'ftp-present', completed_at: '2026-08-26T16:00:00.000Z', count: 1,
root_coverage: [{ root: '/user/app/', coverage_state: 'complete' }, { root: '/mnt/data/app/', coverage_state: 'complete' }],
games: [{ game_id: A8_DISTINCT_CONTROL.titleId }],
} });
await assert.rejects(() => present.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_TARGET_PRESENT');
});
test('a parent-proven absent canonical namespace is complete while unproven absence fails closed', async () => {
const absent = fixture({ scan: {
scan_id: 'ftp-absent-root', started_at: '2026-08-26T16:00:00.000Z', completed_at: '2026-08-26T16:00:01.000Z', games: [],
root_coverage: [
{ root: '/user/app/', coverage_state: 'complete', namespace_state: 'present', coverage_evidence: 'direct-root-listing' },
{ root: '/mnt/data/app/', coverage_state: 'complete', namespace_state: 'absent', coverage_evidence: 'complete-parent-listing' },
],
} });
const plan = await absent.gate.plan(ACTOR);
assert.equal(plan.scan.roots[1].namespaceState, 'absent');
assert.equal(plan.scan.roots[1].coverageEvidence, 'complete-parent-listing');
const unproven = fixture({ scan: {
scan_id: 'ftp-unproven-absent', started_at: '2026-08-26T16:00:00.000Z', completed_at: '2026-08-26T16:00:01.000Z', games: [],
root_coverage: [
{ root: '/user/app/', coverage_state: 'complete' },
{ root: '/mnt/data/app/', coverage_state: 'complete', namespace_state: 'absent' },
],
} });
await assert.rejects(() => unproven.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_SCAN_INCOMPLETE');
const ancestorAbsent = fixture({ scan: {
scan_id: 'ftp-ancestor-absent', started_at: '2026-08-26T16:00:00.000Z', completed_at: '2026-08-26T16:00:01.000Z', games: [],
root_coverage: [
{ root: '/user/app/', coverage_state: 'complete', namespace_state: 'present', coverage_evidence: 'direct-root-listing' },
{ root: '/mnt/data/app/', coverage_state: 'complete', namespace_state: 'absent', coverage_evidence: 'complete-ancestor-listing', evidence_root: '/mnt/', missing_segment: 'data' },
],
} });
const ancestorPlan = await ancestorAbsent.gate.plan(ACTOR);
assert.equal(ancestorPlan.scan.roots[1].coverageEvidence, 'complete-ancestor-listing');
assert.equal(ancestorPlan.scan.roots[1].evidenceRoot, '/mnt/');
assert.equal(ancestorPlan.scan.roots[1].missingSegment, 'data');
});
test('custom or duplicate roots cannot substitute for canonical install-root coverage', async () => {
const custom = fixture({ scan: {
scan_id: 'ftp-custom', started_at: '2026-08-26T16:00:00.000Z', completed_at: '2026-08-26T16:00:01.000Z', games: [],
root_coverage: [{ root: '/user/custom/', coverage_state: 'complete' }],
} });
await assert.rejects(() => custom.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_SCAN_SCOPE');
const duplicate = fixture({ scan: {
scan_id: 'ftp-duplicate', started_at: '2026-08-26T16:00:00.000Z', completed_at: '2026-08-26T16:00:01.000Z', games: [],
root_coverage: [{ root: '/user/app/', coverage_state: 'complete' }, { root: '/user/app', coverage_state: 'complete' }, { root: '/mnt/data/app/', coverage_state: 'complete' }],
} });
await assert.rejects(() => duplicate.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_SCAN_SCOPE');
});
test('exact digest and confirmation are mandatory and the plan is consumed before install', async () => {
const f = fixture();
const plan = await f.gate.plan(ACTOR);
await assert.rejects(() => f.gate.start({ planDigest: '0'.repeat(64), confirmation: plan.confirmation }, ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_MISMATCH');
await assert.rejects(() => f.gate.start({ planDigest: plan.planDigest, confirmation: 'INSTALL' }, ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_CONFIRMATION');
await assert.rejects(() => f.gate.start({ planDigest: plan.planDigest, confirmation: plan.confirmation }, 'authenticated-session-b'), error => error.code === 'PS5_A8_CONTROL_INSTALL_ACTOR_MISMATCH');
assert.equal(f.starts(), 0);
const result = await f.gate.start({ planDigest: plan.planDigest, confirmation: plan.confirmation }, ACTOR);
assert.equal(result.operationId, 'operation-a8-control');
assert.equal(result.installed, false);
assert.equal(result.postScanRequired, true);
assert.equal(result.launchVerified, false);
assert.equal(result.productionRestorable, false);
assert.equal(f.starts(), 1);
await assert.rejects(() => f.gate.start({ planDigest: plan.planDigest, confirmation: plan.confirmation }, ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_REQUIRED');
assert.equal(f.starts(), 1);
});
test('expired plans and artifact or package-set drift cannot start installation', async () => {
const expired = fixture();
const expiredPlan = await expired.gate.plan(ACTOR);
expired.advance(120_001);
await assert.rejects(() => expired.gate.start({ planDigest: expiredPlan.planDigest, confirmation: expiredPlan.confirmation }, ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_EXPIRED');
const drift = fixture();
const driftPlan = await drift.gate.plan(ACTOR);
drift.control.sha256 = 'f'.repeat(64);
await assert.rejects(() => drift.gate.start({ planDigest: driftPlan.planDigest, confirmation: driftPlan.confirmation }, ACTOR), error => error.code === 'PS5_A8_CONTROL_ARTIFACT_DRIFT');
assert.equal(drift.starts(), 0);
const boundary = fixture();
const boundaryPlan = await boundary.gate.plan(ACTOR);
boundary.advance(120_000);
await assert.rejects(() => boundary.gate.start({ planDigest: boundaryPlan.planDigest, confirmation: boundaryPlan.confirmation }, ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_EXPIRED');
});
test('an installer failure still consumes the single-use plan', async () => {
const f = fixture({ startInstall: async () => { throw Object.assign(new Error('provider failed'), { code: 'PROVIDER_FAILED' }); } });
const plan = await f.gate.plan(ACTOR);
await assert.rejects(() => f.gate.start({ planDigest: plan.planDigest, confirmation: plan.confirmation }, ACTOR), error => error.code === 'PROVIDER_FAILED');
await assert.rejects(() => f.gate.start({ planDigest: plan.planDigest, confirmation: plan.confirmation }, ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_REQUIRED');
});
test('unfrozen, unregistered or semantically different package sets are rejected', async () => {
await assert.rejects(() => fixture({ control: { artifactSelection: 'local-unfrozen-build' } }).gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_NOT_READY');
await assert.rejects(() => fixture({ packageSet: { manifest: { controlProfile: 'other' } } }).gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_PACKAGE_SET_DRIFT');
const base = fixture();
base.packageSet.components[0].metadata.absolutePath = '/managed/different.pkg';
await assert.rejects(() => base.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_PACKAGE_SET_DRIFT');
});
test('stale, future and reversed scan timestamps fail closed', async () => {
for (const scan of [
{ scan_id: 'stale', started_at: '2026-08-26T15:50:00.000Z', completed_at: '2026-08-26T15:50:01.000Z' },
{ scan_id: 'future', started_at: '2026-08-26T16:01:00.000Z', completed_at: '2026-08-26T16:01:01.000Z' },
{ scan_id: 'reversed', started_at: '2026-08-26T16:00:01.000Z', completed_at: '2026-08-26T16:00:00.000Z' },
]) {
const f = fixture({ scan: { ...scan, games: [], root_coverage: [{ root: '/user/app/', coverage_state: 'complete' }, { root: '/mnt/data/app/', coverage_state: 'complete' }] } });
await assert.rejects(() => f.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_SCAN_STALE');
}
});
test('overlapping preflights and start-during-scan are rejected', async () => {
let release;
const deferred = new Promise(resolve => { release = resolve; });
const f = fixture({ scanConsole: () => deferred });
const pending = f.gate.plan(ACTOR);
await new Promise(resolve => setImmediate(resolve));
await assert.rejects(() => f.gate.plan(ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_BUSY');
await assert.rejects(() => f.gate.start({ planDigest: '0'.repeat(64), confirmation: A8_DISTINCT_CONTROL.confirmation }, ACTOR), error => error.code === 'PS5_A8_CONTROL_INSTALL_PLAN_BUSY');
release({
scan_id: 'deferred', started_at: '2026-08-26T16:00:00.000Z', completed_at: '2026-08-26T16:00:01.000Z', games: [],
root_coverage: [{ root: '/user/app/', coverage_state: 'complete' }, { root: '/mnt/data/app/', coverage_state: 'complete' }],
});
assert.match((await pending).planDigest, /^[a-f0-9]{64}$/);
});