285 lines
7.2 KiB
TypeScript
285 lines
7.2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import type { BattleSimJobPayload } from '../src/battleSim/types.js';
|
|
import { processBattleSimJob } from '../src/battleSim/processor.js';
|
|
|
|
const buildPayload = (action: BattleSimJobPayload['action']): BattleSimJobPayload => ({
|
|
action,
|
|
repeatCnt: 1,
|
|
year: 200,
|
|
month: 1,
|
|
seed: 'test-seed',
|
|
attackerGeneral: {
|
|
no: 1,
|
|
name: 'Attacker',
|
|
nation: 1,
|
|
turntime: '2026-01-01 00:00:00',
|
|
personal: null,
|
|
special2: null,
|
|
crew: 1000,
|
|
crewtype: 100,
|
|
atmos: 100,
|
|
train: 100,
|
|
intel: 70,
|
|
intel_exp: 0,
|
|
book: null,
|
|
strength: 70,
|
|
strength_exp: 0,
|
|
weapon: null,
|
|
injury: 0,
|
|
leadership: 70,
|
|
leadership_exp: 0,
|
|
horse: null,
|
|
item: null,
|
|
explevel: 0,
|
|
experience: 100,
|
|
dedication: 100,
|
|
officer_level: 3,
|
|
officer_city: 1,
|
|
gold: 1000,
|
|
rice: 1000,
|
|
dex1: 0,
|
|
dex2: 0,
|
|
dex3: 0,
|
|
dex4: 0,
|
|
dex5: 0,
|
|
defence_train: 0,
|
|
recent_war: null,
|
|
warnum: 0,
|
|
killnum: 0,
|
|
killcrew: 0,
|
|
},
|
|
attackerCity: {
|
|
city: 1,
|
|
nation: 1,
|
|
supply: 1,
|
|
name: 'AttackerCity',
|
|
pop: 10000,
|
|
agri: 1000,
|
|
comm: 1000,
|
|
secu: 1000,
|
|
def: 100,
|
|
wall: 100,
|
|
trust: 100,
|
|
level: 2,
|
|
pop_max: 10000,
|
|
agri_max: 1000,
|
|
comm_max: 1000,
|
|
secu_max: 1000,
|
|
def_max: 200,
|
|
wall_max: 200,
|
|
dead: 0,
|
|
state: 0,
|
|
conflict: '{}',
|
|
},
|
|
attackerNation: {
|
|
type: 'test',
|
|
tech: 1000,
|
|
level: 1,
|
|
capital: 1,
|
|
nation: 1,
|
|
name: 'AttackerNation',
|
|
gold: 1000,
|
|
rice: 1000,
|
|
gennum: 1,
|
|
},
|
|
defenderGenerals: [
|
|
{
|
|
no: 2,
|
|
name: 'Defender',
|
|
nation: 2,
|
|
turntime: '2026-01-01 00:00:00',
|
|
personal: null,
|
|
special2: null,
|
|
crew: 1000,
|
|
crewtype: 100,
|
|
atmos: 100,
|
|
train: 100,
|
|
intel: 60,
|
|
intel_exp: 0,
|
|
book: null,
|
|
strength: 60,
|
|
strength_exp: 0,
|
|
weapon: null,
|
|
injury: 0,
|
|
leadership: 60,
|
|
leadership_exp: 0,
|
|
horse: null,
|
|
item: null,
|
|
explevel: 0,
|
|
experience: 100,
|
|
dedication: 100,
|
|
officer_level: 3,
|
|
officer_city: 2,
|
|
gold: 1000,
|
|
rice: 1000,
|
|
dex1: 0,
|
|
dex2: 0,
|
|
dex3: 0,
|
|
dex4: 0,
|
|
dex5: 0,
|
|
defence_train: 0,
|
|
recent_war: null,
|
|
warnum: 0,
|
|
killnum: 0,
|
|
killcrew: 0,
|
|
},
|
|
],
|
|
defenderCity: {
|
|
city: 2,
|
|
nation: 2,
|
|
supply: 1,
|
|
name: 'DefenderCity',
|
|
pop: 10000,
|
|
agri: 1000,
|
|
comm: 1000,
|
|
secu: 1000,
|
|
def: 100,
|
|
wall: 100,
|
|
trust: 100,
|
|
level: 2,
|
|
pop_max: 10000,
|
|
agri_max: 1000,
|
|
comm_max: 1000,
|
|
secu_max: 1000,
|
|
def_max: 200,
|
|
wall_max: 200,
|
|
dead: 0,
|
|
state: 0,
|
|
conflict: '{}',
|
|
},
|
|
defenderNation: {
|
|
type: 'test',
|
|
tech: 1000,
|
|
level: 1,
|
|
capital: 2,
|
|
nation: 2,
|
|
name: 'DefenderNation',
|
|
gold: 1000,
|
|
rice: 1000,
|
|
gennum: 1,
|
|
},
|
|
unitSet: {
|
|
id: 'test',
|
|
name: 'test',
|
|
crewTypes: [
|
|
{
|
|
id: 100,
|
|
armType: 1,
|
|
name: '보병',
|
|
attack: 100,
|
|
defence: 100,
|
|
speed: 7,
|
|
avoid: 10,
|
|
magicCoef: 0,
|
|
cost: 9,
|
|
rice: 9,
|
|
requirements: [],
|
|
attackCoef: {},
|
|
defenceCoef: {},
|
|
info: [],
|
|
initSkillTrigger: null,
|
|
phaseSkillTrigger: null,
|
|
iActionList: null,
|
|
},
|
|
{
|
|
id: 999,
|
|
armType: 9,
|
|
name: '성벽',
|
|
attack: 0,
|
|
defence: 0,
|
|
speed: 1,
|
|
avoid: 0,
|
|
magicCoef: 0,
|
|
cost: 0,
|
|
rice: 9,
|
|
requirements: [],
|
|
attackCoef: {},
|
|
defenceCoef: {},
|
|
info: [],
|
|
initSkillTrigger: null,
|
|
phaseSkillTrigger: null,
|
|
iActionList: null,
|
|
},
|
|
],
|
|
},
|
|
config: {
|
|
armPerPhase: 500,
|
|
maxTrainByCommand: 100,
|
|
maxAtmosByCommand: 100,
|
|
maxTrainByWar: 110,
|
|
maxAtmosByWar: 150,
|
|
castleCrewTypeId: 999,
|
|
armTypes: {
|
|
footman: 1,
|
|
wizard: 4,
|
|
siege: 5,
|
|
misc: 6,
|
|
castle: 9,
|
|
},
|
|
},
|
|
time: {
|
|
year: 200,
|
|
month: 1,
|
|
startYear: 180,
|
|
},
|
|
});
|
|
|
|
describe('battle sim processor', () => {
|
|
it('returns the fixed-seed battle summary instead of only a successful shape', () => {
|
|
const payload = buildPayload('battle');
|
|
const result = processBattleSimJob(payload);
|
|
|
|
expect(result).toMatchObject({
|
|
result: true,
|
|
reason: 'success',
|
|
datetime: '2026-01-01 00:00:00',
|
|
avgWar: 1,
|
|
phase: 2,
|
|
killed: 625,
|
|
maxKilled: 625,
|
|
minKilled: 625,
|
|
dead: 1000,
|
|
maxDead: 1000,
|
|
minDead: 1000,
|
|
attackerRice: 65,
|
|
defenderRice: 83,
|
|
attackerSkills: { 부상: 1 },
|
|
defendersSkills: [{ 회피시도: 1, 회피: 1 }],
|
|
});
|
|
expect(result.lastWarLog?.generalActionLog).toContain('퇴각했습니다.');
|
|
});
|
|
|
|
it('returns the fixed defender ID order for reorder action', () => {
|
|
const payload = buildPayload('reorder');
|
|
const result = processBattleSimJob(payload);
|
|
|
|
expect(result.result).toBe(true);
|
|
expect(result.order).toEqual([2]);
|
|
});
|
|
|
|
it('executes crew trigger handlers in simulator battles', () => {
|
|
const payload = buildPayload('battle');
|
|
payload.unitSet.crewTypes![0]!.phaseSkillTrigger = ['che_선제사격시도', 'che_선제사격발동'];
|
|
payload.unitSet.crewTypes!.splice(1, 0, {
|
|
...payload.unitSet.crewTypes![0]!,
|
|
id: 200,
|
|
name: '수비 보병',
|
|
phaseSkillTrigger: null,
|
|
});
|
|
payload.defenderGenerals[0]!.crewtype = 200;
|
|
|
|
const result = processBattleSimJob(payload);
|
|
|
|
expect(result.result).toBe(true);
|
|
expect(result.attackerSkills?.['선제']).toBe(1);
|
|
});
|
|
|
|
it('fails fast when a simulator unit set references an unknown crew handler', () => {
|
|
const payload = buildPayload('battle');
|
|
payload.unitSet.crewTypes![0]!.iActionList = ['missing_action'];
|
|
|
|
expect(() => processBattleSimJob(payload)).toThrow('Unknown crew type action');
|
|
});
|
|
});
|