Initial curated chimera source from a8ae5b1db07fb49926c94585e2718e5130330974
This commit is contained in:
commit
c737a37f71
531 files changed
+119270
No files matched your search
@@ -0,0 +1,181 @@
|
||||
'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 { createLiveTrophyCatalogService, normalizeCatalog,
|
||||
normalizeInstalledCatalog: normalizeInstalledServiceCatalog } = require('../../src/services/live-trophy-catalog-service');
|
||||
const { normalizeActiveCatalog, activeCatalogMarkup, normalizeInstalledCatalog,
|
||||
installedCatalogMarkup, normalizeTrophyLibrary, trophyLibraryMarkup } = require('../../src/renderer/trophies');
|
||||
|
||||
function adapterResult() {
|
||||
return { status: 'ok', mappingRemoved: true, acquisition: { readOnly: true, persisted: false },
|
||||
entry: { game: { title: 'Example', trophyCount: 2 }, trophies: [
|
||||
{ id: 0, grade: 'bronze', hidden: false, name: 'Start', description: 'Begin', observedUnlocked: true },
|
||||
{ id: 1, grade: 'gold', hidden: true, name: 'Finish', description: 'End', observedUnlocked: false },
|
||||
] } };
|
||||
}
|
||||
|
||||
test('normalizes only the bounded UI-safe active catalog', () => {
|
||||
const value = normalizeCatalog(adapterResult());
|
||||
assert.deepEqual(value.counts, { total: 2, unlocked: 1, locked: 1 });
|
||||
assert.equal(value.trophies[1].name, 'Finish'); assert.equal(value.trophies[0].unlocked, true);
|
||||
assert.equal(value.entry, undefined); assert.equal(value.catalogKey, undefined);
|
||||
assert.deepEqual(value.platform, { family: 'legacy', runsOn: 'ps5', nativePs5: false });
|
||||
assert.deepEqual(value.capabilities, { liveInventory: true, exactOneUnlock: false });
|
||||
});
|
||||
|
||||
test('hash-bound service invokes one fixed adapter and returns a read-only catalog', async t => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-live-trophy-'));
|
||||
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
||||
fs.mkdirSync(path.join(root, 'host'), { recursive: true }); fs.mkdirSync(path.join(root, 'artifacts'));
|
||||
fs.writeFileSync(path.join(root, 'host', 'live-active-catalog-cli.mjs'), 'fixture');
|
||||
const elf = Buffer.from('resident'); fs.writeFileSync(path.join(root, 'artifacts', 'chimera-ps5-legacy-query-resident.elf'), elf);
|
||||
const calls = [];
|
||||
const service = createLiveTrophyCatalogService({ appRoot: root,
|
||||
settings: { get: key => key === 'ftp.host' ? 'console.test' : '' },
|
||||
secrets: { get: key => key === 'trophy.releaseKey' ? '00112233445566778899aabbccddeeff' : '' },
|
||||
adapterRoot: root, expectedElfSha256: crypto.createHash('sha256').update(elf).digest('hex'),
|
||||
runAdapter: async input => { calls.push(input); return JSON.stringify(adapterResult()); } });
|
||||
assert.equal(service.status().available, true);
|
||||
const catalog = await service.activeCatalog();
|
||||
assert.equal(catalog.readOnly, true); assert.equal(catalog.persisted, false); assert.equal(calls.length, 1);
|
||||
assert.equal(catalog.platform.nativePs5, false); assert.equal(catalog.capabilities.exactOneUnlock, false);
|
||||
assert.equal(calls[0].host, 'console.test'); assert.equal(calls[0].releaseKey.length, 32);
|
||||
});
|
||||
|
||||
test('active catalog retries one transient base-map failure and no other failure', async t => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-live-trophy-retry-'));
|
||||
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
||||
fs.mkdirSync(path.join(root, 'host'), { recursive: true }); fs.mkdirSync(path.join(root, 'artifacts'));
|
||||
fs.writeFileSync(path.join(root, 'host', 'live-active-catalog-cli.mjs'), 'fixture');
|
||||
const elf = Buffer.from('resident'); fs.writeFileSync(path.join(root, 'artifacts', 'chimera-ps5-legacy-query-resident.elf'), elf);
|
||||
const options = { appRoot: root, adapterRoot: root,
|
||||
settings: { get: () => 'console.test' }, secrets: { get: () => '00112233445566778899aabbccddeeff' },
|
||||
expectedElfSha256: crypto.createHash('sha256').update(elf).digest('hex') };
|
||||
let attempts = 0;
|
||||
const recovered = createLiveTrophyCatalogService({ ...options, runAdapter: async () => {
|
||||
attempts += 1;
|
||||
if (attempts === 1) throw new Error('Live trophy adapter failed: LEGACY_QUERY_LOAD_PS5DEBUG_LOAD_BASE_MAP_FAILED');
|
||||
return JSON.stringify(adapterResult());
|
||||
} });
|
||||
assert.equal((await recovered.activeCatalog()).available, true); assert.equal(attempts, 2);
|
||||
attempts = 0;
|
||||
const refused = createLiveTrophyCatalogService({ ...options, runAdapter: async () => {
|
||||
attempts += 1; throw new Error('Live trophy adapter failed: LEGACY_QUERY_GAME_MISSING');
|
||||
} });
|
||||
await assert.rejects(refused.activeCatalog(), /LEGACY_QUERY_GAME_MISSING/); assert.equal(attempts, 1);
|
||||
});
|
||||
|
||||
test('production binding is read-only and exposes no arbitrary command or trophy mutation route', () => {
|
||||
const service = fs.readFileSync(path.join(__dirname, '../../src/services/live-trophy-catalog-service.js'), 'utf8');
|
||||
const registry = fs.readFileSync(path.join(__dirname, '../../src/server/register-functional-modules.js'), 'utf8');
|
||||
assert.match(registry, /name: 'trophy:active-catalog'.+classification: 'read-only'/);
|
||||
assert.match(registry, /name: 'trophy:installed-catalog'.+classification: 'read-only'/);
|
||||
assert.match(registry, /name: 'trophy:unlock-one'.+classification: 'sensitive'/);
|
||||
assert.doesNotMatch(service, /shell:\s*true|\bexec\(|upload|writeFile|unlockAll|syncTroph/i);
|
||||
assert.match(service, /FTP_PORT=1337/); assert.match(service, /DEBUG_PORT=764/);
|
||||
const web = fs.readFileSync(path.join(__dirname, '../../renderer/pork-web-shim.js'), 'utf8');
|
||||
for (const method of ['trophyActiveCatalog', 'trophyInstalledCatalog', 'trophyImportedCatalog',
|
||||
'trophyImportDefinition', 'trophyUnlockOne', 'trophyHistoryImportStatus', 'trophyHistoryPreview',
|
||||
'trophyHistoryApply']) assert.match(web, new RegExp(method));
|
||||
});
|
||||
|
||||
test('active catalog renderer shows state and conceals locked hidden definitions', () => {
|
||||
const catalog = normalizeActiveCatalog({ ...normalizeCatalog(adapterResult()),
|
||||
capabilities: { liveInventory: true, exactOneUnlock: true } });
|
||||
const markup = activeCatalogMarkup(catalog, (_key, fallback) => fallback);
|
||||
assert.match(markup, /Example/); assert.match(markup, /1\/2/);
|
||||
assert.match(markup, /Legacy \/ PS4 trophy API running on PS5/);
|
||||
assert.match(markup, /Start/); assert.match(markup, /Locked/);
|
||||
assert.match(markup, /Hidden trophy/); assert.doesNotMatch(markup, /Finish/);
|
||||
assert.doesNotMatch(markup, /End|data-unlock-id="1"/); assert.doesNotMatch(markup, /name="trophyId"/);
|
||||
const revealed = activeCatalogMarkup(catalog, (_key, fallback) => fallback, true);
|
||||
assert.match(revealed, /Finish/); assert.match(revealed, /End/);
|
||||
assert.match(revealed, /data-unlock-id="1"/); assert.doesNotMatch(revealed, /name="trophyId"/);
|
||||
});
|
||||
|
||||
test('premium trophy library merges game definitions with exact live progress', () => {
|
||||
const active = normalizeActiveCatalog({ ...normalizeCatalog(adapterResult()),
|
||||
capabilities: { liveInventory: true, exactOneUnlock: true } });
|
||||
const installed = normalizeInstalledCatalog({ available: true, readOnly: true, count: 2,
|
||||
games: [
|
||||
{ game: { title: 'Example', trophyCount: 2 }, trophies: adapterResult().entry.trophies.map(({ observedUnlocked, ...item }) => item) },
|
||||
{ game: { title: 'Library Game', trophyCount: 1 }, trophies: [{ id: 0, grade: 'silver', hidden: false, name: 'First step', description: 'Begin' }] },
|
||||
] });
|
||||
const library = normalizeTrophyLibrary({ activeCatalog: active, installedCatalog: installed });
|
||||
assert.equal(library.games.length, 2);
|
||||
assert.equal(library.games[0].title, 'Example');
|
||||
assert.equal(library.games[0].coverage, 'live');
|
||||
assert.deepEqual(library.games[0].counts, { total: 2, earned: 1, locked: 1 });
|
||||
assert.equal(library.games[0].trophies[1].earned, false);
|
||||
assert.equal(library.games[0].trophies[1].unlockEligible, true);
|
||||
assert.equal(library.games[1].coverage, 'definitions');
|
||||
assert.equal(library.games[1].trophies[0].earned, null);
|
||||
});
|
||||
|
||||
test('premium trophy library renders game cards, filters and catalog-derived unlock actions', () => {
|
||||
const active = normalizeActiveCatalog({ ...normalizeCatalog(adapterResult()),
|
||||
capabilities: { liveInventory: true, exactOneUnlock: true } });
|
||||
const installed = normalizeInstalledCatalog({ available: true, readOnly: true, count: 1,
|
||||
games: [{ game: { title: 'Example', trophyCount: 2 }, trophies: adapterResult().entry.trophies.map(({ observedUnlocked, ...item }) => item) }] });
|
||||
const library = normalizeTrophyLibrary({ activeCatalog: active, installedCatalog: installed });
|
||||
const markup = trophyLibraryMarkup(library, { selectedGameKey: library.games[0].key, revealHidden: true }, (_key, fallback) => fallback);
|
||||
assert.match(markup, /trophy-library-grid/);
|
||||
assert.match(markup, /trophy-library-game-card/);
|
||||
assert.match(markup, /1 of 2 earned/);
|
||||
assert.match(markup, /data-library-status="locked"/);
|
||||
assert.match(markup, /data-library-grade="gold"/);
|
||||
assert.match(markup, /data-library-unlock-id="1"/);
|
||||
assert.doesNotMatch(markup, /name="trophyId"|unlockAll/);
|
||||
const stylesheet = fs.readFileSync(path.join(__dirname, '../../renderer/styles/trophies-library.css'), 'utf8');
|
||||
assert.match(stylesheet, /\.trophy-library-shell/);
|
||||
assert.match(stylesheet, /scroll-snap-type:x mandatory/);
|
||||
assert.match(stylesheet, /prefers-reduced-motion:reduce/);
|
||||
});
|
||||
|
||||
test('normalizes a bounded installed definition catalog without identifiers', () => {
|
||||
const value = normalizeInstalledServiceCatalog({ status: 'ok', readOnly: true, persisted: false,
|
||||
identifiersReturned: false, count: 1, entries: [{ game: { title: 'Example', trophyCount: 2 },
|
||||
trophies: adapterResult().entry.trophies.map(({ observedUnlocked, ...item }) => item) }] });
|
||||
assert.equal(value.count, 1); assert.equal(value.games[0].game.title, 'Example');
|
||||
assert.equal(JSON.stringify(value).includes('catalogKey'), false);
|
||||
});
|
||||
|
||||
test('installed catalog renderer exposes every game and conceals hidden definitions', () => {
|
||||
const catalog = normalizeInstalledCatalog({ available: true, readOnly: true, count: 1,
|
||||
games: [{ game: { title: 'Library Game', trophyCount: 1 }, trophies: [
|
||||
{ id: 0, grade: 'gold', hidden: true, name: 'Secret', description: 'Spoiler' }
|
||||
] }] });
|
||||
const markup = installedCatalogMarkup(catalog, (_key, fallback) => fallback);
|
||||
assert.match(markup, /Library Game/); assert.match(markup, /Hidden trophy/);
|
||||
assert.doesNotMatch(markup, /Secret|Spoiler/);
|
||||
const revealed = installedCatalogMarkup(catalog, (_key, fallback) => fallback, true);
|
||||
assert.match(revealed, /Secret/); assert.match(revealed, /Spoiler/);
|
||||
});
|
||||
|
||||
test('single unlock requires exact confirmation and independently verifies the result', async t => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-unlock-trophy-'));
|
||||
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
||||
fs.mkdirSync(path.join(root, 'host'), { recursive: true }); fs.mkdirSync(path.join(root, 'artifacts'));
|
||||
const activeCli = Buffer.from('active'), unlockCli = Buffer.from('unlock'), activeElf = Buffer.from('reader'), unlockElf = Buffer.from('writer');
|
||||
fs.writeFileSync(path.join(root, 'host', 'live-active-catalog-cli.mjs'), activeCli);
|
||||
fs.writeFileSync(path.join(root, 'host', 'legacy-unlock-ps5debug-cli.mjs'), unlockCli);
|
||||
fs.writeFileSync(path.join(root, 'artifacts', 'chimera-ps5-legacy-query-resident.elf'), activeElf);
|
||||
fs.writeFileSync(path.join(root, 'artifacts', 'chimera-ps5-legacy-unlock-resident.elf'), unlockElf);
|
||||
let unlocked = false; const calls = [];
|
||||
const service = createLiveTrophyCatalogService({ appRoot: root, adapterRoot: root,
|
||||
settings: { get: () => 'console.test' }, secrets: { get: () => '00112233445566778899aabbccddeeff' },
|
||||
expectedElfSha256: crypto.createHash('sha256').update(activeElf).digest('hex'),
|
||||
expectedUnlockElfSha256: crypto.createHash('sha256').update(unlockElf).digest('hex'),
|
||||
expectedUnlockCliSha256: crypto.createHash('sha256').update(unlockCli).digest('hex'),
|
||||
runAdapter: async input => { calls.push(input); if (input.consent === 'CONSENT_LEGACY_UNLOCK_ONE_EXECUTE') { unlocked = true; return JSON.stringify({ status: 'ok', trophyId: 1, unlockedAfter: true, mappingRemoved: true, ownedHandleDestroyed: true, psnContacted: false }); }
|
||||
const value = adapterResult(); if (unlocked) value.entry.trophies[1].observedUnlocked = true; return JSON.stringify(value); } });
|
||||
await assert.rejects(service.unlockOne({ trophyId: 1, confirmation: 'wrong' }), /Exact confirmation/);
|
||||
const result = await service.unlockOne({ trophyId: 1, confirmation: 'UNLOCK 1 Example' });
|
||||
assert.equal(result.status, 'completed'); assert.equal(result.counts.unlocked, 2);
|
||||
assert.equal(calls.filter(call => call.consent === 'CONSENT_LEGACY_UNLOCK_ONE_EXECUTE').length, 1);
|
||||
});
|
||||
Reference in new issue
Block a user