fix: 게임 시계 완료 감사 경계 보강
This commit is contained in:
@@ -283,6 +283,7 @@ integration('adjustGeneralIcon PostgreSQL persistence', () => {
|
||||
command: Extract<TurnDaemonCommand, { type: 'adjustGeneralIcon' }>,
|
||||
actorUserId = command.userId
|
||||
): Promise<void> => {
|
||||
const clock = world.getGameClockState();
|
||||
await db.inputEvent.create({
|
||||
data: {
|
||||
requestId: command.requestId!,
|
||||
@@ -294,6 +295,12 @@ integration('adjustGeneralIcon PostgreSQL persistence', () => {
|
||||
leaseUntil: new Date('2026-07-31T09:30:00.000Z'),
|
||||
attempts: 1,
|
||||
payload: command as GamePrisma.InputJsonValue,
|
||||
acceptedGameTick: BigInt(clock.tick),
|
||||
acceptedClockRevision: BigInt(clock.revision),
|
||||
acceptedDeadlineGeneration: BigInt(clock.deadlineGeneration),
|
||||
processingGameTick: BigInt(clock.tick),
|
||||
processingClockRevision: BigInt(clock.revision),
|
||||
processingDeadlineGeneration: BigInt(clock.deadlineGeneration),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -263,6 +263,7 @@ integration('inheritance action PostgreSQL atomic persistence', () => {
|
||||
const createInputEvent = async (
|
||||
command: Extract<TurnDaemonCommand, { type: 'inheritanceAction' }>
|
||||
): Promise<void> => {
|
||||
const clock = world.getGameClockState();
|
||||
await db.inputEvent.create({
|
||||
data: {
|
||||
requestId: command.requestId!,
|
||||
@@ -274,6 +275,12 @@ integration('inheritance action PostgreSQL atomic persistence', () => {
|
||||
leaseUntil: new Date('2026-08-24T01:00:00.000Z'),
|
||||
attempts: 1,
|
||||
payload: command as GamePrisma.InputJsonValue,
|
||||
acceptedGameTick: BigInt(clock.tick),
|
||||
acceptedClockRevision: BigInt(clock.revision),
|
||||
acceptedDeadlineGeneration: BigInt(clock.deadlineGeneration),
|
||||
processingGameTick: BigInt(clock.tick),
|
||||
processingClockRevision: BigInt(clock.revision),
|
||||
processingDeadlineGeneration: BigInt(clock.deadlineGeneration),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -99,9 +99,7 @@ integration('game-engine read-model journal PostgreSQL transaction', () => {
|
||||
afterAll(async () => {
|
||||
await hooks?.close();
|
||||
if (db) {
|
||||
await db.$executeRawUnsafe(
|
||||
`ALTER TABLE read_model_outbox DROP CONSTRAINT IF EXISTS ${rollbackConstraint}`
|
||||
);
|
||||
await db.$executeRawUnsafe(`ALTER TABLE read_model_outbox DROP CONSTRAINT IF EXISTS ${rollbackConstraint}`);
|
||||
await db.inputEvent.deleteMany({ where: { requestId } });
|
||||
await db.logEntry.deleteMany({ where: { text: { startsWith: '[read-model-journal]' } } });
|
||||
await db.worldState.deleteMany({ where: { id: worldId } });
|
||||
@@ -187,6 +185,7 @@ integration('game-engine read-model journal PostgreSQL transaction', () => {
|
||||
currentMonth: 2,
|
||||
});
|
||||
|
||||
const commandClock = world.getGameClockState();
|
||||
await db.inputEvent.create({
|
||||
data: {
|
||||
requestId,
|
||||
@@ -194,6 +193,12 @@ integration('game-engine read-model journal PostgreSQL transaction', () => {
|
||||
eventType: 'shiftSchedule',
|
||||
status: 'PROCESSING',
|
||||
payload: {},
|
||||
acceptedGameTick: BigInt(commandClock.tick),
|
||||
acceptedClockRevision: BigInt(commandClock.revision),
|
||||
acceptedDeadlineGeneration: BigInt(commandClock.deadlineGeneration),
|
||||
processingGameTick: BigInt(commandClock.tick),
|
||||
processingClockRevision: BigInt(commandClock.revision),
|
||||
processingDeadlineGeneration: BigInt(commandClock.deadlineGeneration),
|
||||
},
|
||||
});
|
||||
const directResult: TurnDaemonCommandResult = {
|
||||
@@ -250,9 +255,9 @@ integration('game-engine read-model journal PostgreSQL transaction', () => {
|
||||
{ domain: 'records.history', entityId: 0, revision: 1n },
|
||||
]);
|
||||
await expect(db.readModelOutbox.count()).resolves.toBe(3);
|
||||
await expect(db.logEntry.count({ where: { text: { startsWith: '[read-model-journal] direct' } } })).resolves.toBe(
|
||||
3
|
||||
);
|
||||
await expect(
|
||||
db.logEntry.count({ where: { text: { startsWith: '[read-model-journal] direct' } } })
|
||||
).resolves.toBe(3);
|
||||
|
||||
world.updateWorldMeta({ queueProbe: 1 });
|
||||
await hooks.hooks.flushChanges?.(turnRunResult(world));
|
||||
|
||||
@@ -174,6 +174,7 @@ describe('HWE-shaped unification invader resume', () => {
|
||||
const world = new InMemoryTurnWorld(state, snapshot, {
|
||||
schedule: { entries: [{ startMinute: 0, tickMinutes: 1 }] },
|
||||
});
|
||||
const addGeneral = vi.spyOn(world, 'addGeneral');
|
||||
const reservedTurns = new InMemoryReservedTurnStore(
|
||||
{
|
||||
generalTurn: { findMany: vi.fn(), deleteMany: vi.fn(), createMany: vi.fn() },
|
||||
@@ -301,6 +302,14 @@ describe('HWE-shaped unification invader resume', () => {
|
||||
});
|
||||
expect(world.listNations().filter((entry) => entry.name.startsWith('ⓞ'))).toHaveLength(1);
|
||||
expect(world.listGenerals().filter((entry) => entry.npcState === 9)).toHaveLength(10);
|
||||
const initialInvaderTurnTimes = addGeneral.mock.calls
|
||||
.map(([general]) => general)
|
||||
.filter((general) => general.npcState === 9)
|
||||
.map((general) => general.turnTime.getTime());
|
||||
const alignedMonthlyBoundary = addMinutes(liveLastTurnTime, 3).getTime();
|
||||
expect(initialInvaderTurnTimes).toHaveLength(10);
|
||||
expect(Math.min(...initialInvaderTurnTimes)).toBeGreaterThanOrEqual(alignedMonthlyBoundary);
|
||||
expect(Math.max(...initialInvaderTurnTimes)).toBeLessThan(alignedMonthlyBoundary + 60_000);
|
||||
expect(run).toHaveBeenCalledTimes(2);
|
||||
expect(run.mock.calls.map(([targetTime]) => targetTime.toISOString())).toEqual([
|
||||
'2026-08-20T06:24:58.611Z',
|
||||
|
||||
Reference in New Issue
Block a user