import { describe, expect, it } from 'vitest'; import { AnimationState, type GameState } from '../../types'; import { applyCompanionReactionToStance, buildCompanionReactionBatch, } from './companionReactionDirector'; function createState(): GameState { return { worldType: null, customWorldProfile: null, playerCharacter: null, runtimeStats: { playTimeMs: 0, lastPlayTickAt: null, hostileNpcsDefeated: 0, questsAccepted: 0, itemsUsed: 0, scenesTraveled: 0, }, currentScene: 'Story', storyHistory: [], storyEngineMemory: { discoveredFactIds: [], inferredFactIds: [], activeThreadIds: ['thread-1'], resolvedScarIds: [], recentCarrierIds: [], recentSignalIds: [], recentCompanionReactions: [], }, characterChats: {}, animationState: AnimationState.IDLE, currentEncounter: null, npcInteractionActive: false, currentScenePreset: null, sceneHostileNpcs: [], playerX: 0, playerOffsetY: 0, playerFacing: 'right', playerActionMode: 'idle', scrollWorld: false, inBattle: false, playerHp: 0, playerMaxHp: 0, playerMana: 0, playerMaxMana: 0, playerSkillCooldowns: {}, activeBuildBuffs: [], activeCombatEffects: [], playerCurrency: 0, playerInventory: [], playerEquipment: { weapon: null, armor: null, relic: null, }, npcStates: { 'npc-companion': { affinity: 30, helpUsed: false, chattedCount: 0, giftsGiven: 0, inventory: [], recruited: true, revealedFacts: [], knownAttributeRumors: [], firstMeaningfulContactResolved: true, seenBackstoryChapterIds: [], stanceProfile: { trust: 52, warmth: 48, ideologicalFit: 50, fearOrGuard: 30, loyalty: 40, currentConflictTag: null, recentApprovals: [], recentDisapprovals: [], }, }, }, quests: [], roster: [], companions: [ { npcId: 'npc-companion', characterId: 'archer-hero', joinedAtAffinity: 30, hp: 10, maxHp: 10, mana: 10, maxMana: 10, skillCooldowns: {}, }, ], currentBattleNpcId: null, currentNpcBattleMode: null, currentNpcBattleOutcome: null, sparReturnEncounter: null, sparPlayerHpBefore: null, sparPlayerMaxHpBefore: null, sparStoryHistoryBefore: null, }; } describe('companionReactionDirector', () => { it('builds reactions and writes them back to stance', () => { const state = createState(); const reactions = buildCompanionReactionBatch({ state, signals: [ { id: 'signal-1', signalType: 'accept_contract', threadIds: ['thread-1'], }, ], actionText: '接下调查断桥旧案的委托', }); const nextState = applyCompanionReactionToStance({ state, reactions, }); expect(reactions[0]?.reactionType).toBe('approve'); expect(nextState.npcStates['npc-companion']?.stanceProfile?.trust).toBeGreaterThan( state.npcStates['npc-companion']?.stanceProfile?.trust ?? 0, ); }); });