인게임 예약 명령과 장수 상태 표시를 바로잡음
This commit is contained in:
@@ -112,12 +112,22 @@ describe('turn command argument input', () => {
|
||||
).toThrow('턴이 입력되지 않았습니다.');
|
||||
expect(() => assertReservedTurnArgsPassLegacyBasicValidation({ month: '12', year: 0 })).not.toThrow();
|
||||
expect(() => assertReservedTurnArgsPassLegacyBasicValidation({ nationName: '0' })).not.toThrow();
|
||||
expect(() => assertReservedTurnArgsPassLegacyBasicValidation({ nationName: '가나다라마바사아자차' })).toThrow(
|
||||
'국가명은 전각 9자 또는 반각 18자 이하여야 합니다.'
|
||||
);
|
||||
expect(() => assertReservedTurnArgsPassLegacyBasicValidation({ year: '0x10' })).toThrow(
|
||||
'턴이 입력되지 않았습니다.'
|
||||
);
|
||||
await expect(parseReservedTurnArgs('nation', 'che_국호변경', { nationName: '0' })).rejects.toBeDefined();
|
||||
});
|
||||
|
||||
it('exposes the Ref fullwidth nation-name limit to command input clients', async () => {
|
||||
const specs = await loadGeneralTurnCommandSpecs(['che_건국']);
|
||||
expect(buildTurnCommandInputFields(specs[0]!)).toContainEqual(
|
||||
expect.objectContaining({ key: 'nationName', legacyWidthMax: 18 })
|
||||
);
|
||||
});
|
||||
|
||||
it('recursively sanitizes reserved command strings like Ref before command parsing', async () => {
|
||||
expect(
|
||||
sanitizeReservedTurnArgs({
|
||||
|
||||
@@ -88,9 +88,9 @@ describe('Ref command general targets', () => {
|
||||
description: expect.stringContaining('탑승 부대 청룡대'),
|
||||
});
|
||||
expect(detailed.generalTargets.che_발령?.map((entry) => entry.label)).toEqual([
|
||||
'본인 (부대 없음 · 업)',
|
||||
'부대원 (청룡대 · 업)',
|
||||
'부대장 (청룡대 (부대장) · 업)',
|
||||
'본인 (업)',
|
||||
'부대원 (업)',
|
||||
'부대장 (업)',
|
||||
]);
|
||||
expect(detailed.generalTargets.che_발령?.[1]?.description).not.toContain('탑승 부대');
|
||||
expect(detailed.generalTargets.che_포상?.map((entry) => entry.label)).toEqual([
|
||||
|
||||
@@ -177,12 +177,12 @@ describe('messages router missing-flow compatibility', () => {
|
||||
expect(recent.permission).toBe(2);
|
||||
expect(recent.diplomacy[0]).toMatchObject({
|
||||
text: '조회 권한이 없는 외교 메시지입니다.',
|
||||
option: { action: 'noAggression' },
|
||||
option: { action: 'noAggression', permissionRedacted: true },
|
||||
});
|
||||
expect(recent.diplomacy[0]?.option).not.toHaveProperty('invalid');
|
||||
expect(old.diplomacy[0]).toMatchObject({
|
||||
text: '조회 권한이 없는 외교 메시지입니다.',
|
||||
option: { action: 'noAggression' },
|
||||
option: { action: 'noAggression', permissionRedacted: true },
|
||||
});
|
||||
expect(old.diplomacy[0]?.option).not.toHaveProperty('invalid');
|
||||
});
|
||||
|
||||
@@ -154,6 +154,7 @@ describe('nation general and secret office permissions', () => {
|
||||
const ally = general({
|
||||
id: 3,
|
||||
userId: 'u3',
|
||||
injury: 25,
|
||||
gold: 3000,
|
||||
crew: 200,
|
||||
train: 80,
|
||||
@@ -166,6 +167,10 @@ describe('nation general and secret office permissions', () => {
|
||||
expect(result.viewer).toEqual({ generalId: 2, permission: 2 });
|
||||
expect(result.generals.map((g) => g.id)).toEqual([1, 2, 3]);
|
||||
expect(result.generals.find((entry) => entry.id === 3)?.experienceLevel).toBe(200);
|
||||
expect(result.generals.find((entry) => entry.id === 3)).toMatchObject({
|
||||
baseStats: { leadership: 70, strength: 60, intelligence: 50 },
|
||||
stats: { leadership: 52, strength: 45, intelligence: 37 },
|
||||
});
|
||||
expect(result.summary).toMatchObject({ gold: 5000, crew: 800, generalCount: 3 });
|
||||
expect(result.generals[0]?.reservedCommands).toEqual([
|
||||
{ action: 'che_징병', args: { crewType: 1, amount: 300 } },
|
||||
|
||||
@@ -161,6 +161,20 @@ describe('NPC policy router', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the live world development cost instead of the scenario snapshot', async () => {
|
||||
const fixture = createContext({
|
||||
world: {
|
||||
...baseWorld,
|
||||
config: { ...baseWorld.config, const: { develCost: 100 } },
|
||||
meta: { ...baseWorld.meta, develcost: 42 } as typeof baseWorld.meta & { develcost: number },
|
||||
},
|
||||
});
|
||||
|
||||
const result = await appRouter.createCaller(fixture.context).npc.getPolicy();
|
||||
|
||||
expect(result.zeroPolicy.reqNPCDevelGold).toBe(1_260);
|
||||
});
|
||||
|
||||
it('lets a secret-level reader load the page while mapping authoritative ENGINE rejection', async () => {
|
||||
const reader = { ...baseGeneral, officerLevel: 2 };
|
||||
const requestCommand = vi.fn(async () => ({
|
||||
|
||||
@@ -282,6 +282,18 @@ describe('troop router permissions and mutations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('shows all five visible turns as assembly for a managed NPC troop leader', async () => {
|
||||
const fixture = buildContext({
|
||||
me: buildGeneral({ troopId: 1, npcState: 5, meta: { killturn: 70 } }),
|
||||
turns: [{ generalId: 1, turnIdx: 0, actionCode: '휴식' }],
|
||||
result: null,
|
||||
});
|
||||
|
||||
const result = await appRouter.createCaller(fixture.context).troop.getList();
|
||||
|
||||
expect(result.troops[0]?.reservedCommands).toEqual(['집합', '집합', '집합', '집합', '집합']);
|
||||
});
|
||||
|
||||
it('creates a troop only for the general owned by the authenticated user', async () => {
|
||||
const { context, requestCommand } = buildContext({
|
||||
result: { type: 'troopCreate', ok: true, generalId: 1, troopId: 1, troopName: '백마대' },
|
||||
|
||||
Reference in New Issue
Block a user