Files
2026Technology-Competition/docs/superpowers/specs/2026-08-09-error-code-consistency-test-design.md
T

102 lines
5.3 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.
# 错误码一致性测试设计(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.` 开头**的 4 条 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 文件定位(沿用项目先例)
```python
API_DOC = Path(__file__).resolve().parents[1] / "docs/api-design.md"
```
(与 `tests/test_real_samples.py:7``SAMPLES` 定位方式一致;不依赖 CWD,pytest 任意目录启动可跑。)
### 3.3 解析函数(内联纯函数)
```python
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 行数 == 4(防止表格结构变化静默失效;新增/删除 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. 验收标准
1. `pytest tests/test_api_design_consistency.py -v` 全绿(4 passed
2. 全量回归 `python -m pytest -q` ≥ 132 passed / 100.00% 覆盖
3. 手工破坏验证(可选):临时删除文档某 LLM 行 → 用例 1 变红且差异清单可读;恢复正常
4. 不修改任何现有源文件或测试文件
## 7. 影响面
- 新增 1 个测试文件(约 60-80 行)
- 修改 1 个文件:`_AI_USAGE_LOG.md`(追加日志行)
- 提交消息:`test: api §7 与异常树错误码一致性门禁测试(防文档漂移)`
## 8. 不做的事(YAGNI 最终确认)
- ❌ 非 LLM 行验证
- ❌ src 生产辅助模块
- ❌ Enum 化错误码(spec 已明确 str
- ❌ 其它文档表一致性检查