Files
Genarrative/docs/audits/FUNCTION_TEST_AUDIT_2026-04-14.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

129 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 稳定复现出来,避免问题只停留在口头描述。
- 第二步在修复后把断言翻转成“正确行为”,让它们正式成为回归测试。