From 6cc5157b6dabf16af0630ab6a896bb6feea35e0b Mon Sep 17 00:00:00 2001 From: hided62 Date: Sun, 9 Aug 2026 23:55:07 +0000 Subject: [PATCH] test(integration): verify committed read-model projections --- .../test/accountIconPersistence.integration.test.ts | 9 +++++++++ docs/integration-tests.md | 7 +++++++ tools/conditional-integration-registry.tsv | 1 + tools/run-conditional-integration.sh | 13 ++++++++++++- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/game-engine/test/accountIconPersistence.integration.test.ts b/app/game-engine/test/accountIconPersistence.integration.test.ts index dd73e299..be3d9260 100644 --- a/app/game-engine/test/accountIconPersistence.integration.test.ts +++ b/app/game-engine/test/accountIconPersistence.integration.test.ts @@ -306,6 +306,7 @@ integration('adjustGeneralIcon PostgreSQL persistence', () => { CHECK (request_id <> '${rollbackRequestId}' OR status <> 'SUCCEEDED') `); await expect(execute(rollbackCommand)).rejects.toThrow(`violates check constraint "${rollbackConstraint}"`); + expect(hooks.takeCommittedReadModelChanges()).toBeNull(); expect(world.getGeneralById(targetGeneralId)).toMatchObject({ picture: initialPicture, imageServer: initialImageServer, @@ -334,6 +335,7 @@ integration('adjustGeneralIcon PostgreSQL persistence', () => { ); await createInputEvent(actorMismatchCommand, foreignUserId); await expect(execute(actorMismatchCommand)).rejects.toThrow('actor does not match'); + expect(hooks.takeCommittedReadModelChanges()).toBeNull(); expect(world.getGeneralById(targetGeneralId)).toMatchObject({ picture: initialPicture, imageServer: initialImageServer, @@ -357,6 +359,13 @@ integration('adjustGeneralIcon PostgreSQL persistence', () => { generalId: targetGeneralId, updated: true, }); + expect(hooks.takeCommittedReadModelChanges()).toMatchObject({ + generalIds: [targetGeneralId], + mapGeneralIds: [], + frontStatusGeneralIds: [], + lobbyGeneralIds: [targetGeneralId], + reservedGeneralIds: [], + }); expect(world.getGeneralById(targetGeneralId)).toMatchObject({ picture: nextPicture, imageServer: nextImageServer, diff --git a/docs/integration-tests.md b/docs/integration-tests.md index 7fd88c76..f1c458e2 100644 --- a/docs/integration-tests.md +++ b/docs/integration-tests.md @@ -84,6 +84,13 @@ runner는 test 시작 전에 실패합니다. 지원 mode에 marker가 하나도 실행 group의 marker 정규식이 비어도 전체 파일로 선택 범위를 넓히지 않고 실패합니다. +`external_fixture` mode는 격리 빈 schema로 만들 수 없는 명시적 예외입니다. +현재 `CURRENT_SEASON_FIXTURE_DATABASE_URL`은 별도 Ref 현 시즌 importer가 만든 +read-only snapshot을 요구하므로 조건부 runner의 pass/skip 집계에 포함하지 +않습니다. 이 mode는 registry 누락을 숨기기 위한 일반 제외 수단이 아니며, +해당 fixture 검증은 실제 imported snapshot URL을 주입해 별도로 실행하고 결과를 +외부 fixture 범위로 보고해야 합니다. + 관리자 시간 조정의 PostgreSQL 경계는 `runtimeClockShiftPersistence.integration.test.ts`, gateway action `PARTIAL → APPLIED` 경계는 `gatewayRuntimeAction.integration.test.ts`입니다. diff --git a/tools/conditional-integration-registry.tsv b/tools/conditional-integration-registry.tsv index f6870120..3d0526f8 100644 --- a/tools/conditional-integration-registry.tsv +++ b/tools/conditional-integration-registry.tsv @@ -1,5 +1,6 @@ # Environment variable Execution mode CREATE_GENERAL_DATABASE_URL create_general +CURRENT_SEASON_FIXTURE_DATABASE_URL external_fixture GATEWAY_RUNTIME_ACTION_DATABASE_URL gateway_runtime GATEWAY_OPERATION_DATABASE_URL gateway_runtime GATEWAY_RELEASE_DATABASE_URL gateway_runtime diff --git a/tools/run-conditional-integration.sh b/tools/run-conditional-integration.sh index 9a2122e8..855a55ca 100755 --- a/tools/run-conditional-integration.sh +++ b/tools/run-conditional-integration.sh @@ -41,7 +41,7 @@ node_tag=$(printf '%s' "${CI_NODE_INDEX:-local}" | tr -cd 'a-zA-Z0-9_' | tr 'A-Z run_id=$(date -u +%m%d%H%M%S)_$$_${node_tag} export CONDITIONAL_INTEGRATION_RUN_ID=$run_id schema_ownership_token="sammo-conditional-integration:$run_id" -supported_registry_modes="core create_general gateway_runtime immediate_action npc_possession reference_live_sortie reference_npc_possession select_pool" +supported_registry_modes="core create_general external_fixture gateway_runtime immediate_action npc_possession reference_live_sortie reference_npc_possession select_pool" term_grace_seconds=${CONDITIONAL_INTEGRATION_TERM_GRACE_SECONDS:-10} case "$term_grace_seconds" in ''|*[!0-9]*) @@ -88,6 +88,15 @@ done report_dir=$(mktemp -d) summary_file="$report_dir/summary.tsv" validated_registry_file="$report_dir/validated-registry.tsv" +image_upload_secret_file="$report_dir/image-upload-secret" +( + umask 077 + node --input-type=module -e \ + 'import { randomBytes } from "node:crypto"; process.stdout.write(randomBytes(32).toString("hex"));' \ + >"$image_upload_secret_file" +) +export GAME_IMAGE_UPLOAD_SECRET_FILE=$image_upload_secret_file +export GATEWAY_IMAGE_UPLOAD_SECRET_FILE=$image_upload_secret_file active_process_group= cleanup_resources_started=0 @@ -452,7 +461,9 @@ validate_marker_registry pnpm install --frozen-lockfile pnpm --filter @sammo-ts/infra prisma:generate pnpm --filter @sammo-ts/common build +pnpm --filter @sammo-ts/logic build pnpm --filter @sammo-ts/infra build +pnpm --filter @sammo-ts/game-engine build GATEWAY_MIGRATION_TEST_DATABASE_URL=$base_database_url \ pnpm --filter @sammo-ts/infra verify:migration:account-icon