5.3 KiB
5.3 KiB
错误码一致性测试设计(api-design §7 ↔ exceptions.py)
状态: 已批准(brainstorming + DX 审查吸收) 日期: 2026-08-09 里程碑后备路径: LLM 错误码对齐(error_code)收尾
1. 背景与目标
「LLM 错误码对齐」完成后,api-design.md §7 错误码表与 src/genesis/inference/exceptions.py 的异常树通过 error_code 类属性建立了一一对应关系(LLM_TIMEOUT ↔ LLMTimeoutError 等),但该对应关系目前仅靠人工核对维护:
test_inference_errors.py断言了异常类自身的error_code值(代码自证代码,防不了文档漂移)- 文档添加/删除错误码行、或代码新增异常类时,没有测试会在两边失配时变红
目标: 将「api §7 = 唯一事实源」的核对从人工升级为机器验证——新增回归测试解析 docs/api-design.md §7 表格,断言与异常树双向一致,防文档↔代码漂移。
2. 范围与不做的事
范围:
- 仅验证 §7 表格中来源列以
exceptions.开头的 5 条 LLM 错误码行(LLM_TIMEOUT / LLM_NETWORK_ERROR / LLM_NOT_CONFIGURED / LLM_PARSE_ERROR) - 双向验证:异常树 → 文档、文档 → 异常树
不做(YAGNI):
- 不验证非 LLM 错误码行(FILE_TYPE_INVALID、EMBEDDING_FAILED、INTERNAL_ERROR 等来源列为「—」,暂无代码映射)
- 不抽 src 生产模块(测试自包含,零生产代码改动)
- 不做 web-ui §8 端点表等其它文档一致性检查(有第二个需求再提炼共享模块)
3. 设计
3.1 落点
新建独立测试文件 tests/test_api_design_consistency.py(单一职责:文档 ↔ 代码一致性门禁),不污染现有 test_inference_errors.py。
3.2 文件定位(沿用项目先例)
API_DOC = Path(__file__).resolve().parents[1] / "docs/api-design.md"
(与 tests/test_real_samples.py:7 的 SAMPLES 定位方式一致;不依赖 CWD,pytest 任意目录启动可跑。)
3.3 解析函数(内联纯函数)
def _extract_llm_error_rows(document: str) -> dict[str, str]:
"""解析 §7 表格中来源列以 exceptions. 开头的行,返回 {错误码: 来源类名}。"""
- 实现:逐行匹配表格行
| \CODE` | ... | exceptions.Xxx |`,提取错误码与来源类名 - 用途:返回 LLM 错误码 → 来源异常类名 的映射;非 LLM 行(来源 "—")被天然排除
3.4 测试用例(4 个)
| # | 用例 | 验证方向 | 断言要点 |
|---|---|---|---|
| 1 | test_error_doc_lists_every_llm_error_code |
异常树 → 文档 | 遍历 LLMError.__subclasses__(),每个子类 error_code(非 None)必须出现在文档映射中,且来源类名 == 子类名 |
| 2 | test_error_doc_llm_rows_resolve_to_exception |
文档 → 异常树 | 文档映射中每个错误码都能在 LLMError.__subclasses__() 中找到 error_code 相同、类名匹配的唯一类 |
| 3 | test_error_doc_stable_llm_row_count |
格式护栏 | 文档映射中 LLM 行数 == 5(防止表格结构变化静默失效;新增/删除 LLM 行需显式更新此数) |
| 4 | test_error_doc_parse_guard_prevents_silent_noop |
格式护栏 | 若解析结果为空(表格格式被改动导致正则失配),立即硬失败而非静默通过 |
3.5 失败可诊断(DX 修正 F1/F2)
- 测试 1/2 断言失败时,输出双向差异清单:文档有而代码无的码、代码有而文档无的码,并附引导语「请同步 api-design.md §7 或 exceptions.py」
- 通过 pytest 的断言消息(
assert ... , "差异说明")实现,避免裸assert 'LLM_QUOTA' in dict的模糊失败
3.6 契约可读性(DX 修正 F4)
- 文件头部模块 docstring 写明:「此测试为 api-design §7 ↔ exceptions.py 的机器契约,变更错误码必须两处同步修改」
4. 错误处理
| 场景 | 行为 |
|---|---|
| 文档缺失 | read_text() 抛 FileNotFoundError → 测试失败(显式暴露) |
| 表格行解析失配(0 行) | 用例 4 硬失败,杜绝静默失效 |
| 新增异常未登记 / 文档多行无单码 | 用例 1/2 分别双硬失败并列出差异 |
| LLM 行数变化 | 用例 3 失败,提示显式更新计数 |
5. 测试策略与覆盖
- 本功能自身即测试;不新增生产代码,
fail_under=99红线不受触碰(其按src/生效,tests/ 命中不计) - 全量回归保持 ≥ 128 passed(新增 4 用例后 ≥ 132)
- 验证命令:
python -m pytest tests/test_api_design_consistency.py -v(聚焦);python -m pytest -q(全量)
6. 验收标准
pytest tests/test_api_design_consistency.py -v全绿(4 passed)- 全量回归
python -m pytest -q≥ 132 passed / 100.00% 覆盖 - 手工破坏验证(可选):临时删除文档某 LLM 行 → 用例 1 变红且差异清单可读;恢复正常
- 不修改任何现有源文件或测试文件
7. 影响面
- 新增 1 个测试文件(约 60-80 行)
- 修改 1 个文件:
_AI_USAGE_LOG.md(追加日志行) - 提交消息:
test: api §7 与异常树错误码一致性门禁测试(防文档漂移)
8. 不做的事(YAGNI 最终确认)
- ❌ 非 LLM 行验证
- ❌ src 生产辅助模块
- ❌ Enum 化错误码(spec 已明确 str)
- ❌ 其它文档表一致性检查