Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91b2980e89 |
@@ -0,0 +1,208 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import type { City, MapDefinition, Nation } from '@sammo-ts/logic';
|
||||||
|
|
||||||
|
import { InMemoryTurnWorld } from '../src/turn/inMemoryWorld.js';
|
||||||
|
import { composeCalendarHandlers } from '../src/turn/calendarHandlers.js';
|
||||||
|
import { createUpdateCitySupplyHandler } from '../src/turn/monthlyCitySupplyAction.js';
|
||||||
|
import { createMonthlyEventHandler, type MonthlyEventActionHandler } from '../src/turn/monthlyEventHandler.js';
|
||||||
|
import { createYearbookHandler } from '../src/turn/yearbookHandler.js';
|
||||||
|
import type { PendingYearbookSnapshot, TurnWorldSnapshot, TurnWorldState } from '../src/turn/types.js';
|
||||||
|
|
||||||
|
// Ref 계승 계약 (devel TurnExecutionHelper.php 실행 순서):
|
||||||
|
// runEventHandler(PreMonth) -> UpdateCitySupply (보급 재계산/고립)
|
||||||
|
// preUpdateMonthly() -> LogHistory (방금 끝난 달의 연감 스냅샷)
|
||||||
|
// turnDate() -> 월 advance
|
||||||
|
// 즉 월중 출병 점령은 보급을 즉시 끊지 않고(che_출병/aftermath는 점령 도시와
|
||||||
|
// 천도 신수도에만 supply=1을 쓴다), 보급切断은 다음 달 시작 경계에서 일어난다.
|
||||||
|
// 단 경계 스냅샷은 Ref #230(d17b2518) 이후 UpdateCitySupply "뒤"에 찍히므로
|
||||||
|
// 끝난 달(10월)의 연감 지도에는 이미 끊긴 보급선이 반영된다. Core의
|
||||||
|
// composeCalendarHandlers 순서(monthlyEventHandler -> yearbookHandler)가 이
|
||||||
|
// 계약을 보존하는지를 고정한다.
|
||||||
|
|
||||||
|
const map: MapDefinition = {
|
||||||
|
id: 'supply-cut-order-test',
|
||||||
|
name: 'supply-cut-order-test',
|
||||||
|
cities: [
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
name: '공격기지',
|
||||||
|
level: 2,
|
||||||
|
region: 1,
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
connections: [20],
|
||||||
|
max: { population: 2_000, agriculture: 1_000, commerce: 1_000, security: 1_000, defence: 1_000, wall: 1_000 },
|
||||||
|
initial: { population: 1_000, agriculture: 500, commerce: 500, security: 500, defence: 500, wall: 500 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 20,
|
||||||
|
name: '중간 보급로',
|
||||||
|
level: 2,
|
||||||
|
region: 1,
|
||||||
|
position: { x: 1, y: 0 },
|
||||||
|
connections: [10, 21, 22],
|
||||||
|
max: { population: 2_000, agriculture: 1_000, commerce: 1_000, security: 1_000, defence: 1_000, wall: 1_000 },
|
||||||
|
initial: { population: 1_000, agriculture: 500, commerce: 500, security: 500, defence: 500, wall: 500 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
name: '수도',
|
||||||
|
level: 2,
|
||||||
|
region: 1,
|
||||||
|
position: { x: 2, y: 0 },
|
||||||
|
connections: [20],
|
||||||
|
max: { population: 2_000, agriculture: 1_000, commerce: 1_000, security: 1_000, defence: 1_000, wall: 1_000 },
|
||||||
|
initial: { population: 1_000, agriculture: 500, commerce: 500, security: 500, defence: 500, wall: 500 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 22,
|
||||||
|
name: '외곽 도시',
|
||||||
|
level: 2,
|
||||||
|
region: 1,
|
||||||
|
position: { x: 3, y: 0 },
|
||||||
|
connections: [20],
|
||||||
|
max: { population: 2_000, agriculture: 1_000, commerce: 1_000, security: 1_000, defence: 1_000, wall: 1_000 },
|
||||||
|
initial: { population: 1_000, agriculture: 500, commerce: 500, security: 500, defence: 500, wall: 500 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaults: { trust: 50, trade: 100, supplyState: 1, frontState: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildCity = (id: number, nationId: number): City => ({
|
||||||
|
id,
|
||||||
|
name: map.cities.find((city) => city.id === id)?.name ?? `도시${id}`,
|
||||||
|
nationId,
|
||||||
|
level: 2,
|
||||||
|
state: 0,
|
||||||
|
population: 1_000,
|
||||||
|
populationMax: 2_000,
|
||||||
|
agriculture: 500,
|
||||||
|
agricultureMax: 1_000,
|
||||||
|
commerce: 500,
|
||||||
|
commerceMax: 1_000,
|
||||||
|
security: 500,
|
||||||
|
securityMax: 1_000,
|
||||||
|
supplyState: 1,
|
||||||
|
frontState: 0,
|
||||||
|
defence: 500,
|
||||||
|
defenceMax: 1_000,
|
||||||
|
wall: 500,
|
||||||
|
wallMax: 1_000,
|
||||||
|
conflict: {},
|
||||||
|
meta: { trust: 50, trade: 100, officer_set: 0, term: 0 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildNation = (id: number, capitalCityId: number): Nation => ({
|
||||||
|
id,
|
||||||
|
name: `국가${id}`,
|
||||||
|
color: '#000000',
|
||||||
|
capitalCityId,
|
||||||
|
chiefGeneralId: null,
|
||||||
|
gold: 1_000,
|
||||||
|
rice: 1_000,
|
||||||
|
power: 0,
|
||||||
|
level: 1,
|
||||||
|
typeCode: 'che_중립',
|
||||||
|
meta: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
type MapCityCompact = [number, number, number, number, number, number];
|
||||||
|
|
||||||
|
const buildWorld = (): { world: InMemoryTurnWorld; snapshots: () => PendingYearbookSnapshot[] } => {
|
||||||
|
const state: TurnWorldState = {
|
||||||
|
id: 1,
|
||||||
|
currentYear: 191,
|
||||||
|
currentMonth: 10,
|
||||||
|
tickSeconds: 600,
|
||||||
|
lastTurnTime: new Date('0191-10-01T00:00:00.000Z'),
|
||||||
|
meta: {},
|
||||||
|
};
|
||||||
|
const actions = new Map<string, MonthlyEventActionHandler>();
|
||||||
|
const snapshot: TurnWorldSnapshot = {
|
||||||
|
scenarioConfig: {
|
||||||
|
stat: { total: 300, min: 10, max: 100, npcTotal: 150, npcMax: 50, npcMin: 10, chiefMin: 70 },
|
||||||
|
iconPath: '',
|
||||||
|
map: {},
|
||||||
|
const: {},
|
||||||
|
environment: { mapName: map.id, unitSet: 'default' },
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
generals: [],
|
||||||
|
cities: [buildCity(10, 2), buildCity(20, 1), buildCity(21, 1), buildCity(22, 1)],
|
||||||
|
nations: [buildNation(1, 21), buildNation(2, 10)],
|
||||||
|
troops: [],
|
||||||
|
diplomacy: [],
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
targetCode: 'pre_month',
|
||||||
|
priority: 9_000,
|
||||||
|
condition: true,
|
||||||
|
action: [['UpdateCitySupply']],
|
||||||
|
meta: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
initialEvents: [],
|
||||||
|
};
|
||||||
|
let world: InMemoryTurnWorld | null = null;
|
||||||
|
// 프로덕션 turnDaemon과 같은 구성 순서: monthly event(pre_month) -> yearbook.
|
||||||
|
const calendarHandler = composeCalendarHandlers(
|
||||||
|
createMonthlyEventHandler({ getWorld: () => world, startYear: 180, actions }),
|
||||||
|
createYearbookHandler({ profileName: 'test', getWorld: () => world }).handler
|
||||||
|
);
|
||||||
|
world = new InMemoryTurnWorld(state, snapshot, {
|
||||||
|
schedule: { entries: [{ startMinute: 0, tickMinutes: 10 }] },
|
||||||
|
calendarHandler,
|
||||||
|
});
|
||||||
|
actions.set('UpdateCitySupply', createUpdateCitySupplyHandler({ getWorld: () => world!, map }));
|
||||||
|
return {
|
||||||
|
world: world!,
|
||||||
|
snapshots: () => world!.peekDirtyState().pendingYearbookSnapshots,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('10월중 보급로 도시 점령 -> 11월 경계切断 -> 10월 연감 스냅샷 순서', () => {
|
||||||
|
it('keeps supply alive during October and cuts it exactly at the month boundary', async () => {
|
||||||
|
const { world } = buildWorld();
|
||||||
|
|
||||||
|
// che_출병의 aftermath effect가 반영된 10월중 상태: 도시 20이 국가2에
|
||||||
|
// 점령되고 Ref처럼 supplyState=1만 쓴다. 나머지 도시는 무변경.
|
||||||
|
world.updateCity(20, { nationId: 2, supplyState: 1 });
|
||||||
|
|
||||||
|
// 10월 진행중에는 고립될 외곽 도시도 아직 보급 상태다(라이브 지도 기준).
|
||||||
|
expect(world.getCityById(22)?.supplyState).toBe(1);
|
||||||
|
expect(world.getCityById(21)?.supplyState).toBe(1);
|
||||||
|
|
||||||
|
await world.advanceMonth(new Date('0191-11-01T00:00:00.000Z'));
|
||||||
|
|
||||||
|
//切断은 정확히 월 경계에서 일어난다.
|
||||||
|
expect(world.getCityById(22)?.supplyState).toBe(0);
|
||||||
|
expect(world.getCityById(21)?.supplyState).toBe(1);
|
||||||
|
expect(world.getCityById(20)?.supplyState).toBe(1);
|
||||||
|
expect(world.getCityById(10)?.supplyState).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('archives the finished month yearbook map after the boundary supply recalculation (Ref #230 order)', async () => {
|
||||||
|
const { world, snapshots } = buildWorld();
|
||||||
|
world.updateCity(20, { nationId: 2, supplyState: 1 });
|
||||||
|
|
||||||
|
expect(snapshots()).toEqual([]);
|
||||||
|
|
||||||
|
await world.advanceMonth(new Date('0191-11-01T00:00:00.000Z'));
|
||||||
|
|
||||||
|
const pending = snapshots();
|
||||||
|
expect(pending).toHaveLength(1);
|
||||||
|
const october = pending[0]!;
|
||||||
|
expect(october.year).toBe(191);
|
||||||
|
expect(october.month).toBe(10);
|
||||||
|
|
||||||
|
const cityList = (october.map as { cityList: MapCityCompact[] }).cityList;
|
||||||
|
const supplyFlagById = new Map(cityList.map((entry) => [entry[0], entry[5]]));
|
||||||
|
// Ref #230 이후 LogHistory는 UpdateCitySupply 뒤에 실행되므로 끝난 10월의
|
||||||
|
// 연감 지도는 이미 고립을 반영한다. 이 순서가 Core의 계약이다.
|
||||||
|
expect(supplyFlagById.get(22)).toBe(0);
|
||||||
|
expect(supplyFlagById.get(21)).toBe(1);
|
||||||
|
expect(supplyFlagById.get(20)).toBe(1);
|
||||||
|
expect(supplyFlagById.get(10)).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import type { City, General, Nation } from '../src/domain/entities.js';
|
||||||
|
import { resolveGeneralAction } from '../src/actions/engine.js';
|
||||||
|
import { ActionDefinition } from '../src/actions/turn/general/che_출병.js';
|
||||||
|
import type { DispatchResolveContext } from '../src/actions/turn/general/che_출병.js';
|
||||||
|
import type { TurnSchedule } from '../src/turn/calendar.js';
|
||||||
|
import type { WarAftermathConfig, WarEngineConfig } from '../src/war/types.js';
|
||||||
|
import type { MapDefinition, UnitSetDefinition } from '../src/world/types.js';
|
||||||
|
|
||||||
|
// Ref 계승 계약: city.supply 재계산은 월 경계의 UpdateCitySupply(pre_month)에서만
|
||||||
|
// 일어나고, 출병 점령 순간에는 점령 도시와 긴급천도 신수도에만 supply=1을 쓴다.
|
||||||
|
// (ref TurnExecutionHelper.php PreMonth→preUpdateMonthly 순서, process_war.php의
|
||||||
|
// 'supply' => 1 쓰기 두 곳) 월중에는 다른 도시의 supply가 절대 0으로 바뀌면 안 된다.
|
||||||
|
|
||||||
|
const buildGeneral = (id: number, nationId: number, cityId: number): General => ({
|
||||||
|
id,
|
||||||
|
name: `General${id}`,
|
||||||
|
nationId,
|
||||||
|
cityId,
|
||||||
|
troopId: 0,
|
||||||
|
stats: { leadership: 70, strength: 70, intelligence: 70 },
|
||||||
|
experience: 100,
|
||||||
|
dedication: 100,
|
||||||
|
officerLevel: 3,
|
||||||
|
role: {
|
||||||
|
personality: null,
|
||||||
|
specialDomestic: null,
|
||||||
|
specialWar: null,
|
||||||
|
items: { horse: null, weapon: null, book: null, item: null },
|
||||||
|
},
|
||||||
|
injury: 0,
|
||||||
|
gold: 1000,
|
||||||
|
rice: 2000,
|
||||||
|
crew: 1500,
|
||||||
|
crewTypeId: 100,
|
||||||
|
train: 80,
|
||||||
|
atmos: 80,
|
||||||
|
age: 25,
|
||||||
|
npcState: 0,
|
||||||
|
triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} },
|
||||||
|
meta: { killturn: 24 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildCity = (id: number, nationId: number): City => ({
|
||||||
|
id,
|
||||||
|
name: `City${id}`,
|
||||||
|
nationId,
|
||||||
|
level: 2,
|
||||||
|
state: 0,
|
||||||
|
population: 10000,
|
||||||
|
populationMax: 10000,
|
||||||
|
agriculture: 1000,
|
||||||
|
agricultureMax: 1000,
|
||||||
|
commerce: 1000,
|
||||||
|
commerceMax: 1000,
|
||||||
|
security: 1000,
|
||||||
|
securityMax: 1000,
|
||||||
|
supplyState: 1,
|
||||||
|
frontState: 0,
|
||||||
|
defence: 200,
|
||||||
|
defenceMax: 400,
|
||||||
|
wall: 200,
|
||||||
|
wallMax: 400,
|
||||||
|
meta: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildNation = (id: number, capitalCityId: number): Nation => ({
|
||||||
|
id,
|
||||||
|
name: `Nation${id}`,
|
||||||
|
color: '#000000',
|
||||||
|
capitalCityId,
|
||||||
|
chiefGeneralId: null,
|
||||||
|
gold: 5000,
|
||||||
|
rice: 5000,
|
||||||
|
power: 0,
|
||||||
|
level: 1,
|
||||||
|
typeCode: 'test',
|
||||||
|
meta: { tech: 1000 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const unitSet: UnitSetDefinition = {
|
||||||
|
id: 'test',
|
||||||
|
name: 'test',
|
||||||
|
defaultCrewTypeId: 100,
|
||||||
|
crewTypes: [
|
||||||
|
{
|
||||||
|
id: 100,
|
||||||
|
armType: 1,
|
||||||
|
name: 'Infantry',
|
||||||
|
attack: 10,
|
||||||
|
defence: 10,
|
||||||
|
speed: 3,
|
||||||
|
avoid: 5,
|
||||||
|
magicCoef: 0,
|
||||||
|
cost: 0,
|
||||||
|
rice: 1,
|
||||||
|
requirements: [],
|
||||||
|
attackCoef: {},
|
||||||
|
defenceCoef: {},
|
||||||
|
info: [],
|
||||||
|
initSkillTrigger: null,
|
||||||
|
phaseSkillTrigger: null,
|
||||||
|
iActionList: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 999,
|
||||||
|
armType: 5,
|
||||||
|
name: 'Castle',
|
||||||
|
attack: 0,
|
||||||
|
defence: 0,
|
||||||
|
speed: 1,
|
||||||
|
avoid: 0,
|
||||||
|
magicCoef: 0,
|
||||||
|
cost: 0,
|
||||||
|
rice: 10,
|
||||||
|
requirements: [{ type: 'Impossible' }],
|
||||||
|
attackCoef: {},
|
||||||
|
defenceCoef: {},
|
||||||
|
info: [],
|
||||||
|
initSkillTrigger: null,
|
||||||
|
phaseSkillTrigger: null,
|
||||||
|
iActionList: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const warConfig: WarEngineConfig = {
|
||||||
|
armPerPhase: 500,
|
||||||
|
maxTrainByCommand: 100,
|
||||||
|
maxAtmosByCommand: 100,
|
||||||
|
maxTrainByWar: 110,
|
||||||
|
maxAtmosByWar: 150,
|
||||||
|
castleCrewTypeId: 999,
|
||||||
|
armTypes: {
|
||||||
|
footman: 1,
|
||||||
|
archer: 2,
|
||||||
|
cavalry: 3,
|
||||||
|
wizard: 4,
|
||||||
|
siege: 5,
|
||||||
|
misc: 6,
|
||||||
|
castle: 5,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const aftermathConfig: WarAftermathConfig = {
|
||||||
|
initialNationGenLimit: 1,
|
||||||
|
techLevelIncYear: 5,
|
||||||
|
initialAllowedTechLevel: 1,
|
||||||
|
maxTechLevel: 12,
|
||||||
|
defaultCityWall: 1000,
|
||||||
|
baseGold: 0,
|
||||||
|
baseRice: 0,
|
||||||
|
castleCrewTypeId: 999,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rng = {
|
||||||
|
nextFloat1: () => 0.1,
|
||||||
|
nextBool: (probability: number) => probability >= 0.1,
|
||||||
|
nextInt: (minInclusive: number, _maxExclusive: number) => minInclusive,
|
||||||
|
};
|
||||||
|
|
||||||
|
const schedule: TurnSchedule = {
|
||||||
|
entries: [{ startMinute: 0, tickMinutes: 60 }],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 수도 21 - 중간도시 20 - 외곽도시 22 사슬에서 20만 국가1 영토였고,
|
||||||
|
// 공격자 국가2의 도시 10이 20과 인접한다. 20을 먹히면 22는 월 경계 이후에
|
||||||
|
// 고립되지만, 점령 그 순간에는 아니다.
|
||||||
|
const mapCities: MapDefinition['cities'] = [
|
||||||
|
{ id: 10, name: 'AggBase', level: 2, region: 1, position: { x: 0, y: 0 }, connections: [20] },
|
||||||
|
{ id: 20, name: 'Mid', level: 2, region: 1, position: { x: 1, y: 0 }, connections: [10, 21, 22] },
|
||||||
|
{ id: 21, name: 'Cap', level: 2, region: 1, position: { x: 2, y: 0 }, connections: [20] },
|
||||||
|
{ id: 22, name: 'Far', level: 2, region: 1, position: { x: 3, y: 0 }, connections: [20] },
|
||||||
|
].map((city) => ({
|
||||||
|
...city,
|
||||||
|
max: { population: 10000, agriculture: 1000, commerce: 1000, security: 1000, defence: 400, wall: 400 },
|
||||||
|
initial: { population: 10000, agriculture: 1000, commerce: 1000, security: 1000, defence: 200, wall: 200 },
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('출병 점령과 보급切断 시점', () => {
|
||||||
|
it('does not cut any other city supply in the same turn the route city is conquered', () => {
|
||||||
|
const defenderNation = buildNation(1, 21);
|
||||||
|
const attackerNation = buildNation(2, 10);
|
||||||
|
const aggBase = buildCity(10, attackerNation.id);
|
||||||
|
const midCity = buildCity(20, defenderNation.id);
|
||||||
|
const capCity = buildCity(21, defenderNation.id);
|
||||||
|
const farCity = buildCity(22, defenderNation.id);
|
||||||
|
midCity.defence = 0;
|
||||||
|
midCity.wall = 0;
|
||||||
|
const attacker = buildGeneral(1, attackerNation.id, aggBase.id);
|
||||||
|
attacker.turnTime = new Date('2000-01-01T00:00:00Z');
|
||||||
|
|
||||||
|
const context: Omit<DispatchResolveContext, 'addLog'> = {
|
||||||
|
general: attacker,
|
||||||
|
city: aggBase,
|
||||||
|
nation: attackerNation,
|
||||||
|
rng,
|
||||||
|
destCity: midCity,
|
||||||
|
destNation: defenderNation,
|
||||||
|
cities: [aggBase, midCity, capCity, farCity],
|
||||||
|
nations: [attackerNation, defenderNation],
|
||||||
|
generals: [attacker],
|
||||||
|
unitSet,
|
||||||
|
map: { id: 'supply-chain', name: 'supply-chain', cities: mapCities },
|
||||||
|
diplomacy: [
|
||||||
|
{ fromNationId: attackerNation.id, toNationId: defenderNation.id, state: 0, term: 0 },
|
||||||
|
{ fromNationId: defenderNation.id, toNationId: attackerNation.id, state: 0, term: 0 },
|
||||||
|
],
|
||||||
|
time: { year: 191, month: 10, startYear: 180 },
|
||||||
|
seedBase: 'supply-cut-seed',
|
||||||
|
warConfig,
|
||||||
|
aftermathConfig,
|
||||||
|
messageTime: new Date('2000-01-01T00:00:00.000Z'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolution = resolveGeneralAction(
|
||||||
|
new ActionDefinition(),
|
||||||
|
context,
|
||||||
|
{ now: new Date('2000-01-01T00:00:00Z'), schedule },
|
||||||
|
{ destCityId: midCity.id }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 점령 자체가 일어나야 테스트 의미가 있다.
|
||||||
|
expect(resolution.completed).toBe(true);
|
||||||
|
|
||||||
|
const cityPatches = resolution.patches?.cities ?? [];
|
||||||
|
|
||||||
|
// 점령 도시는 소유권 이동 + Ref의 'supply' => 1 재설정만 받는다.
|
||||||
|
const midPatch = cityPatches.find((patch) => patch.id === midCity.id);
|
||||||
|
expect(midPatch?.patch.nationId).toBe(attackerNation.id);
|
||||||
|
expect(midPatch?.patch.supplyState).toBe(1);
|
||||||
|
|
||||||
|
// 월중에는 그 어떤 도시도 supply=0으로 패치되지 않는다.
|
||||||
|
// (Ref: process_war.php는 점령 도시와 천도 신수도에만 supply=1을 쓴다.)
|
||||||
|
expect(
|
||||||
|
cityPatches.filter((patch) => 'supplyState' in patch.patch && patch.patch.supplyState === 0)
|
||||||
|
).toEqual([]);
|
||||||
|
|
||||||
|
// 고립될 외곽도시와 수도는 이 명령 처리 중에 손대지 않는다.
|
||||||
|
expect(farCity.supplyState).toBe(1);
|
||||||
|
expect(capCity.supplyState).toBe(1);
|
||||||
|
expect(cityPatches.some((patch) => patch.id === farCity.id && 'supplyState' in patch.patch)).toBe(false);
|
||||||
|
expect(cityPatches.some((patch) => patch.id === capCity.id && 'supplyState' in patch.patch)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user