From 267d21f1a95311a284f9aebcd194044aeeb9fd0c Mon Sep 17 00:00:00 2001 From: hided62 Date: Sun, 26 Jul 2026 05:12:20 +0000 Subject: [PATCH] test(turn): cover missing target boundaries --- .../general-command-differential-testing.md | 5 ++ .../turn-state-differential-testing.md | 6 +++ ...rnCommandGeneralMatrix.integration.test.ts | 51 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/docs/architecture/general-command-differential-testing.md b/docs/architecture/general-command-differential-testing.md index f33f40e..ac900c8 100644 --- a/docs/architecture/general-command-differential-testing.md +++ b/docs/architecture/general-command-differential-testing.md @@ -30,6 +30,11 @@ turn 6개는 전투태세 1/2/3턴, 내정·전투 특기 초기화 1턴, 은퇴 `last_turn`과 진행 로그, RNG 무소비를 비교한다. cooldown 3개는 특기 초기화 완료 직후 `current + 60 - preReq`, 1턴 전 차단, 경계 월 허용을 ref `next_execute` KV와 core general meta의 공통 projection으로 비교한다. +존재하지 않는 대상 경계 4개는 증여·등용의 장수 ID와 첩보·이동의 도시 ID를 +고정해 원 명령 미완료, 휴식 fallback, RNG 무소비와 semantic delta를 +비교한다. +필수 인자 객체 자체를 생략한 요청은 ref runner가 제한 시간 안에 종료되지 +않아 동적 호환 판정에서 제외한다. 나머지 명령별 제약 실패·값 경계와 전체 core PostgreSQL 재조회가 완료 기준을 통과하기 전까지 55개 명령 전체의 동적 호환 상태를 `확인`으로 올리지 않는다. diff --git a/docs/architecture/turn-state-differential-testing.md b/docs/architecture/turn-state-differential-testing.md index f29f430..52d3154 100644 --- a/docs/architecture/turn-state-differential-testing.md +++ b/docs/architecture/turn-state-differential-testing.md @@ -210,6 +210,12 @@ Three post-required cooldown cases project the legacy `next_execute` KV and core general meta into the same world-level cooldown record. They cover the stored `current + 60 - preReq` value, rejection one turn before availability, and successful execution exactly at the boundary. +Four missing-target cases cover nonexistent general IDs for gift and +employment plus nonexistent city IDs for spying and movement. Both engines +reject the requested command, execute rest without command RNG, and produce +the same semantic state delta. +Requests that omit the required argument object remain unverified because the +reference runner did not terminate within the bounded comparison run. This is not yet a claim that every command-specific constraint, clamp and persistence boundary has been dynamically compared. diff --git a/tools/integration-tests/test/turnCommandGeneralMatrix.integration.test.ts b/tools/integration-tests/test/turnCommandGeneralMatrix.integration.test.ts index f6a139e..9e507f5 100644 --- a/tools/integration-tests/test/turnCommandGeneralMatrix.integration.test.ts +++ b/tools/integration-tests/test/turnCommandGeneralMatrix.integration.test.ts @@ -1058,6 +1058,57 @@ integration('general command post-required cooldown boundary matrix', () => { }, 120_000); }); +const missingTargetCases: Array<{ name: string; action: string; args: Record }> = [ + { + name: 'gift to a missing general', + action: 'che_증여', + args: { isGold: true, amount: 100, destGeneralID: 999 }, + }, + { + name: 'spy on a missing city', + action: 'che_첩보', + args: { destCityID: 999 }, + }, + { + name: 'move to a missing city', + action: 'che_이동', + args: { destCityID: 999 }, + }, + { + name: 'employ a missing general', + action: 'che_등용', + args: { destGeneralID: 999 }, + }, +]; + +integration('general command missing-target fallback matrix', () => { + it.each(missingTargetCases)( + '$name rejects the missing target and falls back without command RNG', + async ({ action, args }) => { + const request = buildRequest(action, args); + const reference = runReferenceTurnCommandTraceRequest( + workspaceRoot!, + request as unknown as Record + ); + const core = await runCoreTurnCommandTrace(request, reference.before); + + expect(reference.execution.outcome).toMatchObject({ completed: false }); + expect(core.execution.outcome).toMatchObject({ + requestedAction: action, + actionKey: '휴식', + usedFallback: true, + }); + expect(core.rng).toEqual(reference.rng); + expect( + compareTurnSnapshotDeltas(reference.before, reference.after, core.before, core.after, { + ignoredPathPatterns: ignoredLifecyclePaths, + }) + ).toEqual([]); + }, + 120_000 + ); +}); + type GeneralConstraintCase = { name: string; action: string;