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

5.5 KiB
Raw Permalink Blame History

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. 本次执行结果

本轮实际执行了以下测试:

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 列表中。
  • 直接原因:
    • matchesCategoryrecovery 分类只判断了是否处于战斗态,没有像 battle / escape 分类那样额外校验 hasAliveMonsters(context.monsters)
  • 修复方式:
    • 已在 matchesCategoryrecovery 分支中,为战斗恢复类 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
    • 如果玩家背包第一项数量为 0modal 默认会选中一件不可出售物品。
  • 直接原因:
    • 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 稳定复现出来,避免问题只停留在口头描述。
  • 第二步在修复后把断言翻转成“正确行为”,让它们正式成为回归测试。