플레이 감사에 외교 관계 최초 관측을 기록

This commit is contained in:
2026-09-16 06:42:50 +00:00
parent 02ee706af7
commit c719cecc80
7 changed files with 247 additions and 2 deletions
@@ -1,3 +1,4 @@
import { initializeAuditDiplomacy } from '../src/playAudit/diplomacy.js';
import { describe, expect, it } from 'vitest';
import type { City, Nation } from '@sammo-ts/logic';
import { InMemoryTurnWorld, type GeneralTurnHandler } from '../src/turn/inMemoryWorld.js';
@@ -128,6 +129,37 @@ const buildWorld = (generalTurnHandler?: GeneralTurnHandler) => {
return world;
};
describe('play audit collection durability state', () => {
it('marks an empty diplomacy baseline without inventing relations and validates before queuing', () => {
const world = buildWorld();
world.removeNation(1);
world.removeNation(2);
expect(() => initializeAuditDiplomacy(world, new Date('invalid'))).toThrow(RangeError);
expect(world.getState().meta.playAuditDiplomacy).toBeUndefined();
expect(world.peekDirtyState().pendingAuditDiplomacy).toEqual([]);
expect(initializeAuditDiplomacy(world)).toBe(true);
expect(world.getState().meta.playAuditDiplomacy).toMatchObject({ relationCount: 0 });
expect(world.peekDirtyState().pendingAuditDiplomacy).toEqual([]);
expect(initializeAuditDiplomacy(world)).toBe(false);
});
it('captures a single diplomacy baseline and restores its marker and queue together', () => {
const world = buildWorld();
const checkpoint = world.captureState();
expect(initializeAuditDiplomacy(world, new Date('2026-09-16T00:00:00Z'))).toBe(true);
const events = world.peekDirtyState().pendingAuditDiplomacy;
expect(events).toHaveLength(2);
expect(events.map((event) => [event.srcNationId, event.destNationId])).toEqual([
[1, 2],
[2, 1],
]);
expect(events[0]).toMatchObject({ source: 'BASELINE', before: null, after: { state: 2, term: 0, dead: 0 } });
expect(initializeAuditDiplomacy(world)).toBe(false);
expect(world.peekDirtyState().pendingAuditDiplomacy).toEqual(events);
world.restoreState(checkpoint);
expect(world.getState().meta.playAuditDiplomacy).toBeUndefined();
expect(world.peekDirtyState().pendingAuditDiplomacy).toEqual([]);
});
it('preserves consecutive diplomacy transitions in one turn and restores them with the checkpoint', () => {
const world = buildWorld({
execute: ({ general }) => ({
@@ -43,6 +43,7 @@ integration('initial audit durability before runtime readiness', () => {
closeDb = () => connector.disconnect();
await db.playAuditMonth.deleteMany();
await db.playAuditPolicy.deleteMany();
await db.playAuditDiplomacyEvent.deleteMany();
await seedScenarioToDatabase({
scenarioId: 903,
databaseUrl: databaseUrl!,
@@ -56,6 +57,15 @@ integration('initial audit durability before runtime readiness', () => {
season: 1,
},
});
await db.nation.createMany({
data: [
{ id: 91990, name: '기준 발신국', color: '#ffffff' },
{ id: 91991, name: '기준 수신국', color: '#000000' },
],
});
await db.diplomacy.create({
data: { srcNationId: 91990, destNationId: 91991, stateCode: 7, term: 12, meta: { dead: 34 } },
});
}, 60_000);
afterAll(async () => {
await runtime?.close();
@@ -80,6 +90,8 @@ integration('initial audit durability before runtime readiness', () => {
expect(String(error)).toContain('fixture initial audit failure');
expect(await db.playAuditMonth.count()).toBe(0);
expect(await db.playAuditPolicy.count()).toBe(0);
expect(await db.playAuditDiplomacyEvent.count()).toBe(0);
expect(asRecord((await db.worldState.findFirstOrThrow()).meta).playAuditDiplomacy).toBeUndefined();
expect(asRecord((await db.worldState.findFirstOrThrow()).meta).playAuditCollection).toBeUndefined();
expect(
(await db.nation.findMany()).every((nation) => asRecord(nation.meta)._playAuditPolicy === undefined)
@@ -104,6 +116,37 @@ integration('initial audit durability before runtime readiness', () => {
expect((await db.turnDaemonLease.findUniqueOrThrow({ where: { profile } })).clockReady).toBe(true);
const initial = await db.playAuditMonth.findFirstOrThrow({ where: { serverId, kind: 'INITIAL' } });
const policies = await db.playAuditPolicy.findMany({ where: { serverId }, orderBy: { id: 'asc' } });
const diplomacy = await db.playAuditDiplomacyEvent.findMany({
where: { serverId },
orderBy: { ordinal: 'asc' },
});
expect(diplomacy).toHaveLength(
await db.diplomacy.count({ where: { srcNationId: { gt: 0 }, destNationId: { gt: 0 } } })
);
expect(diplomacy).toHaveLength(2);
expect(diplomacy[0]).toMatchObject({
srcNationId: 91990,
destNationId: 91991,
before: null,
after: { state: 7, term: 12, dead: 34 },
});
expect(diplomacy[1]).toMatchObject({
srcNationId: 91991,
destNationId: 91990,
after: { state: 2, term: 0, dead: 0 },
});
expect(
diplomacy.every(
(event) =>
event.source === 'BASELINE' &&
event.before === null &&
event.actor === null &&
event.requestId === null
)
).toBe(true);
const diplomacyMarker = asRecord((await db.worldState.findFirstOrThrow()).meta).playAuditDiplomacy;
expect(diplomacyMarker).toMatchObject({ serverId, schemaVersion: 1, relationCount: diplomacy.length });
expect(policies).toHaveLength((await db.nation.count()) * 4);
expect(
policies.every(
@@ -128,6 +171,10 @@ integration('initial audit durability before runtime readiness', () => {
expect(await db.playAuditPolicy.findMany({ where: { serverId }, orderBy: { id: 'asc' } })).toEqual(policies);
expect(await db.playAuditMonth.findMany({ where: { serverId, kind: 'INITIAL' } })).toEqual([initial]);
expect(asRecord((await db.worldState.findFirstOrThrow()).meta).playAuditCollection).toEqual(marker);
expect(await db.playAuditDiplomacyEvent.findMany({ where: { serverId }, orderBy: { ordinal: 'asc' } })).toEqual(
diplomacy
);
expect(asRecord((await db.worldState.findFirstOrThrow()).meta).playAuditDiplomacy).toEqual(diplomacyMarker);
expect(await clock()).toEqual(beforeClock);
expect(await db.inputEvent.count()).toBe(beforeInputs);
await expect(