feat: 예약 턴 명령별 중간 외교 전이와 실행 정보를 기록
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import type { LogEntryDraft, TurnSchedule, UnitSetDefinition } from '@sammo-ts/logic';
|
||||
import { DIPLOMACY_STATE, LogCategory, LogFormat, LogScope } from '@sammo-ts/logic';
|
||||
import type { InMemoryTurnWorld, TurnCalendarHandler } from '../src/turn/inMemoryWorld.js';
|
||||
@@ -71,7 +71,7 @@ const buildUnificationLog = (nationName: string): LogEntryDraft => ({
|
||||
});
|
||||
|
||||
describe('NPC 선전포고·개전·점령 흐름 테스트', () => {
|
||||
it('선전포고부터 개전과 첫 도시 점령까지 진행되어야 한다', async () => {
|
||||
it.each([false, true])('감사 수집 %s에서 선전포고부터 개전과 첫 도시 점령까지 진행되어야 한다', async (auditEnabled) => {
|
||||
const cities = buildLargeTestCities().map(maxCityStats);
|
||||
const cityA1 = cities.find((city) => city.id === 1)!;
|
||||
const cityA2 = cities.find((city) => city.id === 2)!;
|
||||
@@ -206,7 +206,7 @@ describe('NPC 선전포고·개전·점령 흐름 테스트', () => {
|
||||
currentMonth: 5,
|
||||
tickSeconds: 600,
|
||||
lastTurnTime: mockDate,
|
||||
meta: { seed: 1, initYear: 183, initMonth: 5 },
|
||||
meta: { seed: 1, initYear: 183, initMonth: 5, ...(auditEnabled ? { serverId: 'npc-declaration-audit' } : {}) },
|
||||
};
|
||||
|
||||
const schedule: TurnSchedule = {
|
||||
@@ -278,6 +278,7 @@ describe('NPC 선전포고·개전·점령 흐름 테스트', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const auditQueue = vi.spyOn(worldRef.current!, 'queueAuditDiplomacy');
|
||||
const debug = createWorldDebugger(() => worldRef.current, {
|
||||
nationIds: [1, 2],
|
||||
cityIds: [cityA1.id, cityA2.id, cityB1.id, cityB2.id],
|
||||
@@ -326,6 +327,13 @@ describe('NPC 선전포고·개전·점령 흐름 테스트', () => {
|
||||
}
|
||||
expect(declareEntry).not.toBeNull();
|
||||
expect(declareEntry?.state).toBe(DIPLOMACY_STATE.DECLARATION);
|
||||
const declarationAudit = auditQueue.mock.calls.map(([event]) => event).filter(
|
||||
(event) => event.actor?.actionKey === 'che_선전포고'
|
||||
);
|
||||
expect(declarationAudit).toHaveLength(auditEnabled ? 2 : 0);
|
||||
expect(declarationAudit.every((event) => event.eventType === 'TURN_RELATION_CHANGED' && event.actor?.kind === 'nation')).toBe(true);
|
||||
expect(declarationAudit.map((event) => event.after?.state)).toEqual(auditEnabled ? [DIPLOMACY_STATE.DECLARATION, DIPLOMACY_STATE.DECLARATION] : []);
|
||||
|
||||
|
||||
const remainTurns = Math.max(0, (declareEntry?.term ?? 0) - 1);
|
||||
const preWarTarget = addMonths(world!.getState().currentYear, world!.getState().currentMonth, remainTurns);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { City, Nation } from '@sammo-ts/logic';
|
||||
import { InMemoryTurnWorld } from '../src/turn/inMemoryWorld.js';
|
||||
import { InMemoryTurnWorld, type GeneralTurnHandler } from '../src/turn/inMemoryWorld.js';
|
||||
import type { TurnGeneral, TurnWorldSnapshot, TurnWorldState } from '../src/turn/types.js';
|
||||
import {
|
||||
createPlayAuditHandler,
|
||||
@@ -77,7 +77,7 @@ const buildNation = (id: number, power: number, meta: Nation['meta']): Nation =>
|
||||
meta,
|
||||
});
|
||||
|
||||
const buildWorld = () => {
|
||||
const buildWorld = (generalTurnHandler?: GeneralTurnHandler) => {
|
||||
const state: TurnWorldState = {
|
||||
id: 1,
|
||||
currentYear: 200,
|
||||
@@ -122,11 +122,53 @@ const buildWorld = () => {
|
||||
};
|
||||
const world = new InMemoryTurnWorld(state, snapshot, {
|
||||
schedule: { entries: [{ startMinute: 0, tickMinutes: 10 }] },
|
||||
generalTurnHandler,
|
||||
});
|
||||
|
||||
return world;
|
||||
};
|
||||
describe('play audit collection durability state', () => {
|
||||
it('preserves consecutive diplomacy transitions in one turn and restores them with the checkpoint', () => {
|
||||
const world = buildWorld({
|
||||
execute: ({ general }) => ({
|
||||
diplomacyPatches: [1, 0].map((state, index) => ({
|
||||
srcNationId: 1,
|
||||
destNationId: 2,
|
||||
patch: { state, term: 6 },
|
||||
audit: {
|
||||
actionKey: index === 0 ? 'che_선전포고' : 'che_급습',
|
||||
kind: 'nation',
|
||||
actionOrdinal: index + 1,
|
||||
actor: {
|
||||
generalId: general.id,
|
||||
userId: null,
|
||||
name: general.name,
|
||||
nationId: 1,
|
||||
officerLevel: 12,
|
||||
npcState: 2,
|
||||
},
|
||||
},
|
||||
})),
|
||||
}),
|
||||
});
|
||||
const checkpoint = world.captureState();
|
||||
const general = world.getGeneralById(3)!;
|
||||
world.executeGeneralTurn(general);
|
||||
const events = world.peekDirtyState().pendingAuditDiplomacy;
|
||||
expect(events).toHaveLength(2);
|
||||
expect(events.map((event) => [event.before?.state, event.after?.state])).toEqual([
|
||||
[2, 1],
|
||||
[1, 0],
|
||||
]);
|
||||
expect(events.map((event) => event.ordinal)).toEqual([1, 2]);
|
||||
expect(new Set(events.map((event) => event.executionId)).size).toBe(1);
|
||||
expect(events.map((event) => event.actor?.actionKey)).toEqual(['che_선전포고', 'che_급습']);
|
||||
world.restoreState(checkpoint);
|
||||
expect(world.peekDirtyState().pendingAuditDiplomacy).toEqual([]);
|
||||
world.executeGeneralTurn(world.getGeneralById(3)!);
|
||||
expect(world.peekDirtyState().pendingAuditDiplomacy).toEqual(events);
|
||||
});
|
||||
|
||||
it('freezes the initial observation separately from month-end and restores its marker on rollback', () => {
|
||||
const world = buildWorld();
|
||||
const before = world.captureState();
|
||||
|
||||
Reference in New Issue
Block a user