빙의 군주의 외교권자 임명 권한을 복구

This commit is contained in:
2026-09-23 04:35:44 +00:00
parent 99a831b251
commit 7e9cde3592
5 changed files with 38 additions and 4 deletions
@@ -249,9 +249,9 @@ describe('nation personnel router', () => {
});
it('keeps ambassador and auditor candidate pools mutually exclusive like Ref', async () => {
const me = { ...baseGeneral, officerLevel: 12 };
const me = { ...baseGeneral, officerLevel: 12, npcState: 1 };
const rows = [
listRow({ id: 22, name: '군주', officerLevel: 12 }),
listRow({ id: 22, name: '군주', officerLevel: 12, npcState: 1 }),
listRow({ id: 30, name: '현 외교권자', meta: { belong: 5, permission: 'ambassador' } }),
listRow({ id: 31, name: '현 조언자', meta: { belong: 5, permission: 'auditor' } }),
listRow({ id: 32, name: '일반 후보' }),
@@ -283,6 +283,7 @@ describe('nation personnel router', () => {
});
const result = await appRouter.createCaller(context).nation.getPersonnelInfo();
expect(result.me.canChangePermissions).toBe(true);
expect(result.permissionCandidates.ambassadors.map((candidate) => candidate.id)).toEqual([30, 32]);
expect(result.permissionCandidates.auditors.map((candidate) => candidate.id)).toEqual([31, 32]);
});
@@ -288,6 +288,12 @@ export const seedScenarioToDatabase = async (options: ScenarioSeedOptions): Prom
},
});
seed.cities = applyInitialChangeCityEvents(seed.cities, seed.initialEvents);
const seededRulerIds = new Map<number, number>();
for (const general of [...seed.generals].sort((left, right) => left.id - right.id)) {
if (general.officerLevel === 12 && !seededRulerIds.has(general.nationId)) {
seededRulerIds.set(general.nationId, general.id);
}
}
const connector = createGamePostgresConnector({ url: options.databaseUrl });
const generalGold = options.defaultGeneralGold ?? DEFAULT_GENERAL_GOLD;
@@ -556,6 +562,7 @@ export const seedScenarioToDatabase = async (options: ScenarioSeedOptions): Prom
name: nation.name,
color: nation.color,
capitalCityId: nation.capitalCityId ?? null,
chiefGeneralId: seededRulerIds.get(nation.id) ?? null,
gold: nation.gold,
rice: nation.rice,
tech: nation.tech,
@@ -2144,7 +2144,7 @@ async function handleChangePermission(
};
}
const nation = world.getNationById(general.nationId);
if (!nation || general.officerLevel !== 12 || nation.chiefGeneralId !== general.id) {
if (!nation || general.officerLevel !== 12) {
return { type: 'changePermission', ok: false, generalId: command.generalId, reason: '군주가 아닙니다.' };
}
@@ -52,6 +52,7 @@ const buildGeneral = (id: number, overrides: Partial<TurnGeneral> = {}): TurnGen
const buildWorld = (options: {
generals?: TurnGeneral[];
nationMeta?: Record<string, TriggerValue>;
nationChiefGeneralId?: number | null;
cityMeta?: Record<string, TriggerValue>;
currentYear?: number;
scenarioConst?: Record<string, unknown>;
@@ -106,7 +107,7 @@ const buildWorld = (options: {
name: '위',
color: '#777777',
capitalCityId: 1,
chiefGeneralId: 1,
chiefGeneralId: options.nationChiefGeneralId === undefined ? 1 : options.nationChiefGeneralId,
gold: 10_000,
rice: 20_000,
power: 0,
@@ -321,6 +322,26 @@ describe('nation personnel world commands', () => {
expect(fixture.world.getGeneralById(3)?.meta.permission).toBe('normal');
});
it('allows a possessed ruler to appoint an ambassador when a seeded nation has no chief pointer', async () => {
const fixture = buildWorld({
nationChiefGeneralId: null,
generals: [
buildGeneral(1, { officerLevel: 12, npcState: 1 }),
buildGeneral(2, { npcState: 2 }),
],
});
await expect(
fixture.handler.handle({
type: 'changePermission',
userId: 'user-1',
generalId: 1,
isAmbassador: true,
targetGeneralIds: [2],
})
).resolves.toMatchObject({ ok: true });
expect(fixture.world.getGeneralById(2)?.meta.permission).toBe('ambassador');
});
it('kicks for an unlocked head officer with resource, troop, permission, and log side effects', async () => {
const target = buildGeneral(3, {
troopId: 3,
@@ -591,6 +591,11 @@ describeDb('scenario database seed', () => {
]);
expect(nationCount).toBe(seed.nations.length);
const seededRuler = seed.generals.find((general) => general.officerLevel === 12 && general.nationId > 0);
expect(seededRuler).toBeDefined();
expect(
(await connector.prisma.nation.findUnique({ where: { id: seededRuler!.nationId } }))?.chiefGeneralId
).toBe(seededRuler!.id);
expect(cityCount).toBe(seed.cities.length);
expect(generalCount).toBe(seed.generals.length);
expect(diplomacyCount).toBe(seed.nations.length * Math.max(0, seed.nations.length - 1));