feat: 직접 writer를 내구성 변화 저널에 연결

86개 API mutation을 분류하고 메시지 mailbox, 베팅, 국가 설정, 예약 명령의 revision/outbox 표식을 소유 transaction에 연결한다. 공개 SSE는 식별자 없는 invalidation만 노출한다.
This commit is contained in:
2026-08-16 18:38:08 +00:00
parent e4ac2a60b1
commit dd4645e4d0
22 changed files with 485 additions and 55 deletions
+9 -1
View File
@@ -1,5 +1,6 @@
import { describe, expect, it, vi } from 'vitest';
import { ChangeJournal } from '@sammo-ts/common';
import type { GameSessionTokenPayload } from '@sammo-ts/common/auth/gameToken';
import type { RedisConnector } from '@sammo-ts/infra';
@@ -70,6 +71,7 @@ const auth: GameSessionTokenPayload = {
};
const buildContext = () => {
const changeJournal = new ChangeJournal();
const requestCommand = vi.fn(async (command: unknown) => ({
type: 'setNationMeta',
ok: true,
@@ -95,6 +97,7 @@ const buildContext = () => {
battleSim: {} as GameApiContext['battleSim'],
profile: { id: 'che', scenario: 'default', name: 'che:default' },
auth,
changeJournal,
uploadDir: 'uploads',
uploadPath: '/uploads',
uploadPublicUrl: null,
@@ -102,7 +105,7 @@ const buildContext = () => {
flushStore: new InMemoryFlushStore(),
gameTokenSecret: 'test-secret',
};
return { caller: appRouter.createCaller(context), requestCommand };
return { caller: appRouter.createCaller(context), requestCommand, changeJournal };
};
describe('nation HTML API boundary', () => {
@@ -135,6 +138,11 @@ describe('nation HTML API boundary', () => {
},
expectedUpdatedAt: undefined,
});
expect(fixture.changeJournal.snapshot()).toEqual([
{ domain: 'dashboard.global', entityId: 0 },
...(procedure === 'setNotice' ? [{ domain: 'front.nation' as const, entityId: 1 }] : []),
{ domain: 'nation.content', entityId: 1 },
]);
});
it.each(['setNotice', 'setScoutMsg'] as const)(