跳转至

测试 fixtures

tests/fixtures/ 存放测试套件的确定性输入数据:代表性线程的合成原始 API 抓取raw_entries.json / raw_blocks.json / thread.json),外加作为 golden 快照随仓库提交的渲染产物(conversation.md + turns/)。任何渲染层回归都会破坏与 golden 的逐字节比对。

什么是 fixture

每个 fixture 是一个目录,含原始抓取外加一棵 golden/ 树:

路径 作用
raw_entries.json 原始 API 抓取:线程的 entries
raw_blocks.json 原始 API 抓取:workflow blocks(该模式无 workflow 时缺失,如 search_demostudy_demo
thread.json 归档的线程元数据
golden/conversation.md + golden/turns/turn_*.md 随仓库提交的渲染产物,逐字节比对

所有 fixtures 均为合成且确定性的数据:

  • 账户身份——用户名 / 显示名 / 邮箱 / user_id 均为共享占位符(alice / bobAlice Example / Bob Examplealice@example.com / bob@example.com00000000-0000-4000-8000-0000000000aa / 00000000-0000-4000-8000-0000000000bb);BOT 空间为 00000000-0000-4000-8000-0000000000b0 / bot-EXAMPLEread_write_token 是固定占位符 00000000-0000-4000-8000-0000000000ff
  • 标识符映射——thread / entry / asset 的 UUID 为 uuid5 派生(固定 namespace + 原值)的合成值,带 5cbeef00 前缀标记,全仓库一致:相同输入恒映射到相同输出,保留交叉引用。toolu_ 运行 id 同样变为带 5crub0 标记的定长合成 id。
  • 用户文本——查询 / 标题 / 空间名 / 问答负载 / 文件句柄路径均为通用合成文本;CloudFront/S3 签名 URL 的签名查询串已剥离。

以下通用占位字符串是测试语义标记,属有意保留:已推送All doneAdd analysis tool shell wrapper这是子代理的最终结论段落Answer skipped.

命名

fixture 目录只使用描述性名称——标识模式或缺陷场景,绝不携带任何线程标识符:

  • 完整线程 fixtures:<mode>_demo(如 search_democouncil_demo)。
  • 裁剪缺陷场景:scenario_<defect-scenario>

清单

完整线程快照(golden = 渲染产物,逐字节)

fixture 覆盖
search_demo search,单轮;R 代码围栏 / 行内代码
deep_research_demo deep-research;raw 含 \(,golden 含 26× $$ —— 端到端数学定界符转换
computer_demo computer,多轮(7 轮)workflow 渲染
council_demo council 模型委员会渲染(raw 抓取约 1.0 MB)
study_demo study(通用学习问题)

裁剪场景 fixtures(单 entry 裁剪;golden = 对应 turn 文件,turn 重新编号)

特定缺陷场景裁剪为单个 entry(保留 thread_metadata,清空 background_entries——已核实被裁 entry 的 workflow block 均不携带 sub-agent 步骤依赖,因此裁剪不改变渲染路径)。

fixture 覆盖
scenario_computer_answer_fallback computer answer 回退:当普通 FINAL 路径被守卫跳过(仅剩 workflow_snapshots)时,从模式化 workflow_block 的 variant=answer TEXT 中还原 answer(parsers.wf_block_answer
scenario_subagent_fallback sub-agent 回退:无 background 匹配时,渲染 sub-agent 标题 + 该步骤自身的条目(sub-agent 摘要不会丢失)
scenario_user_response WORKFLOW_ITEM_USER_RESPONSE 问答渲染(4 问 / 4 答);query 为空的 turn
scenario_subagent_stub subagent_result 残桩 turn:无锚点 background sub-agent 的 10 秒时间窗关联
scenario_workflow_item_nested 嵌套 WORKFLOW_ITEM_WORKFLOW 折叠块渲染

中断 / 取消场景(多 entry + background_entries 裁剪,关系保持完整)

中断语义依赖 entry ↔ background 的归属关系,因此不能清空 background_entries;裁剪采用 uuid 子集(锚定 / 残桩时间窗 / 附录三类载荷各至少保留一个)。

fixture 覆盖
scenario_limit_interrupted 额度上限中断(locked_reason=spending_limit_exceeded + AWAITING):workflow 头部 / sub-agent 标题 / 残桩 turn 摘要上的 ⏸ 标注;归属瀑布第 ③ 级 —— 未消费的 38 步载荷落入 conversation.md 附录;锚定 / 残桩时间窗载荷不进入附录,绝不重复渲染
scenario_canceled WORKFLOW_CANCELED:workflow 头部 ⛔ 标注

维护 fixtures

tests/scrub_fixtures.py 是确定性维护工具(tests/scrub_fixtures.py:529):位置式文本替换、标识符重映射与 golden 快照重生成。幂等,可安全重复运行。

uv run python tests/scrub_fixtures.py            # 应用替换并重生成 golden
uv run python tests/scrub_fixtures.py --check    # 只跑门禁,不写文件
  • golden 重生成tests/scrub_fixtures.py:500-524)——每个 fixture 的原始 JSON 在临时目录中经与生产及测试套件相同的离线 rerender 路径重渲;golden/ 逐字节一致地刷新,轮数不一致会直接报错中止。
  • 替换来源位于仓库之外——账户身份值运行时读自用户级配置(~/.config/pplx-export/config.toml,经 pplx_export.config;见配置);内容类替换对读自本地覆盖映射 tests/scrub_pairs.local.json——不入库;随仓库提交的模板是 tests/scrub_pairs.example.jsonpairs:按序应用的 [旧值, 新值] 条目;gate:零容忍子串)。
  • 确定性重映射——其余 UUID → 带 5cbeef00 标记的 uuid5 合成值,toolu_ id → 带 5crub0 标记的定长合成 id,read_write_token → 固定占位值;已带标记前缀的值跳过(幂等)。作用域:位置式对只作用于所属 fixture 目录;本地内容对与身份 / token / UUID / toolu_ 映射全局生效。
  • --check 门禁模式——扫描 tests/fixtures/ 下每个文件,查找全部替换来源、门禁子串以及本地绝对路径 / 签名 URL 的残留,命中即以非零码退出,且不写任何文件。重复运行时,新值已存在的映射视为已应用。

改了 fixture 的原始 JSON 或渲染器之后运行它;提交 fixture 变更前运行 --check。在测试套件内部,tests/conftest.py::render_fixture 使用同一条离线重渲路径,因此快照测试始终用渲染器的最新输出对比已提交的 golden。

另见