fix: 은퇴 명예 기록과 유산 수명주기 정합성을 복원한다
은퇴·사망·통일의 저장 순서와 명예의 전당 및 명장일람 판정을 Ref 흐름에 맞춘다. 유산 행동을 인증된 daemon transaction으로 통합하고 중복 지급·고유 아이템·로그·오류 경계를 회귀 테스트한다.
This commit is contained in:
@@ -1,68 +1,258 @@
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
import { SystemClock } from '@sammo-ts/common';
|
||||
import type { GameSessionTokenPayload } from '@sammo-ts/common/auth/gameToken';
|
||||
import { createGamePostgresConnector, type GamePrismaClient, type RedisConnector } from '@sammo-ts/infra';
|
||||
import {
|
||||
createDatabaseTurnHooks,
|
||||
DatabaseTurnDaemonCommandQueue,
|
||||
EngineStateManager,
|
||||
InMemoryTurnStateStore,
|
||||
InMemoryTurnWorld,
|
||||
loadTurnWorldFromDatabase,
|
||||
TurnDaemonLifecycle,
|
||||
type TurnGeneral,
|
||||
type TurnWorldSnapshot,
|
||||
type TurnWorldState,
|
||||
} from '@sammo-ts/game-engine';
|
||||
import { createTurnDaemonCommandHandler } from '@sammo-ts/game-engine/turn/worldCommandHandler.js';
|
||||
import {
|
||||
createGamePostgresConnector,
|
||||
type GamePrisma,
|
||||
type GamePrismaClient,
|
||||
type RedisConnector,
|
||||
} from '@sammo-ts/infra';
|
||||
import type { MapDefinition, ScenarioConfig, ScenarioMeta, TurnSchedule } from '@sammo-ts/logic';
|
||||
|
||||
import { RedisAccessTokenStore } from '../src/auth/accessTokenStore.js';
|
||||
import { InMemoryBattleSimTransport } from '../src/battleSim/inMemoryTransport.js';
|
||||
import type { GameApiContext } from '../src/context.js';
|
||||
import { InMemoryTurnDaemonTransport } from '../src/daemon/inMemoryTransport.js';
|
||||
import { DatabaseTurnDaemonTransport } from '../src/daemon/databaseTransport.js';
|
||||
import { InMemoryFlushStore } from '../src/auth/flushStore.js';
|
||||
import { appRouter } from '../src/router.js';
|
||||
|
||||
const databaseUrl = process.env.INPUT_EVENT_DATABASE_URL;
|
||||
const databaseUrl = process.env.IMMEDIATE_ACTION_DATABASE_URL;
|
||||
const integration = describe.skipIf(!databaseUrl);
|
||||
const actorGeneralId = 8_701;
|
||||
const checkedGeneralId = 8_702;
|
||||
const actorNationId = 871;
|
||||
const checkedNationId = 872;
|
||||
const worldId = 992_320;
|
||||
const actorId = 7_320;
|
||||
const targetId = 7_321;
|
||||
const actorNationId = 7_322;
|
||||
const targetNationId = 7_323;
|
||||
const actorUserId = 'inherit-owner-message-actor';
|
||||
const checkedUserId = 'inherit-owner-message-checked';
|
||||
const targetUserId = 'inherit-owner-message-target';
|
||||
const requestId = 'integration:inherit-owner-message:success';
|
||||
const engineRequestId = `${requestId}:inherit.checkOwner:engine:0:inheritanceAction`;
|
||||
const schedule: TurnSchedule = { entries: [{ startMinute: 0, tickMinutes: 10 }] };
|
||||
const scenarioConfig: ScenarioConfig = {
|
||||
stat: { total: 200, min: 10, max: 100, npcTotal: 150, npcMax: 75, npcMin: 10, chiefMin: 70 },
|
||||
iconPath: '.',
|
||||
map: {},
|
||||
const: { inheritCheckOwnerPoint: 1_000 },
|
||||
environment: { mapName: 'che', unitSet: 'che' },
|
||||
};
|
||||
const scenarioMeta: ScenarioMeta = {
|
||||
title: '소유자 확인 통합',
|
||||
startYear: 200,
|
||||
life: null,
|
||||
fiction: null,
|
||||
history: [],
|
||||
ignoreDefaultEvents: false,
|
||||
};
|
||||
const map: MapDefinition = { id: 'inherit-owner-message', name: scenarioMeta.title, cities: [] };
|
||||
const state: TurnWorldState = {
|
||||
id: worldId,
|
||||
currentYear: 200,
|
||||
currentMonth: 4,
|
||||
tickSeconds: 600,
|
||||
lastTurnTime: new Date('2026-08-19T00:00:00.000Z'),
|
||||
meta: { hiddenSeed: 'inherit-owner-message', isunited: 0, scenarioMeta },
|
||||
};
|
||||
|
||||
const general = (overrides: Partial<TurnGeneral>): TurnGeneral => ({
|
||||
id: actorId,
|
||||
userId: actorUserId,
|
||||
name: '확인장수',
|
||||
nationId: actorNationId,
|
||||
cityId: 1,
|
||||
troopId: 0,
|
||||
stats: { leadership: 70, strength: 45, intelligence: 85 },
|
||||
turnTime: new Date('2026-08-19T00:10:00.000Z'),
|
||||
recentWarTime: null,
|
||||
role: {
|
||||
items: { horse: null, weapon: null, book: null, item: null },
|
||||
personality: null,
|
||||
specialDomestic: null,
|
||||
specialWar: null,
|
||||
},
|
||||
triggerState: { flags: {}, counters: {}, modifiers: {}, meta: {} },
|
||||
meta: { killturn: 24, inherit_spent_dyn: 0 },
|
||||
inheritancePoints: { previous: 1_500 },
|
||||
penalty: {},
|
||||
officerLevel: 1,
|
||||
experience: 0,
|
||||
dedication: 0,
|
||||
injury: 0,
|
||||
gold: 1_000,
|
||||
rice: 1_000,
|
||||
crew: 0,
|
||||
crewTypeId: 0,
|
||||
train: 0,
|
||||
atmos: 0,
|
||||
age: 30,
|
||||
npcState: 0,
|
||||
...overrides,
|
||||
});
|
||||
const actor = general({});
|
||||
const target = general({
|
||||
id: targetId,
|
||||
userId: targetUserId,
|
||||
name: '피확인장수',
|
||||
nationId: targetNationId,
|
||||
meta: { killturn: 24, owner_name: '피확인 계정' },
|
||||
inheritancePoints: { previous: 0 },
|
||||
});
|
||||
const nation = (id: number, name: string, chiefGeneralId: number) => ({
|
||||
id,
|
||||
name,
|
||||
color: id === actorNationId ? '#123456' : '#654321',
|
||||
capitalCityId: null,
|
||||
chiefGeneralId,
|
||||
gold: 0,
|
||||
rice: 0,
|
||||
power: 0,
|
||||
level: 1,
|
||||
typeCode: 'che_def',
|
||||
meta: {},
|
||||
});
|
||||
const actorNation = nation(actorNationId, '확인국', actorId);
|
||||
const targetNation = nation(targetNationId, '피확인국', targetId);
|
||||
const auth: GameSessionTokenPayload = {
|
||||
version: 1,
|
||||
profile: 'che:inherit-owner-message',
|
||||
issuedAt: '2026-08-19T00:00:00.000Z',
|
||||
expiresAt: '2026-08-20T00:00:00.000Z',
|
||||
sessionId: 'inherit-owner-message-session',
|
||||
user: {
|
||||
id: actorUserId,
|
||||
username: actorUserId,
|
||||
displayName: '확인자 계정',
|
||||
roles: ['user'],
|
||||
},
|
||||
user: { id: actorUserId, username: actorUserId, displayName: '확인자 계정', roles: ['user'] },
|
||||
sanctions: {},
|
||||
};
|
||||
|
||||
const hasMailboxChange = (payload: unknown): boolean => {
|
||||
if (!payload || typeof payload !== 'object' || !('changes' in payload)) return false;
|
||||
const changes = (payload as { changes?: unknown }).changes;
|
||||
if (!Array.isArray(changes)) return false;
|
||||
const mailboxes = new Set([actorGeneralId, checkedGeneralId]);
|
||||
return changes.some(
|
||||
(change) =>
|
||||
Array.isArray(change) &&
|
||||
change[0] === 'messages.mailbox' &&
|
||||
typeof change[1] === 'number' &&
|
||||
mailboxes.has(change[1])
|
||||
);
|
||||
};
|
||||
const toCreate = (entry: TurnGeneral): GamePrisma.GeneralCreateManyInput => ({
|
||||
id: entry.id,
|
||||
userId: entry.userId,
|
||||
name: entry.name,
|
||||
nationId: entry.nationId,
|
||||
cityId: entry.cityId,
|
||||
npcState: entry.npcState,
|
||||
leadership: entry.stats.leadership,
|
||||
strength: entry.stats.strength,
|
||||
intel: entry.stats.intelligence,
|
||||
turnTime: entry.turnTime,
|
||||
meta: entry.meta as GamePrisma.InputJsonValue,
|
||||
penalty: entry.penalty as GamePrisma.InputJsonValue,
|
||||
});
|
||||
|
||||
integration('inherit owner lookup private messages', () => {
|
||||
let db: GamePrismaClient;
|
||||
let closeDb: (() => Promise<void>) | undefined;
|
||||
let worldStateId: number;
|
||||
|
||||
const buildContext = (requestId: string): GameApiContext => {
|
||||
const redisClient = {
|
||||
get: async () => null,
|
||||
set: async () => null,
|
||||
beforeAll(async () => {
|
||||
const schema = new URL(databaseUrl!).searchParams.get('schema');
|
||||
if (!schema?.endsWith('immediate_action_integration')) throw new Error(`Unsafe schema: ${schema}`);
|
||||
const connector = createGamePostgresConnector({ url: databaseUrl! });
|
||||
await connector.connect();
|
||||
db = connector.prisma;
|
||||
closeDb = () => connector.disconnect();
|
||||
await db.inputEvent.deleteMany({ where: { requestId: engineRequestId } });
|
||||
await db.message.deleteMany({ where: { mailbox: { in: [actorId, targetId] } } });
|
||||
await db.inheritanceLog.deleteMany({ where: { userId: actorUserId } });
|
||||
await db.inheritancePoint.deleteMany({ where: { userId: { in: [actorUserId, targetUserId] } } });
|
||||
await db.rankData.deleteMany({ where: { generalId: { in: [actorId, targetId] } } });
|
||||
await db.general.deleteMany({ where: { id: { in: [actorId, targetId] } } });
|
||||
await db.nation.deleteMany({ where: { id: { in: [actorNationId, targetNationId] } } });
|
||||
await db.worldState.deleteMany({ where: { id: worldId } });
|
||||
await db.worldState.create({
|
||||
data: {
|
||||
id: worldId,
|
||||
scenarioCode: 'inherit-owner-message',
|
||||
currentYear: 200,
|
||||
currentMonth: 4,
|
||||
tickSeconds: 600,
|
||||
config: JSON.parse(JSON.stringify(scenarioConfig)) as GamePrisma.InputJsonValue,
|
||||
meta: state.meta as GamePrisma.InputJsonValue,
|
||||
},
|
||||
});
|
||||
await db.nation.createMany({
|
||||
data: [
|
||||
{ id: actorNationId, name: actorNation.name, color: actorNation.color, level: 1 },
|
||||
{ id: targetNationId, name: targetNation.name, color: targetNation.color, level: 1 },
|
||||
],
|
||||
});
|
||||
await db.general.createMany({ data: [actor, target].map(toCreate) });
|
||||
await db.inheritancePoint.create({ data: { userId: actorUserId, key: 'previous', value: 1_500 } });
|
||||
await db.rankData.create({
|
||||
data: { generalId: actorId, nationId: actorNationId, type: 'inherit_spent_dyn', value: 0 },
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (db) {
|
||||
await db.inputEvent.deleteMany({ where: { requestId: engineRequestId } });
|
||||
await db.message.deleteMany({ where: { mailbox: { in: [actorId, targetId] } } });
|
||||
await db.inheritanceLog.deleteMany({ where: { userId: actorUserId } });
|
||||
await db.inheritancePoint.deleteMany({ where: { userId: { in: [actorUserId, targetUserId] } } });
|
||||
await db.rankData.deleteMany({ where: { generalId: { in: [actorId, targetId] } } });
|
||||
await db.general.deleteMany({ where: { id: { in: [actorId, targetId] } } });
|
||||
await db.nation.deleteMany({ where: { id: { in: [actorNationId, targetNationId] } } });
|
||||
await db.worldState.deleteMany({ where: { id: worldId } });
|
||||
}
|
||||
await closeDb?.();
|
||||
});
|
||||
|
||||
it('commits once and returns the durable result without double charging on the same API retry', async () => {
|
||||
const snapshot: TurnWorldSnapshot = {
|
||||
generals: [actor, target],
|
||||
cities: [],
|
||||
nations: [actorNation, targetNation],
|
||||
troops: [],
|
||||
diplomacy: [],
|
||||
events: [],
|
||||
initialEvents: [],
|
||||
scenarioConfig,
|
||||
scenarioMeta,
|
||||
map,
|
||||
};
|
||||
return {
|
||||
const world = new InMemoryTurnWorld(state, snapshot, { schedule });
|
||||
const queue = new DatabaseTurnDaemonCommandQueue(db);
|
||||
await queue.initialize();
|
||||
const hooks = await createDatabaseTurnHooks(databaseUrl!, world);
|
||||
const stateManager = new EngineStateManager();
|
||||
stateManager.register('world', {
|
||||
capture: () => world.captureState(),
|
||||
restore: (value) => world.restoreState(value),
|
||||
});
|
||||
const lifecycle = new TurnDaemonLifecycle(
|
||||
{
|
||||
clock: new SystemClock(),
|
||||
controlQueue: queue,
|
||||
commandResponder: queue,
|
||||
getNextTickTime: () => new Date(Date.now() + 3_600_000),
|
||||
stateStore: new InMemoryTurnStateStore(world),
|
||||
processor: {
|
||||
run: async () => {
|
||||
throw new Error('scheduled turn must not run in inheritance API integration');
|
||||
},
|
||||
},
|
||||
commandHandler: createTurnDaemonCommandHandler({ world }),
|
||||
hooks: hooks.hooks,
|
||||
stateManager,
|
||||
},
|
||||
{ profile: 'inherit-owner-message', defaultBudget: { budgetMs: 100, maxGenerals: 1, catchUpCap: 1 } }
|
||||
);
|
||||
const redisClient = { get: async () => null, set: async () => null };
|
||||
const context: GameApiContext = {
|
||||
requestId,
|
||||
db,
|
||||
redis: redisClient as unknown as RedisConnector['client'],
|
||||
turnDaemon: new InMemoryTurnDaemonTransport(),
|
||||
turnDaemon: new DatabaseTurnDaemonTransport(db, 10_000),
|
||||
battleSim: new InMemoryBattleSimTransport(),
|
||||
profile: { id: 'che', scenario: 'inherit-owner-message', name: 'che:inherit-owner-message' },
|
||||
uploadDir: 'uploads',
|
||||
@@ -72,157 +262,46 @@ integration('inherit owner lookup private messages', () => {
|
||||
accessTokenStore: new RedisAccessTokenStore(redisClient, 'che:inherit-owner-message'),
|
||||
flushStore: new InMemoryFlushStore(),
|
||||
gameTokenSecret: 'test-secret',
|
||||
readModelOutbox: { wake: vi.fn() },
|
||||
};
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const connector = createGamePostgresConnector({ url: databaseUrl! });
|
||||
await connector.connect();
|
||||
db = connector.prisma;
|
||||
closeDb = () => connector.disconnect();
|
||||
|
||||
await db.inputEvent.deleteMany({ where: { actorUserId } });
|
||||
await db.message.deleteMany({ where: { mailbox: { in: [actorGeneralId, checkedGeneralId] } } });
|
||||
await db.inheritanceLog.deleteMany({ where: { userId: actorUserId } });
|
||||
await db.inheritancePoint.deleteMany({ where: { userId: actorUserId } });
|
||||
await db.general.deleteMany({ where: { id: { in: [actorGeneralId, checkedGeneralId] } } });
|
||||
await db.nation.deleteMany({ where: { id: { in: [actorNationId, checkedNationId] } } });
|
||||
await db.readModelRevision.deleteMany({
|
||||
where: { domain: 'messages.mailbox', entityId: { in: [actorGeneralId, checkedGeneralId] } },
|
||||
});
|
||||
|
||||
await db.nation.createMany({
|
||||
data: [
|
||||
{ id: actorNationId, name: '확인국', color: '#123456', level: 2 },
|
||||
{ id: checkedNationId, name: '피확인국', color: '#654321', level: 3 },
|
||||
],
|
||||
});
|
||||
await db.general.createMany({
|
||||
data: [
|
||||
{
|
||||
id: actorGeneralId,
|
||||
userId: actorUserId,
|
||||
name: '확인장수',
|
||||
nationId: actorNationId,
|
||||
cityId: 1,
|
||||
npcState: 0,
|
||||
turnTime: new Date('0200-01-01T00:00:00.000Z'),
|
||||
meta: { ownerName: '확인자 계정' },
|
||||
},
|
||||
{
|
||||
id: checkedGeneralId,
|
||||
userId: checkedUserId,
|
||||
name: '피확인장수',
|
||||
nationId: checkedNationId,
|
||||
cityId: 1,
|
||||
npcState: 0,
|
||||
turnTime: new Date('0200-01-01T00:00:00.000Z'),
|
||||
meta: { ownerName: '피확인 계정' },
|
||||
},
|
||||
],
|
||||
});
|
||||
await db.inheritancePoint.create({
|
||||
data: { userId: actorUserId, key: 'previous', value: 1_500 },
|
||||
});
|
||||
const world = await db.worldState.create({
|
||||
data: {
|
||||
scenarioCode: 'inherit-owner-message',
|
||||
currentYear: 200,
|
||||
currentMonth: 4,
|
||||
tickSeconds: 600,
|
||||
config: { const: { inheritCheckOwnerPoint: 1_000 } },
|
||||
meta: { isUnited: 0 },
|
||||
},
|
||||
});
|
||||
worldStateId = world.id;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const outboxes = await db.readModelOutbox.findMany({ select: { id: true, payload: true } });
|
||||
const outboxIds = outboxes.filter(({ payload }) => hasMailboxChange(payload)).map(({ id }) => id);
|
||||
if (outboxIds.length > 0) {
|
||||
await db.readModelOutbox.deleteMany({ where: { id: { in: outboxIds } } });
|
||||
let loop: Promise<void> | undefined;
|
||||
try {
|
||||
loop = lifecycle.start();
|
||||
const caller = appRouter.createCaller(context);
|
||||
const expected = { ok: true, ownerName: '피확인 계정', targetName: '피확인장수' };
|
||||
await expect(caller.inherit.checkOwner({ targetGeneralId: targetId })).resolves.toEqual(expected);
|
||||
await expect(caller.inherit.checkOwner({ targetGeneralId: targetId })).resolves.toEqual(expected);
|
||||
} finally {
|
||||
await lifecycle.stop('inherit owner integration finished');
|
||||
await loop;
|
||||
await hooks.close();
|
||||
}
|
||||
await db.inputEvent.deleteMany({ where: { actorUserId } });
|
||||
await db.message.deleteMany({ where: { mailbox: { in: [actorGeneralId, checkedGeneralId] } } });
|
||||
await db.inheritanceLog.deleteMany({ where: { userId: actorUserId } });
|
||||
await db.inheritancePoint.deleteMany({ where: { userId: actorUserId } });
|
||||
await db.general.deleteMany({ where: { id: { in: [actorGeneralId, checkedGeneralId] } } });
|
||||
await db.nation.deleteMany({ where: { id: { in: [actorNationId, checkedNationId] } } });
|
||||
await db.readModelRevision.deleteMany({
|
||||
where: { domain: 'messages.mailbox', entityId: { in: [actorGeneralId, checkedGeneralId] } },
|
||||
});
|
||||
await db.worldState.delete({ where: { id: worldStateId } });
|
||||
await closeDb?.();
|
||||
});
|
||||
|
||||
it('commits the point charge, log, and both Ref-compatible private messages', async () => {
|
||||
const requestId = 'integration:inherit-owner-message:success';
|
||||
await expect(
|
||||
appRouter.createCaller(buildContext(requestId)).inherit.checkOwner({ targetGeneralId: checkedGeneralId })
|
||||
).resolves.toEqual({
|
||||
ok: true,
|
||||
ownerName: '피확인 계정',
|
||||
targetName: '피확인장수',
|
||||
});
|
||||
|
||||
await expect(
|
||||
db.inheritancePoint.findUniqueOrThrow({
|
||||
where: { userId_key: { userId: actorUserId, key: 'previous' } },
|
||||
})
|
||||
db.inheritancePoint.findUniqueOrThrow({ where: { userId_key: { userId: actorUserId, key: 'previous' } } })
|
||||
).resolves.toMatchObject({ value: 500 });
|
||||
await expect(db.inheritanceLog.findMany({ where: { userId: actorUserId } })).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
year: 200,
|
||||
month: 4,
|
||||
text: '1000 포인트로 장수 소유자 확인',
|
||||
}),
|
||||
]);
|
||||
|
||||
const messages = await db.message.findMany({
|
||||
where: { mailbox: { in: [actorGeneralId, checkedGeneralId] } },
|
||||
orderBy: { mailbox: 'asc' },
|
||||
});
|
||||
expect(messages).toHaveLength(2);
|
||||
expect(
|
||||
messages.map(({ mailbox, type, src, dest, message }) => ({ mailbox, type, src, dest, message }))
|
||||
).toEqual([
|
||||
{
|
||||
mailbox: actorGeneralId,
|
||||
type: 'private',
|
||||
src: 0,
|
||||
dest: actorGeneralId,
|
||||
message: expect.objectContaining({
|
||||
src: expect.objectContaining({ generalId: 0, nationName: 'System' }),
|
||||
dest: expect.objectContaining({ generalId: actorGeneralId, generalName: '확인장수' }),
|
||||
text: '피확인장수의 소유자는 피확인 계정 입니다.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
mailbox: checkedGeneralId,
|
||||
type: 'private',
|
||||
src: 0,
|
||||
dest: checkedGeneralId,
|
||||
message: expect.objectContaining({
|
||||
src: expect.objectContaining({ generalId: 0, nationName: 'System' }),
|
||||
dest: expect.objectContaining({ generalId: checkedGeneralId, generalName: '피확인장수' }),
|
||||
text: '소유자명이 누군가에 의해 확인되었습니다.',
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
await expect(
|
||||
db.inputEvent.findUniqueOrThrow({ where: { requestId: `${requestId}:inherit.checkOwner` } })
|
||||
).resolves.toMatchObject({ status: 'SUCCEEDED', actorUserId });
|
||||
await expect(
|
||||
db.readModelRevision.findMany({
|
||||
where: { domain: 'messages.mailbox', entityId: { in: [actorGeneralId, checkedGeneralId] } },
|
||||
orderBy: { entityId: 'asc' },
|
||||
db.rankData.findUniqueOrThrow({
|
||||
where: { generalId_type: { generalId: actorId, type: 'inherit_spent_dyn' } },
|
||||
})
|
||||
).resolves.toEqual([
|
||||
expect.objectContaining({ domain: 'messages.mailbox', entityId: actorGeneralId, revision: 1n }),
|
||||
expect.objectContaining({ domain: 'messages.mailbox', entityId: checkedGeneralId, revision: 1n }),
|
||||
).resolves.toMatchObject({ value: 1_000 });
|
||||
await expect(db.inheritanceLog.count({ where: { userId: actorUserId } })).resolves.toBe(1);
|
||||
const messages = await db.message.findMany({
|
||||
where: { mailbox: { in: [actorId, targetId] } },
|
||||
orderBy: { id: 'asc' },
|
||||
select: { mailbox: true, message: true },
|
||||
});
|
||||
expect(messages.map(({ mailbox, message }) => [mailbox, (message as { text: string }).text])).toEqual([
|
||||
[actorId, '피확인장수의 소유자는 피확인 계정 입니다.'],
|
||||
[targetId, '소유자명이 누군가에 의해 확인되었습니다.'],
|
||||
]);
|
||||
});
|
||||
await expect(db.inputEvent.findMany({ where: { requestId: engineRequestId } })).resolves.toEqual([
|
||||
expect.objectContaining({ status: 'SUCCEEDED', attempts: 1, actorUserId }),
|
||||
]);
|
||||
const reloaded = await loadTurnWorldFromDatabase({ databaseUrl: databaseUrl! });
|
||||
expect(reloaded.snapshot.generals.find((entry) => entry.id === actorId)).toMatchObject({
|
||||
meta: { inherit_spent_dyn: 1_000 },
|
||||
inheritancePoints: { previous: 500 },
|
||||
});
|
||||
}, 30_000);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user