# Function 测试审计(2026-04-14) 补充更新: - 本文记录的 2 个 bug 已在同日完成代码修复。 - 对应测试已经从“稳定复现旧行为”切换为“验证修复后行为”。 ## 1. 本次新增测试 本轮新增了两组 function 相关测试: - `src/data/stateFunctions.test.ts` - 覆盖 state function 的运行时过滤、优先级、选项解析、排序逻辑。 - `src/data/functionCatalog/functionCatalog.test.ts` - 覆盖 function 文档映射、flow helper、NPC helper modal 初始化逻辑。 这两组测试都直接挂在现有 `vitest` 体系里,没有新建独立测试框架。 ## 2. 本次执行结果 本轮实际执行了以下测试: ```bash npx vitest run src/data/stateFunctions.test.ts src/data/functionCatalog/functionCatalog.test.ts npx vitest run src/data/npcInteractions.test.ts src/hooks/story/storyGenerationState.test.ts src/services/runtimeStoryService.test.ts ``` 执行结果: - 新增测试:`2` 个文件,`12` 条测试,全部通过。 - 复跑已有 function 相关测试:`3` 个文件,`17` 条测试,全部通过。 - 修复回归测试:`5` 个文件,`30` 条测试,全部通过。 - 编码检查:`1516` 个文件全部通过。 说明: - 本轮不是“测试全绿就代表没有问题”。 - 最初定位出的 2 个问题,已经在后续修复回合里转成了回归测试。 ## 3. 历史 bug 与修复结果 ### 3.1 `battle_recover_breath` - 所在位置:`src/data/stateFunctions.ts` - 状态:已修复 - 原始问题表现: - 当 `inBattle = true`,但当前没有存活敌人时,`battle_recover_breath` 仍会留在可执行 function 列表中。 - 直接原因: - `matchesCategory` 对 `recovery` 分类只判断了是否处于战斗态,没有像 `battle` / `escape` 分类那样额外校验 `hasAliveMonsters(context.monsters)`。 - 修复方式: - 已在 `matchesCategory` 的 `recovery` 分支中,为战斗恢复类 function 补上 `hasAliveMonsters(context.monsters)` 判断。 - 修复前影响: - 在“敌人已死但战斗态尚未清理干净”的边界帧里,界面仍可能出现战斗恢复类选项。 - 这会让 function 池和真实战斗状态产生残留错位。 - 当前验证方式: - `inBattle = true` - `monsters = [{ hp: 0, ... }]` - 调用 `getExecutableFunctions(context)` - 现在返回结果应为空,不再包含 `battle_recover_breath` - 对应用例: - `src/data/stateFunctions.test.ts` - 用例名:`removes battle_recover_breath when combat has no living monsters` ### 3.2 `npc_trade` - 所在位置:`src/data/functionCatalog/npc/npcTrade.ts` - 状态:已修复 - 原始问题表现: - trade modal 初始化时,`selectedPlayerItemId` 直接取 `state.playerInventory[0]?.id`。 - 如果玩家背包第一项数量为 `0`,modal 默认会选中一件不可出售物品。 - 直接原因: - `buildNpcTradeModalState` 没有过滤 `quantity <= 0` 的物品,也没有寻找第一个可交易物品。 - 修复方式: - 已改为优先选择 `quantity > 0` 的可交易物品。 - 同时对 NPC 库存和玩家背包都使用同一条筛选规则,避免默认选中空物品。 - 修复前影响: - 交易面板第一次打开时,默认状态可能就是不可确认的。 - 用户需要手动切换到第二件物品,才会进入可提交状态。 - 当前验证方式: - `playerInventory[0].quantity = 0` - `playerInventory[1].quantity > 0` - 调用 `buildNpcTradeModalState(...)` - 现在 `selectedPlayerItemId` 应该自动落到第一件可交易物品 - 对应用例: - `src/data/functionCatalog/functionCatalog.test.ts` - 用例名:`prefers the first tradable player item when zero-quantity items exist` - `src/hooks/story/storyGenerationState.test.ts` - 用例名:`skips zero-quantity player items when opening the trade modal` ## 4. 本轮已验证通过的 function 能力 以下内容本轮已通过测试验证,没有发现新的明显问题: - state function runtime 构建: - `idle_follow_clue` 已正确从运行时候选池移除。 - `idle_explore_forward` 的运行时文案覆盖仍然生效。 - state function 选项行为: - 高压战斗下 `battle_recover_breath` 会被正确提权。 - 营地场景会正确隐藏 `idle_explore_forward`。 - `idle_travel_next_scene` 会强制使用运行时建议 actionText。 - `battle_all_in_crush` 会保留外部传入的自定义 actionText。 - story option 排序仍保持“前 2 个 model 锁定 + 后续按 priority 排序”。 - flow helper: - `story_continue_adventure` - `camp_travel_home_scene` - NPC helper: - `npc_preview_talk` - `npc_gift` - `npc_recruit` - 文档映射: - 当前 `SERVER_RUNTIME_FUNCTION_IDS` 全部都能在 function catalog 文档中找到对应条目。 - 本轮扫描到的 function `source` 路径全部存在,没有出现失效引用。 ## 5. 本轮修复动作 - `battle_recover_breath` - 已补充战斗恢复类 function 的存活敌人校验,避免战斗边界帧残留非法选项。 - `npc_trade` - 已把 trade modal 默认选中逻辑改为优先寻找可交易物品,不再直接吃数组第一项。 - 回归测试 - 原先记录旧行为的测试已翻转为修复后预期,并额外补了一条 `storyGenerationState` 接入层测试。 ## 6. 备注 这次测试资产的意义分两步: - 第一步先把 bug 稳定复现出来,避免问题只停留在口头描述。 - 第二步在修复后把断言翻转成“正确行为”,让它们正式成为回归测试。