diff --git a/tools/integration-tests/test/turnCommandNationMatrix.integration.test.ts b/tools/integration-tests/test/turnCommandNationMatrix.integration.test.ts index 78553f5..92ffdf6 100644 --- a/tools/integration-tests/test/turnCommandNationMatrix.integration.test.ts +++ b/tools/integration-tests/test/turnCommandNationMatrix.integration.test.ts @@ -14,6 +14,10 @@ const integration = describe.skipIf(!workspaceRoot || process.env.TURN_DIFFERENT const readGold = (row: { gold?: unknown } | undefined): number => (typeof row?.gold === 'number' ? row.gold : 0); const readNationResource = (row: { gold?: unknown; rice?: unknown } | undefined, resource: 'gold' | 'rice'): number => typeof row?.[resource] === 'number' ? row[resource] : 0; +const readNationMeta = (row: { meta?: unknown } | undefined): Record => + typeof row?.meta === 'object' && row.meta !== null && !Array.isArray(row.meta) + ? (row.meta as Record) + : {}; const NPC_SEIZURE_MESSAGE_TEXT = '몰수를 하다니... 이것이 윗사람이 할 짓이란 말입니까...'; const ignoredLifecyclePaths = [ @@ -182,6 +186,17 @@ const buildRequest = ( { ...general(1, 1, 3, 12), ...fixturePatches.generals?.[1] }, { ...general(2, 2, 70, 12), ...fixturePatches.generals?.[2] }, { ...general(3, 1, 3, 1), ...fixturePatches.generals?.[3] }, + ...Object.entries(fixturePatches.generals ?? {}) + .filter(([id]) => !['1', '2', '3'].includes(id)) + .map(([id, patch]) => ({ + ...general( + Number(id), + typeof patch.nationId === 'number' ? patch.nationId : 0, + typeof patch.cityId === 'number' ? patch.cityId : 3, + typeof patch.officerLevel === 'number' ? patch.officerLevel : 1 + ), + ...patch, + })), ], ...(fixturePatches.troops ? { troops: fixturePatches.troops } : {}), ...(fixturePatches.randomFoundingCandidateCityIds @@ -1028,3 +1043,108 @@ integration('nation material aid target and input constraints', () => { 120_000 ); }); + +integration('nation material aid accumulated assistance and officer logs', () => { + it.each([ + { + name: 'array entry', + priorEntry: [1, 250], + }, + { + name: 'legacy object entry', + priorEntry: { 0: 1, 1: 250 }, + }, + ])( + 'accumulates a repeated aid amount from an existing $name', + async ({ priorEntry }) => { + const request = buildRequest( + 'che_물자원조', + { + destNationID: 2, + amountList: [100, 200], + }, + { + nations: { + 2: { + nationEnv: { + recv_assist: { + n1: priorEntry, + n9: [9, 400], + }, + }, + }, + }, + } + ); + const reference = runReferenceTurnCommandTraceRequest( + workspaceRoot!, + request as unknown as Record + ); + const core = await runCoreTurnCommandTrace(request, reference.before); + const referenceDest = reference.after.nations.find((entry) => entry.id === 2); + const coreDest = core.after.nations.find((entry) => entry.id === 2); + + expect(readNationMeta(referenceDest).recv_assist).toEqual({ + n1: [1, 550], + n9: [9, 400], + }); + expect(readNationMeta(coreDest).recv_assist).toEqual(readNationMeta(referenceDest).recv_assist); + expect(core.rng).toEqual(reference.rng); + expect( + compareTurnSnapshotDeltas(reference.before, reference.after, core.before, core.after, { + ignoredPathPatterns: ignoredLifecyclePaths, + }) + ).toEqual([]); + }, + 120_000 + ); + + it('notifies eligible officers in both nations with the legacy messages', async () => { + const request = buildRequest( + 'che_물자원조', + { + destNationID: 2, + amountList: [100, 200], + }, + { + generals: { + 3: { officerLevel: 5 }, + 5: { nationId: 2, cityId: 70, officerLevel: 5, officerCityId: 70 }, + }, + } + ); + const reference = runReferenceTurnCommandTraceRequest( + workspaceRoot!, + request as unknown as Record + ); + const core = await runCoreTurnCommandTrace(request, reference.before); + const referenceLogs = reference.after.logs.slice(reference.before.logs.length); + const sourceOfficerLog = { + generalId: 3, + text: expect.stringContaining('타국으로 금100 쌀200을 지원했습니다.'), + }; + const destinationOfficerLog = { + generalId: 5, + text: expect.stringContaining('아국에서 금100 쌀200을 원조했습니다.'), + }; + + expect(referenceLogs).toEqual( + expect.arrayContaining([ + expect.objectContaining(sourceOfficerLog), + expect.objectContaining(destinationOfficerLog), + ]) + ); + expect(core.after.logs).toEqual( + expect.arrayContaining([ + expect.objectContaining(sourceOfficerLog), + expect.objectContaining(destinationOfficerLog), + ]) + ); + expect(core.rng).toEqual(reference.rng); + expect( + compareTurnSnapshotDeltas(reference.before, reference.after, core.before, core.after, { + ignoredPathPatterns: ignoredLifecyclePaths, + }) + ).toEqual([]); + }, 120_000); +});