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 { return { id: 'npc-trader', kind: 'npc', npcName: '梁伯', npcDescription: '沿路摆摊的商人。', npcAvatar: '梁', context: '商贩', ...overrides, }; } function createInventoryItem( id: string, name: string, overrides: Partial = {}, ): InventoryItem { return { id, name, description: `${name} 的测试描述`, quantity: 1, category: 'misc', rarity: 'common', tags: [], value: 1, ...overrides, }; } function createModalState(overrides: Partial = {}): 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'); }); });