Files
chimera-Public/test/server/ch-1301-clean-ps5-fpkg-builder.test.js

200 lines
10 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 {
A8_DISTINCT_CONTROL,
PINNED_PS5_BUILDER,
createPs5NativePkgBuilderProvider,
} = require('../../src/services/ps5-native-pkg-builder-provider');
const CLEAN_CAPABILITIES = Object.freeze({
backupToDebugFpkg: true,
dataDlcToDebugFpkg: true,
updateToDebugFpkg: false,
backupInspection: true,
packageInspection: true,
packageValidation: true,
launchReadiness: true,
licenseFreeBuild: false,
distinctTitleControlBuild: true,
finalizedFihOutput: true,
});
function fixture() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-clean-fpkg-builder-'));
const toolRoot = path.join(root, 'tool');
const sourceRoot = path.join(root, 'source');
const workRoot = path.join(root, 'work');
fs.mkdirSync(toolRoot, { recursive: true });
fs.mkdirSync(path.join(sourceRoot, 'sce_sys'), { recursive: true });
fs.mkdirSync(workRoot, { recursive: true });
fs.writeFileSync(path.join(sourceRoot, 'sce_sys', 'param.json'), '{}');
fs.writeFileSync(path.join(sourceRoot, 'eboot.bin'), Buffer.from([0x7f, 0x45, 0x4c, 0x46]));
const executable = path.join(toolRoot, PINNED_PS5_BUILDER.executable);
fs.writeFileSync(executable, 'fixture');
return { root, toolRoot, sourceRoot, workRoot };
}
function cleanProbe(overrides = {}) {
return {
product: 'LibProsperoPkg',
version: '2.6.0',
revision: '9084c0e88cd8d7cd9c0b353b8b6b89f7d1cc4275',
abi: 'chimera-prospero-bridge-v4',
patched: true,
patch: 'chimera-stream-large-package-assembly-u2c24-v2',
capabilities: CLEAN_CAPABILITIES,
...overrides,
};
}
test('production builder pin is the exact streaming semantic bridge-v4 baseline', () => {
assert.equal(PINNED_PS5_BUILDER.abi, 'chimera-prospero-bridge-v4');
assert.equal(PINNED_PS5_BUILDER.version, '2.6.0');
assert.equal(PINNED_PS5_BUILDER.patched, true);
assert.equal(PINNED_PS5_BUILDER.patch, 'chimera-stream-large-package-assembly-u2c24-v2');
assert.equal(PINNED_PS5_BUILDER.patchSha256, '65e04ca3462ef4e619d296d3656c3658a6083283102a38bfb69d095f369db135');
assert.equal('patchSet' in PINNED_PS5_BUILDER, false);
});
test('probe recognizes the exact clean revision but blocks its unproven license-free converter', async t => {
const f = fixture();
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
let report = cleanProbe();
const provider = createPs5NativePkgBuilderProvider({
toolRoot: f.toolRoot,
allowedWorkRoots: [f.workRoot],
allowedSourceRoots: [f.sourceRoot],
runner: async () => ({ code: 0, output: JSON.stringify(report) }),
});
let status = await provider.probe();
assert.equal(status.pinned, true);
assert.equal(status.clean, true);
assert.equal(status.available, false);
assert.equal(status.controlAvailable, true);
assert.equal(status.reason, 'capability-mismatch');
report = cleanProbe({ patch: 'unexpected-patch' });
assert.equal((await provider.probe()).available, false);
report = cleanProbe({ revision: '0'.repeat(40) });
assert.equal((await provider.probe()).available, false);
report = cleanProbe({ capabilities: { ...CLEAN_CAPABILITIES, licenseFreeBuild: true } });
status = await provider.probe();
assert.equal(status.available, true);
});
test('distinct control build uses one fixed non-colliding identity and exact upstream assets', async t => {
const f = fixture();
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
fs.writeFileSync(path.join(f.sourceRoot, 'eboot.bin'), Buffer.from('fixture-eboot'));
fs.writeFileSync(path.join(f.sourceRoot, 'sce_sys', 'icon0.png'), Buffer.from('fixture-icon'));
const descriptor = {
...A8_DISTINCT_CONTROL,
assets: {
ebootSha256: require('node:crypto').createHash('sha256').update('fixture-eboot').digest('hex'),
iconSha256: require('node:crypto').createHash('sha256').update('fixture-icon').digest('hex'),
},
};
const outputPath = path.join(f.workRoot, 'control.pkg');
const workPath = path.join(f.workRoot, 'control-work');
let request;
const runner = async ({ args }) => {
if (args[0] === 'probe') return { code: 0, output: JSON.stringify(cleanProbe()) };
assert.deepEqual(args.slice(0, 2), ['build-control', '--request']);
request = JSON.parse(fs.readFileSync(args[2], 'utf8'));
fs.writeFileSync(outputPath, Buffer.concat([Buffer.from([0x7f, 0x46, 0x49, 0x48]), Buffer.alloc(64)]));
fs.writeFileSync(args[4], JSON.stringify({ ok: true, outputPath, licenseFree: true, fixedIdentity: true }));
return { code: 0, output: '' };
};
const provider = createPs5NativePkgBuilderProvider({
toolRoot: f.toolRoot, allowedWorkRoots: [f.workRoot], allowedSourceRoots: [f.sourceRoot], runner,
distinctControl: descriptor,
});
const result = await provider.buildDistinctControl({ sourcePath: f.sourceRoot, outputPath, workPath });
assert.equal(request.abi, 'chimera-prospero-bridge-v4');
assert.equal(request.operation, 'build-control');
assert.equal(request.titleId, A8_DISTINCT_CONTROL.titleId);
assert.equal(request.contentId, A8_DISTINCT_CONTROL.contentId);
assert.equal(request.componentType, 'base');
assert.equal(request.sourceAssets.ebootSha256, descriptor.assets.ebootSha256);
assert.equal(request.sourceAssets.iconSha256, descriptor.assets.iconSha256);
assert.equal(result.report.licenseFree, true);
});
test('distinct control build rejects source drift and caller-selected identity before the bridge', async t => {
const f = fixture();
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
const provider = createPs5NativePkgBuilderProvider({
toolRoot: f.toolRoot, allowedWorkRoots: [f.workRoot], allowedSourceRoots: [f.sourceRoot],
runner: async () => ({ code: 0, output: JSON.stringify(cleanProbe()) }),
});
await assert.rejects(
() => provider.buildDistinctControl({ sourcePath: f.sourceRoot, outputPath: path.join(f.workRoot, 'control.pkg'), workPath: path.join(f.workRoot, 'work'), titleId: 'PPSA12345' }),
error => error.code === 'PS5_A8_CONTROL_IDENTITY_FIXED',
);
await assert.rejects(
() => provider.buildDistinctControl({ sourcePath: f.sourceRoot, outputPath: path.join(f.workRoot, 'control.pkg'), workPath: path.join(f.workRoot, 'work') }),
error => error.code === 'PS5_A8_CONTROL_SOURCE_DRIFT',
);
});
test('build invokes only build-backup with an immutable semantic v4 request when all clean capabilities are proven', async t => {
const f = fixture();
t.after(() => fs.rmSync(f.root, { recursive: true, force: true }));
const outputPath = path.join(f.workRoot, 'artifact.pkg');
const buildWork = path.join(f.workRoot, 'job');
let request;
let reportOverride = {};
const runner = async ({ args }) => {
if (args[0] === 'probe') return { code: 0, output: JSON.stringify(cleanProbe({ capabilities: { ...CLEAN_CAPABILITIES, licenseFreeBuild: true, backupBuildApi: 'ProsperoPackageBuilder' } })) };
assert.deepEqual(args.slice(0, 2), ['build-backup', '--request']);
request = JSON.parse(fs.readFileSync(args[2], 'utf8'));
fs.writeFileSync(outputPath, Buffer.concat([Buffer.from([0x7f, 0x46, 0x49, 0x48]), Buffer.alloc(64)]));
fs.writeFileSync(args[4], JSON.stringify({ ok: true, outputPath, buildApi: 'ProsperoPackageBuilder', licenseFree: true, validation: { accepted: true }, unresolvedModules: [], launchReadiness: { isLaunchReady: true }, ...reportOverride }));
return { code: 0, output: '' };
};
const provider = createPs5NativePkgBuilderProvider({ toolRoot: f.toolRoot, allowedWorkRoots: [f.workRoot], allowedSourceRoots: [f.sourceRoot], runner });
await provider.build({
componentType: 'base', sourcePath: f.sourceRoot, sourceSize: 6,
outputPath, workPath: buildWork, titleId: 'PPSA12345',
contentId: 'UP0001-PPSA12345_00-CHIMERAFPKG00001', version: '01.00', title: 'Fixture',
});
assert.equal(request.abi, 'chimera-prospero-bridge-v4');
assert.equal(request.operation, 'build-backup');
assert.equal(request.componentType, 'base');
assert.equal('licenseFree' in request, false);
assert.equal('fakeSignSelfModules' in request, false);
assert.equal('tuning' in request, false);
for (const invalid of [{ licenseFree: false }, { buildApi: 'ProsperoBackupConverter' }, { outputPath: path.join(f.workRoot, 'other.pkg') }, { validation: { accepted: false } }, { unresolvedModules: ['eboot.bin'] }]) {
reportOverride = invalid;
await assert.rejects(provider.build({ componentType: 'base', sourcePath: f.sourceRoot, sourceSize: 6, outputPath, workPath: buildWork, titleId: 'PPSA12345', contentId: 'UP0001-PPSA12345_00-CHIMERAFPKG00001' }), { code: 'PS5_BUILDER_OUTPUT' });
}
});
test('Docker production stage applies only the pinned streaming patch, never the historical V16 series', () => {
const docker = fs.readFileSync('Dockerfile', 'utf8');
assert.match(docker, /ARG LIBPROSPEROPKG_COMMIT=9084c0e88cd8d7cd9c0b353b8b6b89f7d1cc4275/);
assert.match(docker, /patches\/0001-stream-large-package-assembly\.patch/);
assert.match(docker, /git -C \/src\/LibProsperoPkg apply/);
const streamingPatch = fs.readFileSync('tools/chimera-prospero-bridge/patches/0001-stream-large-package-assembly.patch', 'utf8');
assert.match(streamingPatch, /BuildFromFsTreeToFile/);
assert.match(streamingPatch, /BuildForPackageFile/);
assert.match(streamingPatch, /ProsperoOuterPfsAddressingPlan/);
assert.match(streamingPatch, /WriteU2cBase24\(entry\.AsSpan\(4, 3\), nextBase\)/);
assert.match(streamingPatch, /value is < 0 or > 0xFFFFFF/);
assert.doesNotMatch(streamingPatch, /MountImageKey|RsaPkcs1EncryptKey|PfsGenEncKey|PfsGenSignKey/);
assert.doesNotMatch(docker, /CHIMERA_PATCH_SET|chimera-license-free-base-v16/);
assert.match(docker, /LibProsperoPkg-9084c0e-streaming/);
});
test('historical patches remain documented outside the production path', () => {
const legacyRoot = 'tools/chimera-prospero-bridge/legacy/v16-patches';
assert.equal(fs.existsSync(path.join(legacyRoot, '0001-parallel-fast-store.patch')), true);
const notice = fs.readFileSync(path.join(legacyRoot, 'README.md'), 'utf8');
assert.match(notice, /historical research only/i);
assert.match(notice, /not used by the production builder/i);
});