Files
core2026/app/game-engine/test/tournamentAutoStart.test.ts
T

147 lines
5.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { RedisConnector } from '@sammo-ts/infra';
import type { Nation } from '@sammo-ts/logic';
import { InMemoryTurnWorld } from '../src/turn/inMemoryWorld.js';
import { createTournamentAutoStartHandler } from '../src/turn/tournamentAutoStart.js';
import type { TurnWorldSnapshot, TurnWorldState } from '../src/turn/types.js';
const buildNation = (id: number): Nation => ({
id,
name: `국가${id}`,
color: '#777777',
capitalCityId: null,
chiefGeneralId: null,
gold: 0,
rice: 0,
power: 0,
level: 1,
typeCode: 'che_중립',
meta: {},
});
describe('monthly tournament auto start', () => {
it('uses the legacy monthly RNG after nation power rolls and persists the remaining pattern', async () => {
const state: TurnWorldState = {
id: 1,
currentYear: 193,
currentMonth: 1,
tickSeconds: 600,
lastTurnTime: new Date('0193-01-01T00:00:00.000Z'),
meta: {
hiddenSeed: 'monthly-post-tail-2',
tournamentPattern: [0, 1, 2, 3],
},
};
const snapshot: TurnWorldSnapshot = {
scenarioConfig: {
stat: { total: 300, min: 10, max: 100, npcTotal: 150, npcMax: 50, npcMin: 10, chiefMin: 70 },
iconPath: '',
map: {},
const: {},
environment: { mapName: 'test', unitSet: 'default' },
},
map: { id: 'test', name: 'test', cities: [] },
diplomacy: [],
events: [],
initialEvents: [],
generals: [],
cities: [],
nations: [buildNation(1), buildNation(2)],
troops: [],
};
const values = new Map<string, string>();
const redis = {
get: async (key: string) => values.get(key) ?? null,
set: async (key: string, value: string) => {
values.set(key, value);
return 'OK';
},
} as unknown as RedisConnector['client'];
let world: InMemoryTurnWorld | null = null;
const consumed: boolean[] = [];
world = new InMemoryTurnWorld(state, snapshot, {
schedule: { entries: [{ startMinute: 0, tickMinutes: 10 }] },
calendarHandler: createTournamentAutoStartHandler({
profileName: 'test',
getWorld: () => world,
getRedisClient: () => redis,
getWorldConfig: () => ({ tournamentTrig: true }),
getNationPowerRollCount: () => 2,
onTournamentRollConsumed: (value) => consumed.push(value),
now: () => new Date('2026-07-25T00:00:00.000Z'),
}),
});
await world.advanceMonth(new Date('0193-02-01T00:00:00.000Z'));
expect(consumed).toEqual([false, true]);
expect(JSON.parse(values.get('sammo:test:tournament:state') ?? '{}')).toMatchObject({
stage: 1,
phase: 0,
type: 3,
auto: true,
openYear: 193,
openMonth: 2,
termSeconds: 600,
nextAt: '2026-07-25T00:10:00.000Z',
});
expect(world.getState().meta.tournamentPattern).toEqual([0, 1, 2]);
expect(world.peekDirtyState().logs).toEqual([
expect.objectContaining({
text: "<B><b>【대회】</b></><C>설전</> 대회가 개최됩니다! 천하의 <span class='ev_highlight'>책사</span>들을 모집하고 있습니다!",
}),
]);
});
it('does not consume a tournament roll while a tournament is active', async () => {
const state: TurnWorldState = {
id: 1,
currentYear: 193,
currentMonth: 1,
tickSeconds: 600,
lastTurnTime: new Date('0193-01-01T00:00:00.000Z'),
meta: { hiddenSeed: 'active-tournament' },
};
const snapshot: TurnWorldSnapshot = {
scenarioConfig: {
stat: { total: 300, min: 10, max: 100, npcTotal: 150, npcMax: 50, npcMin: 10, chiefMin: 70 },
iconPath: '',
map: {},
const: {},
environment: { mapName: 'test', unitSet: 'default' },
},
map: { id: 'test', name: 'test', cities: [] },
diplomacy: [],
events: [],
initialEvents: [],
generals: [],
cities: [],
nations: [buildNation(1)],
troops: [],
};
const redis = {
get: async () => JSON.stringify({ stage: 1 }),
set: async () => 'OK',
} as unknown as RedisConnector['client'];
let world: InMemoryTurnWorld | null = null;
const consumed: boolean[] = [];
world = new InMemoryTurnWorld(state, snapshot, {
schedule: { entries: [{ startMinute: 0, tickMinutes: 10 }] },
calendarHandler: createTournamentAutoStartHandler({
profileName: 'test',
getWorld: () => world,
getRedisClient: () => redis,
getWorldConfig: () => ({ tournamentTrig: true }),
getNationPowerRollCount: () => 1,
onTournamentRollConsumed: (value) => consumed.push(value),
}),
});
await world.advanceMonth(new Date('0193-02-01T00:00:00.000Z'));
expect(consumed).toEqual([false]);
expect(world.peekDirtyState().logs).toEqual([]);
});
});