조건부 통합 테스트 등록과 시계 fixture를 바로잡는다

This commit is contained in:
2026-09-23 16:21:52 +00:00
parent 7e1783d651
commit 08be92b6fa
6 changed files with 37 additions and 4 deletions
@@ -5,6 +5,7 @@ import { SystemClock } from '@sammo-ts/common';
import { import {
createDatabaseTurnHooks, createDatabaseTurnHooks,
DatabaseTurnDaemonCommandQueue, DatabaseTurnDaemonCommandQueue,
DatabaseTurnDaemonLease,
EngineStateManager, EngineStateManager,
InMemoryTurnStateStore, InMemoryTurnStateStore,
InMemoryTurnWorld, InMemoryTurnWorld,
@@ -695,6 +696,10 @@ liveDescribe('auction worker durable recovery', () => {
const redisConnector = createRedisConnector(resolveRedisConfigFromEnv()); const redisConnector = createRedisConnector(resolveRedisConfigFromEnv());
await redisConnector.connect(); await redisConnector.connect();
// 독립 worker는 활성 daemon의 clock_ready fence가 있을 때만 GAME_TIME을 진행한다.
const lease = await DatabaseTurnDaemonLease.connect(databaseUrl!, { profile: profileName });
await lease.acquire();
await lease.markClockReady();
const keys = buildAuctionTimerKeys(profileName); const keys = buildAuctionTimerKeys(profileName);
const abortController = new AbortController(); const abortController = new AbortController();
const worker = runAuctionWorker({ signal: abortController.signal }); const worker = runAuctionWorker({ signal: abortController.signal });
@@ -725,6 +730,8 @@ liveDescribe('auction worker durable recovery', () => {
} finally { } finally {
abortController.abort(); abortController.abort();
await worker; await worker;
await lease.close();
await connector.prisma.turnDaemonLease.delete({ where: { profile: profileName } });
await redisConnector.client.del([keys.timerKey, keys.historyKey]); await redisConnector.client.del([keys.timerKey, keys.historyKey]);
await redisConnector.disconnect(); await redisConnector.disconnect();
await connector.prisma.errorLog.deleteMany({ await connector.prisma.errorLog.deleteMany({
@@ -384,8 +384,8 @@ integration('generic general creation through the durable turn daemon', () => {
actorUserId: userId, actorUserId: userId,
}); });
expect(access.lastRefresh?.getTime()).toBe(event.createdAt.getTime()); expect(access.lastRefresh?.getTime()).toBe(event.createdAt.getTime());
const turnGridOffsetSeconds = // 상속 턴 시간대는 daemon cursor가 아닌 5분 격자의 위상을 보존한다.
((created.turnTime.getTime() - runtime!.world.getState().lastTurnTime.getTime()) / 1000 + 300) % 300; const turnGridOffsetSeconds = ((created.turnTime.getTime() / 1000) % 300 + 300) % 300;
expect(turnGridOffsetSeconds).toBeGreaterThanOrEqual(35); expect(turnGridOffsetSeconds).toBeGreaterThanOrEqual(35);
expect(turnGridOffsetSeconds).toBeLessThan(40); expect(turnGridOffsetSeconds).toBeLessThan(40);
@@ -72,6 +72,14 @@ const state: TurnWorldState = {
currentMonth: 1, currentMonth: 1,
tickSeconds: 600, tickSeconds: 600,
lastTurnTime: new Date('2026-07-31T09:00:00.000Z'), lastTurnTime: new Date('2026-07-31T09:00:00.000Z'),
clockBaseTime: new Date('2026-07-31T09:00:00.000Z'),
clockTick: 0,
lastTurnTick: 0,
clockMode: 'manual',
clockPhase: 'MANUAL',
clockWallAnchor: new Date('2026-07-31T09:00:00.000Z'),
clockRevision: 1,
deadlineGeneration: 1,
meta: { meta: {
killturn: 24, killturn: 24,
lastTurnTime: '2026-07-31T09:00:00.000Z', lastTurnTime: '2026-07-31T09:00:00.000Z',
@@ -209,6 +217,14 @@ integration('adjustGeneralIcon PostgreSQL persistence', () => {
currentYear: state.currentYear, currentYear: state.currentYear,
currentMonth: state.currentMonth, currentMonth: state.currentMonth,
tickSeconds: state.tickSeconds, tickSeconds: state.tickSeconds,
clockBaseTime: state.clockBaseTime,
clockTick: BigInt(state.clockTick ?? 0),
lastTurnTick: BigInt(state.lastTurnTick ?? 0),
clockMode: state.clockMode,
clockPhase: state.clockPhase,
clockWallAnchor: state.clockWallAnchor,
clockRevision: BigInt(state.clockRevision ?? 1),
deadlineGeneration: BigInt(state.deadlineGeneration ?? 1),
config: JSON.parse(JSON.stringify(scenarioConfig)) as GamePrisma.InputJsonValue, config: JSON.parse(JSON.stringify(scenarioConfig)) as GamePrisma.InputJsonValue,
meta: state.meta as GamePrisma.InputJsonValue, meta: state.meta as GamePrisma.InputJsonValue,
}, },
@@ -224,6 +224,14 @@ integration('monthly catalog boundary persistence', () => {
currentYear: 190, currentYear: 190,
currentMonth: 12, currentMonth: 12,
tickSeconds: 600, tickSeconds: 600,
clockBaseTime: new Date('2026-07-25T01:50:00.000Z'),
clockTick: 0n,
lastTurnTick: 0n,
clockMode: 'manual',
clockPhase: 'MANUAL',
clockWallAnchor: new Date('2026-07-25T01:50:00.000Z'),
clockRevision: 1n,
deadlineGeneration: 1n,
config: scenarioConfig, config: scenarioConfig,
meta: { meta: {
hiddenSeed: 'monthly-boundary-core-catalog', hiddenSeed: 'monthly-boundary-core-catalog',
@@ -16,7 +16,7 @@ test('keeps every non-database conditional Ref suite in one requirement group',
); );
assert.deepEqual(counts, { assert.deepEqual(counts, {
reference_command: 5, reference_command: 3,
reference_full_lifecycle: 1, reference_full_lifecycle: 1,
reference_instant_diplomacy: 1, reference_instant_diplomacy: 1,
reference_monthly: 1, reference_monthly: 1,
@@ -49,7 +49,7 @@ test('enables Ref suites only with the Ref runtime and saved traces only as a pa
}); });
assert.equal( assert.equal(
selectEnabledConditionalIntegrationFiles(entries, { TURN_DIFFERENTIAL_REFERENCE: '1' }).referenceFiles.length, selectEnabledConditionalIntegrationFiles(entries, { TURN_DIFFERENTIAL_REFERENCE: '1' }).referenceFiles.length,
9 7
); );
assert.deepEqual( assert.deepEqual(
selectEnabledConditionalIntegrationFiles(entries, { selectEnabledConditionalIntegrationFiles(entries, {
@@ -8,6 +8,8 @@ GATEWAY_RELEASE_DATABASE_URL gateway_runtime
GENERAL_LIFECYCLE_DATABASE_URL core GENERAL_LIFECYCLE_DATABASE_URL core
IMMEDIATE_ACTION_DATABASE_URL immediate_action IMMEDIATE_ACTION_DATABASE_URL immediate_action
INPUT_EVENT_DATABASE_URL core INPUT_EVENT_DATABASE_URL core
PLAY_AUDIT_COST_DATABASE_URL external_fixture
PLAY_AUDIT_DECISION_DATABASE_URL external_fixture
PLAY_AUDIT_RETENTION_DATABASE_URL external_fixture PLAY_AUDIT_RETENTION_DATABASE_URL external_fixture
PLAY_AUDIT_STARTUP_DATABASE_URL external_fixture PLAY_AUDIT_STARTUP_DATABASE_URL external_fixture
LIVE_SORTIE_PERSISTENCE_DATABASE_URL reference_live_sortie LIVE_SORTIE_PERSISTENCE_DATABASE_URL reference_live_sortie
1 # Environment variable Execution mode
8 GENERAL_LIFECYCLE_DATABASE_URL core
9 IMMEDIATE_ACTION_DATABASE_URL immediate_action
10 INPUT_EVENT_DATABASE_URL core
11 PLAY_AUDIT_COST_DATABASE_URL external_fixture
12 PLAY_AUDIT_DECISION_DATABASE_URL external_fixture
13 PLAY_AUDIT_RETENTION_DATABASE_URL external_fixture
14 PLAY_AUDIT_STARTUP_DATABASE_URL external_fixture
15 LIVE_SORTIE_PERSISTENCE_DATABASE_URL reference_live_sortie