'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}$/); });