'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 { createImportedTrophyDefinitionService } = require('../../src/services/imported-trophy-definition-service'); const { createMemoryTrophyProgressionPlanRepository, createTrophyProgressionPlanner } = require('../../src/services/trophy-progression-planner-service'); const root = path.resolve(__dirname, '../..'); const read = file => fs.readFileSync(path.join(root, file), 'utf8'); test('desktop and web expose planning commands but no live progression apply command', () => { const main = read('main.js'); const preload = read('preload.js'); const web = read('renderer/pork-web-shim.js'); const server = read('src/server/register-functional-modules.js'); assert.match(main, /_registerTrophyProgression\(ipcMain, \{ app, db \}\)/); assert.match(read('src/ipc/trophy-progression.js'), /trophy:imported-catalog:import/); for (const channel of ['status', 'catalogs', 'create', 'plans', 'plan']) { assert.match(preload, new RegExp(`trophy:progression:${channel}`)); assert.match(web, new RegExp(`trophy:progression:${channel}`)); assert.match(server, new RegExp(`trophy:progression:${channel}`)); } assert.doesNotMatch(preload, /trophy:progression:apply/); assert.doesNotMatch(web, /trophy:progression:apply/); assert.doesNotMatch(server, /name: 'trophy:progression:apply'/); }); test('progression planning persists locally and remains separated from PSN providers', () => { const ipc = read('src/ipc/trophy-progression.js'); assert.match(ipc, /createImportedTrophyDefinitionService/); assert.match(ipc, /createSqliteTrophyProgressionPlanRepository/); assert.doesNotMatch(ipc, /psn-api|node-fetch|https?:|trophyHistory/); const service = read('src/services/trophy-progression-planner-service.js'); assert.match(service, /psnWriteCapability: false/); assert.match(service, /liveApply: \{ available: false/); }); test('a local definition package can produce a progression preview without PSN import', async t => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-progression-catalog-')); t.after(() => fs.rmSync(directory, { recursive: true, force: true })); const catalogs = createImportedTrophyDefinitionService({ root: directory }); const catalogKey = 'np:22222222222222222222222222222222'; catalogs.importPackage({ schema: 'chimera-trophy-definition', schemaVersion: 1, catalogKey, revision: 'fixture', game: { title: 'Offline Fixture', description: '', trophyCount: 2 }, trophies: [ { id: 0, grade: 'bronze', groupId: 0, hidden: false, name: 'Start', description: 'Begin the story.' }, { id: 1, grade: 'platinum', groupId: 0, hidden: false, name: 'Done', description: 'Earn all trophies.' }, ] }); const planner = createTrophyProgressionPlanner({ repository: createMemoryTrophyProgressionPlanRepository(), catalogProvider: catalogs, now: () => Date.parse('2026-08-11T12:00:00Z') }); const plan = await planner.create({ catalogKey, mode: 'synthetic_unlock', trophyIds: [0, 1], strategy: 'balanced', minimumIntervalMinutes: 30, startDateTime: '2021-01-01T00:00:00Z', endDateTime: '2021-01-03T00:00:00Z' }); assert.equal(plan.provenance.psnImportRequired, false); assert.deepEqual(plan.items.map(item => item.trophyId), [0, 1]); });