163 lines
4.9 KiB
TypeScript
163 lines
4.9 KiB
TypeScript
import { existsSync } from 'node:fs';
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import { SERVER_RUNTIME_FUNCTION_IDS } from '../../../packages/shared/src/contracts/rpgRuntimeStoryAction';
|
|
import type { Encounter, GameState, InventoryItem } from '../../types';
|
|
import {
|
|
ALL_FUNCTION_DOCUMENTATION,
|
|
buildCampTravelHomeOption,
|
|
buildContinueAdventureOption,
|
|
buildNpcGiftModalState,
|
|
buildNpcPreviewTalkOption,
|
|
buildNpcRecruitModalState,
|
|
buildNpcTradeModalState,
|
|
CAMP_TRAVEL_HOME_FUNCTION,
|
|
CONTINUE_ADVENTURE_FUNCTION,
|
|
getFunctionDocumentationById,
|
|
isNpcPreviewTalkOption,
|
|
NPC_PREVIEW_TALK_FUNCTION,
|
|
shouldNpcRecruitOpenModal,
|
|
} from './index';
|
|
import { RPG_FUNCTION_RUNTIME_OVERVIEW } from './runtimeIndex';
|
|
|
|
function createEncounter(overrides: Partial<Encounter> = {}): Encounter {
|
|
return {
|
|
id: 'npc-trader',
|
|
kind: 'npc',
|
|
npcName: '梁伯',
|
|
npcDescription: '沿路摆摊的商人。',
|
|
npcAvatar: '梁',
|
|
context: '商贩',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function createInventoryItem(
|
|
id: string,
|
|
name: string,
|
|
overrides: Partial<InventoryItem> = {},
|
|
): InventoryItem {
|
|
return {
|
|
id,
|
|
name,
|
|
description: `${name} 的测试描述`,
|
|
quantity: 1,
|
|
category: 'misc',
|
|
rarity: 'common',
|
|
tags: [],
|
|
value: 1,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function createModalState(overrides: Partial<GameState> = {}): GameState {
|
|
return {
|
|
playerInventory: [
|
|
createInventoryItem('player-potion', '疗伤药'),
|
|
createInventoryItem('player-charm', '护符'),
|
|
],
|
|
companions: [
|
|
{
|
|
npcId: 'npc-ally-1',
|
|
characterId: 'ally-1',
|
|
name: '阿青',
|
|
role: '同伴',
|
|
joinedAtAffinity: 12,
|
|
},
|
|
],
|
|
...overrides,
|
|
} as GameState;
|
|
}
|
|
|
|
describe('functionCatalog', () => {
|
|
it('keeps function documentation ids unique and source files resolvable', () => {
|
|
const documentationIds = ALL_FUNCTION_DOCUMENTATION.map((entry) => entry.id);
|
|
|
|
expect(new Set(documentationIds).size).toBe(documentationIds.length);
|
|
ALL_FUNCTION_DOCUMENTATION.forEach((entry) => {
|
|
expect(existsSync(entry.source), `${entry.id} -> ${entry.source}`).toBe(
|
|
true,
|
|
);
|
|
expect(getFunctionDocumentationById(entry.id)).toEqual(entry);
|
|
});
|
|
});
|
|
|
|
it('covers every server runtime function id with documentation metadata', () => {
|
|
SERVER_RUNTIME_FUNCTION_IDS.forEach((functionId) => {
|
|
expect(getFunctionDocumentationById(functionId)).not.toBeNull();
|
|
});
|
|
});
|
|
|
|
it('keeps runtime overview aligned with the main function documentation list', () => {
|
|
expect(RPG_FUNCTION_RUNTIME_OVERVIEW.allDocumentation).toEqual(
|
|
ALL_FUNCTION_DOCUMENTATION,
|
|
);
|
|
});
|
|
|
|
it('builds flow helper options with the expected function ids', () => {
|
|
const continueOption = buildContinueAdventureOption();
|
|
const campTravelOption = buildCampTravelHomeOption('竹林古道');
|
|
|
|
expect(continueOption.functionId).toBe(CONTINUE_ADVENTURE_FUNCTION.id);
|
|
expect(continueOption.priority).toBe(99);
|
|
expect(campTravelOption.functionId).toBe('camp_travel_home_scene');
|
|
expect(campTravelOption.actionText).toBe('前往 竹林古道');
|
|
expect(campTravelOption.detailText).toBe('离开营地,前往 竹林古道。');
|
|
expect(CAMP_TRAVEL_HOME_FUNCTION.runtime?.executor).toContain(
|
|
'server-rs/crates/module-runtime-story/src/session_action.rs',
|
|
);
|
|
expect(CAMP_TRAVEL_HOME_FUNCTION.detailedDescription).toContain(
|
|
'后端 resolver',
|
|
);
|
|
});
|
|
|
|
it('builds npc preview talk options from the current encounter', () => {
|
|
const option = buildNpcPreviewTalkOption(createEncounter());
|
|
|
|
expect(option.functionId).toBe(NPC_PREVIEW_TALK_FUNCTION.id);
|
|
expect(option.actionText).toBe('与 梁伯 交谈');
|
|
expect(isNpcPreviewTalkOption(option)).toBe(true);
|
|
});
|
|
|
|
it('builds modal helper state for trade, gift and recruit flows', () => {
|
|
const state = createModalState();
|
|
const encounter = createEncounter();
|
|
const tradeModal = buildNpcTradeModalState(
|
|
encounter,
|
|
'先看看货',
|
|
'npc-herb',
|
|
'player-potion',
|
|
);
|
|
const giftModal = buildNpcGiftModalState(
|
|
encounter,
|
|
'送你一样东西',
|
|
'player-charm',
|
|
);
|
|
const recruitModal = buildNpcRecruitModalState(
|
|
state,
|
|
encounter,
|
|
'谈谈同行的事',
|
|
);
|
|
|
|
expect(tradeModal.selectedNpcItemId).toBe('npc-herb');
|
|
expect(tradeModal.selectedPlayerItemId).toBe('player-potion');
|
|
expect(giftModal.selectedItemId).toBe('player-charm');
|
|
expect(recruitModal.selectedReleaseNpcId).toBe('npc-ally-1');
|
|
expect(shouldNpcRecruitOpenModal(2, 2)).toBe(true);
|
|
expect(shouldNpcRecruitOpenModal(1, 2)).toBe(false);
|
|
});
|
|
|
|
it('keeps server-selected trade item ids when opening the trade modal', () => {
|
|
const encounter = createEncounter();
|
|
const tradeModal = buildNpcTradeModalState(
|
|
encounter,
|
|
'交易',
|
|
'npc-herb',
|
|
'usable-item',
|
|
);
|
|
|
|
expect(tradeModal.selectedPlayerItemId).toBe('usable-item');
|
|
});
|
|
});
|