'use strict'; const assert = require('node:assert/strict'); const crypto = require('node:crypto'); const test = require('node:test'); const { CONSENT } = require('../../src/services/inactive-legacy-trophy-unlock-service'); const { createLegacyDetachedTrophyMutationAdapter } = require('../../src/services/legacy-detached-trophy-mutation-adapter'); function sha256(value) { return crypto.createHash('sha256').update(value).digest('hex'); } test('native inactive mutation adapter is fail-closed without artifact pins', () => { assert.deepEqual(createLegacyDetachedTrophyMutationAdapter().status(), { available: false, reasonCode: 'NATIVE_MUTATION_ADAPTER_NOT_PINNED', }); }); test('native inactive mutation adapter verifies both artifacts and forwards an exact-one request', async () => { const cliPath = 'cli'; const elfPath = 'elf'; const cli = Buffer.from('cli'); const elf = Buffer.from('elf'); const files = new Map([[cliPath, cli], [elfPath, elf]]); const catalogSha256 = 'a'.repeat(64); const trophySha256 = 'b'.repeat(64); const backupSha256 = 'c'.repeat(64); let forwarded; const adapter = createLegacyDetachedTrophyMutationAdapter({ cliPath, elfPath, expectedCliSha256: sha256(cli), expectedElfSha256: sha256(elf), fs: { existsSync: (candidate) => files.has(candidate), readFileSync: (candidate) => files.get(candidate) }, runAdapter: async (request) => { forwarded = request; return { status: 'unlocked', catalogSha256, trophySha256, targetInactive: true, unlockCalls: 1, psnRequests: 0, contextDestroyed: true, handleDestroyed: true, filesPersisted: 0, }; }, }); const result = await adapter.unlockOne({ consent: CONSENT, catalogSha256, trophySha256, trophyId: 7, backupSha256, }); assert.equal(result.status, 'unlocked'); assert.equal(forwarded.trophyId, 7); assert.equal(forwarded.expectedElfSha256, sha256(elf)); assert.equal(Object.hasOwn(forwarded, 'titleId'), false); }); test('native inactive mutation adapter rejects a hash mismatch before execution', async () => { let ran = false; const files = new Map([['cli', Buffer.from('cli')], ['elf', Buffer.from('elf')]]); const adapter = createLegacyDetachedTrophyMutationAdapter({ cliPath: 'cli', elfPath: 'elf', expectedCliSha256: '0'.repeat(64), expectedElfSha256: sha256(files.get('elf')), fs: { existsSync: (candidate) => files.has(candidate), readFileSync: (candidate) => files.get(candidate) }, runAdapter: async () => { ran = true; }, }); assert.equal(adapter.status().reasonCode, 'NATIVE_MUTATION_ADAPTER_HASH_MISMATCH'); await assert.rejects(adapter.unlockOne({}), /unavailable/i); assert.equal(ran, false); }); test('native inactive mutation adapter rejects caller-supplied title metadata', async () => { const files = new Map([['cli', Buffer.from('cli')], ['elf', Buffer.from('elf')]]); const adapter = createLegacyDetachedTrophyMutationAdapter({ cliPath: 'cli', elfPath: 'elf', expectedCliSha256: sha256(files.get('cli')), expectedElfSha256: sha256(files.get('elf')), fs: { existsSync: (candidate) => files.has(candidate), readFileSync: (candidate) => files.get(candidate) }, runAdapter: async () => { throw new Error('must not run'); }, }); await assert.rejects(adapter.unlockOne({ titleId: 'CUSA00000' }), /forbidden field/i); });