'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const { createPsnTrophyHistoryImportService, titlePlatform } = require('../../src/services/psn-trophy-history-import-service'); function fixture({ missingTimestamp = false } = {}) { const stored = new Map(); const saved = []; const secrets = { set(key, value) { value ? stored.set(key, String(value)) : stored.delete(key); }, get: key => stored.get(key) || '', has: key => stored.has(key) }; const psnApi = { async exchangeNpssoForAccessCode() { return 'access-code'; }, async exchangeAccessCodeForAuthTokens() { return { accessToken: 'access-token', refreshToken: 'refresh-token', expiresIn: 3600 }; }, async exchangeRefreshTokenForAuthTokens() { return { accessToken: 'new-access-token', refreshToken: 'new-refresh-token', expiresIn: 3600 }; }, async makeUniversalSearch() { return { domainResponses: [{ results: [{ socialMetadata: { accountId: '1234567890123456789', onlineId: 'Nuklear_Rabbit', country: 'BE', language: 'nl-BE' } }] }] }; }, async getUserTitles(_auth, _account, options) { if (options.offset) return { trophyTitles: [], totalItemCount: 2 }; return { totalItemCount: 2, trophyTitles: [ { npCommunicationId: 'NPWR10001_00', npServiceName: 'trophy', trophyTitleName: 'Legacy Game', trophyTitlePlatform: 'PS4', progress: 50, earnedTrophies: { bronze: 1 }, lastUpdatedDateTime: '2017-03-04T05:06:07Z' }, { npCommunicationId: 'NPWR20002_00', npServiceName: 'trophy2', trophyTitleName: 'Native Game', trophyTitlePlatform: 'PS5', progress: 100, earnedTrophies: { gold: 1 }, lastUpdatedDateTime: '2024-02-03T04:05:06Z' }, ] }; }, async getTitleTrophyGroups(_auth, communicationId) { return { trophyGroups: [{ trophyGroupId: 'default', trophyGroupName: communicationId }] }; }, async getTitleTrophies(_auth, communicationId) { return { totalItemCount: 1, trophies: [{ trophyId: communicationId.includes('20002') ? 2 : 1, trophyGroupId: 'default', trophyName: `${communicationId} trophy`, trophyDetail: 'Original detail', trophyType: communicationId.includes('20002') ? 'gold' : 'bronze', trophyHidden: communicationId.includes('20002') }] }; }, async getUserTrophiesEarnedForTitle(_auth, _account, communicationId) { return { totalItemCount: 1, trophies: [{ trophyId: communicationId.includes('20002') ? 2 : 1, earned: true, earnedDateTime: missingTimestamp ? undefined : (communicationId.includes('20002') ? '2024-02-03T04:05:06.789Z' : '2017-03-04T05:06:07.123Z') }] }; }, }; const repository = { save(parsed, provenance) { const record = { id: `psn-${parsed.canonicalSha256}`, canonicalSha256: parsed.canonicalSha256, importedAt: provenance.importedAt, titleCount: parsed.titleCount, trophyCount: parsed.trophyCount, snapshot: parsed.snapshot, provenance }; saved.push(record); return record; }, get(id) { return saved.find(record => record.id === id) || null; }, list() { return [...saved].reverse(); } }; return { service: createPsnTrophyHistoryImportService({ repository, secrets, psnApi, now: () => 1_800_000_000_000, minIntervalMs: 0 }), saved, secrets }; } test('PSN history importer previews all platforms and imports only the frozen selection', async () => { const { service, saved, secrets } = fixture(); const auth = await service.authenticate({ npsso: 'a'.repeat(64) }); assert.equal(auth.configured, true); assert.equal(secrets.get('trophy.psn.history.access-token'), 'access-token'); const preview = await service.preview({ onlineId: 'Nuklear_Rabbit' }); assert.deepEqual(preview.games.map(game => game.platform), ['PS4', 'PS5']); assert.equal(preview.games[1].service, 'trophy2'); const result = await service.importSelection({ previewId: preview.previewId, selection: { mode: 'selected', keys: [preview.games[1].key] } }); assert.equal(result.games.length, 1); assert.equal(result.games[0].platform, 'PS5'); assert.equal(saved[0].snapshot.titles[0].service, 'trophy2'); assert.equal(saved[0].snapshot.titles[0].trophies[0].hidden, true); assert.equal(saved[0].snapshot.titles[0].trophies[0].earnedDateTime, '2024-02-03T04:05:06.789Z'); assert.equal(saved[0].provenance.kind, 'provider-fetch'); assert.match(result.snapshot.account.accountId, /\*+6789$/); assert.equal(service.listSnapshots().length, 1); const detail = service.getSnapshot({ id: result.snapshot.id }); assert.equal(detail.titles[0].trophies[0].earnedDateTime, '2024-02-03T04:05:06.789Z'); assert.equal(JSON.stringify(detail).includes('1234567890123456789'), false); }); test('mass import retains original timestamps for legacy and Trophy2 sets', async () => { const { service, saved } = fixture(); await service.authenticate({ npsso: 'b'.repeat(64) }); const preview = await service.preview({ onlineId: 'Nuklear_Rabbit' }); await service.importSelection({ previewId: preview.previewId, selection: { mode: 'all' } }); assert.deepEqual(saved[0].snapshot.titles.map(title => title.platform), ['PS4', 'PS5']); assert.deepEqual(saved[0].snapshot.titles.map(title => title.trophies[0].earnedDateTime), ['2017-03-04T05:06:07.123Z', '2024-02-03T04:05:06.789Z']); }); test('earned trophy without an original timestamp rejects the complete import', async () => { const { service, saved } = fixture({ missingTimestamp: true }); await service.authenticate({ npsso: 'c'.repeat(64) }); const preview = await service.preview({ onlineId: 'Nuklear_Rabbit' }); await assert.rejects(() => service.importSelection({ previewId: preview.previewId, selection: { mode: 'selected', keys: [preview.games[0].key] } }), error => error.code === 'TROPHY_IMPORT_TIMESTAMP'); assert.equal(saved.length, 0); }); test('platform mapping keeps native Trophy2 distinct and accepts historical systems', () => { assert.equal(titlePlatform({ npServiceName: 'trophy2', trophyTitlePlatform: 'PS5' }), 'PS5'); assert.equal(titlePlatform({ npServiceName: 'trophy', trophyTitlePlatform: 'PS3' }), 'PS3'); assert.equal(titlePlatform({ npServiceName: 'trophy', trophyTitlePlatform: 'PSVita' }), 'PSVITA'); });