Files
chimera-Public/test/server/ch-1304-ps5-fpkg-launch.test.js
T

490 lines
26 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const crypto = require('node:crypto');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const test = require('node:test');
const { createPs5NativePackageInstallService } = require('../../src/services/ps5-native-package-install-service');
const { createPs5UploadPackageInstallerProvider, PINNED_PS5_PACKAGE_INSTALLER } = require('../../src/services/ps5upload-package-installer-provider');
function response(body, ok = true, status = 200) {
return { ok, status, json: async () => body, text: async () => JSON.stringify(body) };
}
function packageFixture() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-ch1304-'));
const bytes = Buffer.concat([Buffer.from([0x7f, 0x46, 0x49, 0x48]), Buffer.alloc(64, 0x44)]);
const filePath = path.join(root, 'EP9000-PPSA01234_00-CHIMERATEST00001.pkg');
fs.writeFileSync(filePath, bytes);
return {
root,
component: {
componentId: 'base', type: 'base', order: 1, size: bytes.length,
sha256: crypto.createHash('sha256').update(bytes).digest('hex'),
contentId: 'EP9000-PPSA01234_00-CHIMERATEST00001',
metadata: { absolutePath: filePath, titleId: { value: 'PPSA01234' } },
},
};
}
function exactRuntimeIdentity() {
return {
observed: {
engine: { revision: PINNED_PS5_PACKAGE_INSTALLER.revision, sha256: PINNED_PS5_PACKAGE_INSTALLER.artifacts.engineLinuxX64Sha256, evidence: 'running-process-bytes' },
payload: { revision: PINNED_PS5_PACKAGE_INSTALLER.revision, sha256: PINNED_PS5_PACKAGE_INSTALLER.artifacts.payloadSha256, evidence: 'loader-input-and-instance-transition' },
dpi: { active: false },
},
};
}
function readyLaunchEvidence(overrides = {}) {
return {
ready: true,
capabilities: {
debugPackageRuntime: true,
fpkgEnableActive: true,
kernelPatchReady: true,
packageInstallerReady: true,
launchServiceReady: true,
processObservationReady: true,
syslogObservationReady: true,
foregroundClean: true,
},
logWatermark: { schema: 'chimera.ps5-syslog-watermark.v1', sha256: '0'.repeat(64), bytes: 0, tail: '' },
runtimeFingerprint: 'f'.repeat(64),
...overrides,
};
}
async function installedOperation(t, providerOverrides = {}, serviceOverrides = {}) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-ch1304-state-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const component = { componentId: 'base', type: 'base', order: 1, sha256: 'a'.repeat(64), size: 68 };
const provider = {
preflightInstall: async () => ({ ready: true }),
install: async () => ({ status: 'accepted_unverified', sessionId: 'install-1', acceptedUnverified: true }),
installStatus: async () => ({ phase: 'done', providerVerified: true }),
cancel: async () => ({ cancelled: true }),
preflightLaunch: async () => readyLaunchEvidence(),
registerIfRequired: async () => ({ required: false, registered: true, basis: 'independent-post-scan' }),
launch: async () => ({ accepted: true, route: '/api/ps5/app/launch' }),
observeLaunch: async () => ({ observed: false, processes: [] }),
...providerOverrides,
};
const service = createPs5NativePackageInstallService({
provider,
stager: async () => ({ stagedPath: '/user/data/ps5upload/pkg_library/test.pkg' }),
stateFile: path.join(root, 'operations.json'),
...serviceOverrides,
});
const started = await service.start({ titleId: 'PPSA01234', packageSet: { id: 7, components: [component] } });
for (let attempt = 0; attempt < 40 && service.get(started.id).status !== 'awaiting_post_scan'; attempt += 1) await new Promise(resolve => setTimeout(resolve, 5));
const installed = service.markPostScan(started.id, { source: 'post-install-console-scan', titleId: 'PPSA01234' });
return { root, provider, service, installed };
}
test('post-scan stops at installed_observed and cannot qualify a restore artifact', async t => {
const { installed } = await installedOperation(t);
assert.equal(installed.status, 'installed_observed');
assert.equal(installed.installed, true);
assert.equal(installed.installedContentVerified, false);
assert.equal(installed.launchVerified, false);
assert.equal(installed.productionRestorable, false);
assert.equal(installed.launchQualificationRequired, true);
});
test('registration-only evidence never proves installed payload completeness', async t => {
let registrations = 0;
let launches = 0;
const { installed, service } = await installedOperation(t, {
registerIfRequired: async input => {
registrations += 1;
assert.deepEqual(input, { titleId: 'PPSA01234', installedObserved: true });
return { required: false, registered: true, titleId: 'PPSA01234', basis: 'sony-managed-installed-package' };
},
launch: async () => { launches += 1; return { accepted: true, route: '/api/ps5/app/launch' }; },
});
const launched = await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
assert.equal(registrations, 1);
assert.equal(launches, 1);
assert.equal(launched.launchEvidence.registration.required, false);
assert.equal(launched.launchEvidence.registration.basis, 'sony-managed-installed-package');
assert.equal(launched.installedContentVerified, false);
assert.equal(launched.productionRestorable, false);
});
test('installed-package registration contract has no DRM-patch or caller-selected source path', async t => {
const { installed, service } = await installedOperation(t);
await assert.rejects(
() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234', patchDrmType: true }),
error => error.code === 'PS5_FPKG_PROVIDER_INPUT',
);
assert.equal(service.get(installed.id).status, 'installed_observed');
});
test('launch requires exact title-bound confirmation and all semantic runtime capabilities', async t => {
const { installed, service } = await installedOperation(t, {
preflightLaunch: async () => readyLaunchEvidence({ capabilities: { ...readyLaunchEvidence().capabilities, fpkgEnableActive: false } }),
});
await assert.rejects(() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY OTHER00000' }), error => error.code === 'PS5_FPKG_LAUNCH_CONFIRMATION');
await assert.rejects(() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' }), error => error.code === 'PS5_FPKG_LAUNCH_RUNTIME_NOT_READY');
assert.equal(service.get(installed.id).status, 'installed_observed');
});
test('launch acknowledgement remains unverified until stable matching process evidence', async t => {
let clock = 10_000;
const observations = [
{ observed: true, process: { pid: 42, name: 'eboot.bin', titleId: 'PPSA01234' }, rawEvidence: { processes: 1 } },
{ observed: true, process: { pid: 42, name: 'eboot.bin', titleId: 'PPSA01234' }, rawEvidence: { processes: 1 } },
];
const { installed, service } = await installedOperation(t, { observeLaunch: async () => observations.shift() }, { now: () => clock, launchStabilityMs: 5_000 });
const acknowledged = await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
assert.equal(acknowledged.status, 'launching');
assert.equal(acknowledged.launchVerified, false);
assert.equal(acknowledged.productionRestorable, false);
const first = await service.pollLaunchQualification(installed.id);
assert.equal(first.status, 'launching');
clock += 5_000;
const verified = await service.pollLaunchQualification(installed.id);
assert.equal(verified.status, 'launch_verified');
assert.equal(verified.launchVerified, true);
assert.equal(verified.productionRestorable, true);
assert.equal(verified.launchEvidence.process.pid, 42);
assert.equal(verified.launchEvidence.process.titleId, 'PPSA01234');
assert.equal(verified.launchEvidence.observationCount, 2);
assert.deepEqual(verified.launchEvidence.boundaries, { verifyImage: 'passed', pfsMount: 'passed', executable: 'reached', process: 'stable', basis: 'stable-exact-title-process' });
assert.match(verified.launchEvidence.runtimeFingerprint, /^[a-f0-9]{64}$/);
});
test('matching process which disappears before stability is launch_failed', async t => {
let clock = 1_000;
const observations = [
{ observed: true, process: { pid: 77, name: 'eboot.bin', titleId: 'PPSA01234' } },
{ observed: false, processes: [] },
];
const { installed, service } = await installedOperation(t, { observeLaunch: async () => observations.shift() }, { now: () => clock, launchStabilityMs: 10_000 });
await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
await service.pollLaunchQualification(installed.id);
clock += 1_000;
const failed = await service.pollLaunchQualification(installed.id);
assert.equal(failed.status, 'launch_failed');
assert.equal(failed.error.code, 'PS5_FPKG_PROCESS_EXITED_EARLY');
assert.equal(failed.productionRestorable, false);
});
for (const [failureBoundary, code] of [['verifyImage', 'PS5_FPKG_VERIFY_IMAGE_FAILED'], ['PfsMountGameData_PPR() = 0x80020060', 'PS5_FPKG_PFS_MOUNT_FAILED']]) {
test(`${failureBoundary} evidence is retained and normalized`, async t => {
const { installed, service } = await installedOperation(t, { observeLaunch: async () => ({ observed: false, failureBoundary, rawEvidence: { klog: failureBoundary } }) });
await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
const failed = await service.pollLaunchQualification(installed.id);
assert.equal(failed.status, 'launch_failed');
assert.equal(failed.error.code, code);
assert.match(JSON.stringify(failed.launchEvidence.rawEvidence), new RegExp(failureBoundary.replace(/[()]/g, '\\$&')));
});
}
test('provider-normalized ShellUI launch-flow failure reaches the durable launch state', async t => {
const { installed, service } = await installedOperation(t, {
observeLaunch: async () => ({
observed: false,
failureBoundary: 'ShellUI launch flow',
failureCode: 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED',
rawEvidence: { currentWindow: 'LaunchFlowError.PPSA01234' },
}),
});
await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
const failed = await service.pollLaunchQualification(installed.id);
assert.equal(failed.status, 'launch_failed');
assert.equal(failed.error.code, 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED');
assert.equal(failed.launchVerified, false);
assert.equal(failed.productionRestorable, false);
});
test('normalized launch failure survives when the raw process evidence exceeds the retention preview', async t => {
const normalizedEvent = { category: 'launch_flow_error', titleId: 'PPSA01234', code: 'LaunchFlowError.PPSA01234' };
const { installed, service } = await installedOperation(t, {
observeLaunch: async () => ({
observed: false,
failureBoundary: 'ShellUI launch flow',
failureCode: 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED',
normalizedEvents: [normalizedEvent],
truncated: false,
syslog: { continuity: 'exact-watermark', currentWindow: 'LaunchFlowError.PPSA01234', currentSha256: 'a'.repeat(64) },
rawEvidence: { processes: Array.from({ length: 800 }, (_, pid) => ({ pid: pid + 1, name: `process-${pid}-${'x'.repeat(80)}` })), currentWindow: 'LaunchFlowError.PPSA01234' },
}),
});
await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
const failed = await service.pollLaunchQualification(installed.id);
const retained = failed.launchEvidence.rawEvidence[0];
assert.equal(retained.evidence.truncated, true);
assert.equal(retained.summary.failureCode, 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED');
assert.equal(retained.summary.failureBoundary, 'ShellUI launch flow');
assert.deepEqual(retained.summary.normalizedEvents, [normalizedEvent]);
assert.equal(retained.summary.syslog.continuity, 'exact-watermark');
assert.equal(retained.summary.syslog.currentWindowBytes, Buffer.byteLength('LaunchFlowError.PPSA01234'));
assert.equal(failed.error.details.summary.failureCode, 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED');
});
test('accepted instrumented LNC result followed by ShellUI failure is classified after the Sony call', async t => {
const diagnostic = {
schema: 'ps5upload.shellui-launch-observation.v1',
diagnosticIdentity: 'chimera-p15-shellui-observe-v1',
attempts: [{ index: 1, callEntered: true, sonyReturnObserved: true, rawSigned32: 0, wrapperRc: 0 }],
terminalAttemptIndex: 1,
terminalWrapperRc: 0,
providerAccepted: true,
providerNormalizedResult: 'accepted',
};
const { installed, service } = await installedOperation(t, {
launch: async () => ({ accepted: true, diagnostic }),
observeLaunch: async () => ({
observed: false,
failureBoundary: 'ShellUI launch flow',
failureCode: 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED',
rawEvidence: { currentWindow: 'LaunchFlowError.PPSA01234' },
}),
});
await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
const failed = await service.pollLaunchQualification(installed.id);
assert.equal(failed.status, 'launch_failed');
assert.equal(failed.error.code, 'PS5_FPKG_SHELLUI_POST_LNC_FAILED');
assert.equal(failed.launchEvidence.request.diagnostic.terminalWrapperRc, 0);
assert.equal(failed.launchVerified, false);
assert.equal(failed.productionRestorable, false);
});
test('structured diagnostic rejection is persisted before launch_failed and never replayed', async t => {
let launches = 0;
const diagnostic = {
schema: 'ps5upload.shellui-launch-observation.v1',
diagnosticIdentity: 'chimera-p15-shellui-observe-v1',
operationId: '123e4567-e89b-42d3-a456-426614174000',
titleId: 'PPSA01234',
attempts: [{ index: 1, wrapperRc: 27, wrapperHex: '0x0000001b' }],
internalRetryOccurred: false,
terminalAttemptIndex: 1,
terminalWrapperRc: 27,
terminalWrapperHex: '0x0000001b',
providerAccepted: false,
providerNormalizedResult: 'sony_rejected',
};
const { installed, service } = await installedOperation(t, {
launch: async ({ operationId }) => {
launches += 1;
return { accepted: false, titleId: 'PPSA01234', operationId, diagnostic };
},
});
await assert.rejects(
() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' }),
error => error.code === 'PS5_FPKG_LAUNCH_REQUEST_REJECTED',
);
const failed = service.get(installed.id);
assert.equal(failed.status, 'launch_failed');
assert.equal(failed.launchEvidence.request.diagnostic.terminalWrapperRc, 27);
assert.equal(failed.launchEvidence.requestedAt > 0, true);
await assert.rejects(() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' }), error => error.code === 'PS5_FPKG_LAUNCH_STATE');
assert.equal(launches, 1);
});
test('restart during launch persists interrupted outcome_unknown and never replays launch', async t => {
let launches = 0;
const { root, installed, service, provider } = await installedOperation(t, { launch: async () => { launches += 1; return { accepted: true }; } });
await service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
const restarted = createPs5NativePackageInstallService({ provider, stager: async () => ({}), stateFile: path.join(root, 'operations.json') });
const restored = restarted.get(installed.id);
assert.equal(restored.status, 'interrupted');
assert.equal(restored.launchOutcome, 'outcome_unknown');
assert.equal(restored.launchVerified, false);
assert.equal(launches, 1);
});
test('concurrent launch starts reserve before preflight and produce exactly one provider request', async t => {
let releasePreflight;
let launches = 0;
const deferred = new Promise(resolve => { releasePreflight = resolve; });
const { installed, service } = await installedOperation(t, {
preflightLaunch: async () => deferred,
launch: async () => { launches += 1; return { accepted: true }; },
});
const first = service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
await Promise.resolve();
await assert.rejects(
() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' }),
error => error.code === 'PS5_FPKG_LAUNCH_STATE',
);
releasePreflight(readyLaunchEvidence());
const result = await first;
assert.equal(result.status, 'launching');
assert.equal(launches, 1);
});
test('corrupt persisted operation state fails closed and preserves its bytes', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-ch1304-corrupt-state-'));
const stateFile = path.join(root, 'operations.json');
const corrupt = Buffer.from('{"operations":');
const provider = {
preflightInstall: async () => ({ ready: true }),
install: async () => ({}),
installStatus: async () => ({}),
};
try {
fs.writeFileSync(stateFile, corrupt);
assert.throws(
() => createPs5NativePackageInstallService({ provider, stager: async () => ({}), stateFile }),
error => error.code === 'PS5_PKG_OPERATION_STATE_CORRUPT',
);
assert.deepEqual(fs.readFileSync(stateFile), corrupt);
} finally { fs.rmSync(root, { recursive: true, force: true }); }
});
test('legacy installed operation rehydrates immutable package identity without claiming content verification', async t => {
let launches = 0;
const { root, installed, provider } = await installedOperation(t, {
launch: async () => { launches += 1; return { accepted: true }; },
});
const stateFile = path.join(root, 'operations.json');
const persisted = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
const legacy = persisted.find(item => item.id === installed.id);
delete legacy.packageIdentity;
delete legacy.packageIdentitySha256;
fs.writeFileSync(stateFile, JSON.stringify(persisted, null, 2));
const restarted = createPs5NativePackageInstallService({ provider, stager: async () => ({}), stateFile });
const migrated = restarted.get(installed.id);
assert.match(migrated.packageIdentity, /^base:base:[a-f0-9]{64}:68$/);
assert.match(migrated.packageIdentitySha256, /^[a-f0-9]{64}$/);
assert.equal(migrated.packageIdentityProvenance, 'derived-persisted-component-manifest-v1');
assert.equal(migrated.installedContentVerified, false);
assert.equal(migrated.launchVerified, false);
assert.equal(migrated.productionRestorable, false);
const launched = await restarted.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' });
assert.equal(launched.status, 'launching');
assert.equal(launches, 1);
});
test('partial legacy package identity remains fail-closed', async t => {
const { root, installed, provider } = await installedOperation(t);
const stateFile = path.join(root, 'operations.json');
const persisted = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
const legacy = persisted.find(item => item.id === installed.id);
delete legacy.packageIdentitySha256;
fs.writeFileSync(stateFile, JSON.stringify(persisted, null, 2));
const restarted = createPs5NativePackageInstallService({ provider, stager: async () => ({}), stateFile });
await assert.rejects(
() => restarted.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' }),
error => error.code === 'PS5_FPKG_ARTIFACT_HASH_DRIFT',
);
});
test('ambiguous launch transport failure persists outcome_unknown and cannot be replayed', async t => {
let launches = 0;
const { installed, service } = await installedOperation(t, {
launch: async () => { launches += 1; throw Object.assign(new Error('connection reset'), { code: 'ECONNRESET' }); },
});
await assert.rejects(
() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' }),
error => error.code === 'PS5_FPKG_LAUNCH_OUTCOME_UNKNOWN',
);
const unknown = service.get(installed.id);
assert.equal(unknown.status, 'interrupted');
assert.equal(unknown.launchOutcome, 'outcome_unknown');
assert.equal(unknown.launchVerified, false);
assert.equal(unknown.productionRestorable, false);
assert.equal(unknown.error.code, 'PS5_FPKG_LAUNCH_OUTCOME_UNKNOWN');
await assert.rejects(
() => service.startLaunchQualification(installed.id, { confirmation: 'LAUNCH VERIFY PPSA01234' }),
error => error.code === 'PS5_FPKG_LAUNCH_STATE',
);
assert.equal(launches, 1);
});
test('provider pin, routes and runtime evidence are exact and caller cannot select host or title indirectly', async t => {
const item = packageFixture(); t.after(() => fs.rmSync(item.root, { recursive: true, force: true }));
const requests = [];
let processRequests = 0;
const provider = createPs5UploadPackageInstallerProvider({
engineOrigin: 'http://127.0.0.1:19113', consoleHost: 'ps5.local', packageRoot: item.root,
getRuntimeIdentity: exactRuntimeIdentity,
fetch: async (url, options = {}) => {
requests.push({ url: String(url), body: options.body ? JSON.parse(options.body) : null });
if (String(url).endsWith('/api/version')) return response({ version: '5.4.19' });
if (String(url).includes('/api/ps5/status')) return response({ version: '5.4.19', ucred_elevated: true });
if (String(url).includes('/api/ps5/readiness')) return response({ ready: true });
if (String(url).includes('/api/ps5/process/list')) {
processRequests += 1;
return response({ processes: [
{ pid: 107, name: 'kstuff.elf' },
{ pid: 108, name: 'etaHEN Utility Daemon' },
...(processRequests > 1 ? [{ pid: 91, name: 'eboot.bin', title_id: 'PPSA01234', kind: 'app' }] : []),
], truncated: false });
}
if (String(url).includes('/api/ps5/syslog/tail')) return response({ text: '' });
if (String(url).includes('/api/ps5/app/launch')) return response({ ok: true });
return response({});
},
});
assert.equal(PINNED_PS5_PACKAGE_INSTALLER.version, '5.4.19');
assert.equal(PINNED_PS5_PACKAGE_INSTALLER.revision, 'ec0e5c4e95520f2dc26c226a909dc5a95e385f41');
const preflight = await provider.preflightLaunch({ titleId: 'PPSA01234', components: [item.component] });
assert.equal(preflight.ready, true);
assert.deepEqual(Object.values(preflight.capabilities), [true, true, true, true, true, true, true, true, true]);
const launched = await provider.launch({ titleId: 'PPSA01234' });
assert.equal(launched.accepted, true);
assert.equal(launched.acknowledgementConclusive, false);
assert.equal(launched.clientAutomaticRetry, false);
assert.equal(launched.upstreamInternalRetryPossible, true);
const observed = await provider.observeLaunch({ titleId: 'PPSA01234' });
assert.equal(observed.process.pid, 91);
const launchRequest = requests.find(entry => entry.url.includes('/api/ps5/app/launch'));
assert.deepEqual(launchRequest.body, { addr: 'ps5.local:9114', title_id: 'PPSA01234' });
await assert.rejects(() => provider.launch({ titleId: 'PPSA01234', host: 'attacker.invalid' }), error => error.code === 'PS5_FPKG_PROVIDER_INPUT');
await assert.rejects(() => provider.launch({ titleId: '../bad' }), error => error.code === 'PS5_FPKG_IDENTITY_INVALID');
});
test('install preflight fails closed when direct fPKG runtime evidence is absent', async t => {
const item = packageFixture(); t.after(() => fs.rmSync(item.root, { recursive: true, force: true }));
const provider = createPs5UploadPackageInstallerProvider({
engineOrigin: 'http://127.0.0.1:19113', consoleHost: 'ps5.local', packageRoot: item.root,
fetch: async url => {
if (String(url).endsWith('/api/version')) return response({ version: '5.4.19' });
if (String(url).includes('/api/ps5/status')) return response({ ucred_elevated: true, kernel_rw: true });
if (String(url).includes('/api/ps5/readiness')) return response({ ready: true });
return response({});
},
});
const readiness = await provider.preflightInstall({ components: [item.component] });
assert.equal(readiness.ready, false);
assert.equal(readiness.errorCode, 'PS5_FPKG_INSTALL_RUNTIME_NOT_READY');
assert.equal(readiness.capabilities.fpkgEnableActive, false);
assert.equal(readiness.capabilities.debugPackageRuntime, false);
});
test('artifact byte drift is rejected before a launch request', async t => {
const item = packageFixture(); t.after(() => fs.rmSync(item.root, { recursive: true, force: true }));
let launches = 0;
const provider = createPs5UploadPackageInstallerProvider({
engineOrigin: 'http://127.0.0.1:19113', consoleHost: 'ps5.local', packageRoot: item.root,
fetch: async url => {
if (String(url).endsWith('/api/version')) return response({ version: '5.4.19' });
if (String(url).includes('/api/ps5/status')) return response({ intdebug_avail: true, fpkg_enable_active: true, kernel_rw: true, ucred_elevated: true, launch_service_ready: true });
if (String(url).includes('/api/ps5/readiness')) return response({ ready: true });
if (String(url).includes('/api/ps5/process/list')) return response({ processes: [], truncated: false });
launches += 1; return response({ ok: true });
},
});
fs.writeFileSync(item.component.metadata.absolutePath, Buffer.concat([Buffer.from([0x7f, 0x46, 0x49, 0x48]), Buffer.alloc(64, 0x45)]));
await assert.rejects(() => provider.preflightLaunch({ titleId: 'PPSA01234', components: [item.component] }), error => error.code === 'PS5_FPKG_ARTIFACT_HASH_DRIFT');
assert.equal(launches, 0);
});
test('registry exposes launch qualification as a separate exact-confirmed lifecycle', () => {
const registry = fs.readFileSync(path.resolve(__dirname, '../../src/server/create-registry.js'), 'utf8');
for (const command of ['ps5-native-packages:restore-launch-plan', 'ps5-native-packages:restore-launch', 'ps5-native-packages:restore-launch-status']) assert.match(registry, new RegExp(command));
assert.match(registry, /confirmation: input => `LAUNCH VERIFY \$\{ps5NativePackageInstalls\.get\(input\?\.operationId\)\.titleId\}`/);
assert.match(registry, /launchVerified: false, productionRestorable: false, nextAction: 'launch-qualification-required'/);
});