fix: 특수 유저 커맨드의 Ref 호환 경계를 보강한다

수뇌 국가 설정과 NPC 정책을 actor-bound ENGINE mutation으로 옮기고, 추방·등용·점령·멸망·아이템 폐기의 특수 분기를 Ref와 맞춘다.

요청 ID를 사용자·프로필별로 격리하고 토너먼트 손상 projection을 fail-closed하며 실제 DB 및 Ref 차등 회귀를 보강한다.
This commit is contained in:
2026-08-24 12:10:57 +00:00
parent 5389f8ed94
commit 630bc29100
74 changed files with 3893 additions and 1141 deletions
+34 -11
View File
@@ -140,9 +140,26 @@ describe('nation personnel router', () => {
await caller.nation.changePermission({ isAmbassador: true, targetGeneralIds: [9] });
expect(requestCommand.mock.calls).toEqual([
[{ type: 'appoint', generalId: 22, destGeneralId: 7, destCityId: 1, officerLevel: 4 }],
[{ type: 'kick', generalId: 22, destGeneralId: 8 }],
[{ type: 'changePermission', generalId: 22, isAmbassador: true, targetGeneralIds: [9] }],
[
{
type: 'appoint',
userId: 'user-22',
generalId: 22,
destGeneralId: 7,
destCityId: 1,
officerLevel: 4,
},
],
[{ type: 'kick', userId: 'user-22', generalId: 22, destGeneralId: 8 }],
[
{
type: 'changePermission',
userId: 'user-22',
generalId: 22,
isAmbassador: true,
targetGeneralIds: [9],
},
],
]);
});
@@ -160,6 +177,7 @@ describe('nation personnel router', () => {
expect(requestCommand).toHaveBeenCalledWith({
type: 'kick',
requestId: 'http-nation-kick:nation.kick:engine:0:kick',
userId: 'user-22',
generalId: 22,
destGeneralId: 8,
});
@@ -277,7 +295,7 @@ describe('nation personnel router', () => {
};
const makeCommand = () =>
vi.fn(async () => ({
type: 'setNationMeta',
type: 'setNationSetting',
ok: true,
nationId: 1,
updatedAt: '2026-01-01T00:01:00.000Z',
@@ -291,15 +309,13 @@ describe('nation personnel router', () => {
.nation.setRate({ amount: 20 })
).resolves.toEqual({ ok: true });
expect(headCommand).toHaveBeenCalledWith({
type: 'setNationMeta',
type: 'setNationSetting',
userId: 'user-22',
generalId: 22,
nationId: 1,
updates: { rate: 20 },
expectedUpdatedAt: '2026-01-01T00:00:00.000Z',
mutation: { kind: 'rate', amount: 20 },
});
expect(changeJournal.snapshot()).toEqual([
{ domain: 'dashboard.global', entityId: 0 },
{ domain: 'nation.content', entityId: 1 },
]);
expect(changeJournal.snapshot()).toEqual([]);
const ambassadorCommand = makeCommand();
const ambassador = {
@@ -312,6 +328,13 @@ describe('nation personnel router', () => {
.createCaller(createContext({ me: ambassador, db: nationDb, requestCommand: ambassadorCommand }))
.nation.setRate({ amount: 25 })
).resolves.toEqual({ ok: true });
expect(ambassadorCommand).toHaveBeenCalledWith({
type: 'setNationSetting',
userId: 'user-22',
generalId: 22,
nationId: 1,
mutation: { kind: 'rate', amount: 25 },
});
const memberCommand = makeCommand();
const member = { ...baseGeneral, officerLevel: 1 };