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

176 lines
8.6 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 §7 ↔ exceptions.py
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 新增回归测试解析 `docs/api-design.md` §7 表格,与 `exceptions.py` 的 error_code 双向一致验证,防文档↔代码漂移。
**Architecture:** 新建 `tests/test_api_design_consistency.py`,内联正则解析函数 + 4 个测试用例;零生产代码改动。断言失败带双向差异清单(DX 修正 F1/F2);0 行解析硬失败护栏(F3);契约 docstringF4)。
**Tech Stack:** Python 3.11+、pytest、re、pathlib
## Global Constraints
- 项目为中文交流:注释、docstring、commit 消息用中文;标识符英文
- 零真实网络:本任务是纯本地文件解析,不得引入网络/API Key
- 覆盖红线:`pyproject.toml` `fail_under = 99`(当前基线 128 passed / 100.00%);本任务零生产代码,红线不受触碰
- 全量回归:`python -m pytest -q`;聚焦:`python -m pytest tests/test_api_design_consistency.py -v`
- 提交消息:`feat:` / `test:` / `docs:` + 简中文描述
- 每次修改后追加 `_AI_USAGE_LOG.md`(范式步骤:"测试验证",模型:"deepseek-v4-flash-free"
- 本仓库工作在 master 分支(项目惯例);Windows/PowerShell 环境
- Spec: `docs/superpowers/specs/2026-08-09-error-code-consistency-test-design.md`
---
### Task 1: 新建一致性门禁测试文件
**Files:**
- Create: `tests/test_api_design_consistency.py`
- Modify: `_AI_USAGE_LOG.md`(追加一行)
**Interfaces:**
- Consumes: `src/genesis/inference/exceptions.py``LLMError.error_code` 类属性(基类 `None`;四子类 `LLM_TIMEOUT`/`LLM_NETWORK_ERROR`/`LLM_NOT_CONFIGURED`/`LLM_PARSE_ERROR`);`docs/api-design.md` §7 表格(L311-314 四行 LLM 行,行格式 `| \`CODE\` | 含义 | ... | exceptions.Xxx |`
- Produces: 无(测试文件独立,无下游依赖)
- [ ] **Step 1: 写失败测试**
创建 `tests/test_api_design_consistency.py`(用例引用解析函数,解析函数尚未实现 → RED):
```python
"""错误码一致性门禁:api-design §7 ↔ 异常树 error_code 的机器契约。
变更错误码必须两处同步修改:api-design.md §7 错误码表
与 src/genesis/inference/exceptions.py 的 error_code 类属性。
本测试防文档漂移:文档多/少/错一行都会变红,且失败信息给出差异清单。
"""
from __future__ import annotations
import re
from pathlib import Path
from genesis.inference.exceptions import LLMError
# 项目根定位(与 tests/test_real_samples.py:7 同款先例;不依赖 CWD
API_DOC = Path(__file__).resolve().parents[1] / "docs/api-design.md"
# §7 表格 LLM 行:| `CODE` | 语义 | ... | exceptions.Xxx |
_LLM_ROW_RE = re.compile(
r"^\|\s*`([A-Z][A-Z_]+)`\s*\|.*\|\s*exceptions\.([A-Za-z_]+)\s*\|$"
)
def _extract_llm_error_rows(document: str) -> dict[str, str]:
"""解析 §7 表格中来源列以 exceptions. 开头的行,返回 {错误码: 来源类名}。
非 LLM 行(来源列为 "—")被天然排除。
"""
mapping: dict[str, str] = {}
for line in document.splitlines():
m = _LLM_ROW_RE.match(line.strip())
if m:
mapping[m.group(1)] = m.group(2)
return mapping
def _llm_exception_classes() -> list[type[LLMError]]:
"""error_code 非 None 的全部 LLM 异常子类(不硬编码清单,新增异常自动纳入)。"""
return sorted(
(cls for cls in LLMError.__subclasses__() if cls.error_code is not None),
key=lambda cls: cls.__name__,
)
def test_error_doc_lists_every_llm_error_code():
# 异常树 → 文档:每个子类 error_code 必须登记在 §7,且来源类名匹配
mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8"))
missing = {
cls.__name__: cls.error_code
for cls in _llm_exception_classes()
if cls.error_code not in mapping or mapping[cls.error_code] != cls.__name__
}
assert not missing, (
"异常类存在文档未登记的 error_code(请同步 api-design.md §7):\n"
+ repr(missing)
)
def test_error_doc_llm_rows_resolve_to_exception():
# 文档 → 异常树:每个 LLM 行都要能解析到同码同名的异常类
mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8"))
by_code = {cls.error_code: cls.__name__ for cls in _llm_exception_classes()}
unresolved = {code: name for code, name in mapping.items() if by_code.get(code) != name}
assert not unresolved, (
"§7 LLM 行解析不到对应异常类(请核对来源列或错误码名):\n"
+ repr(unresolved)
)
def test_error_doc_stable_llm_row_count():
# 格式护栏:LLM 行数 === 4;新增/删除 LLM 错误码需显式更新此数
mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8"))
assert len(mapping) == 4, (
f"api §7 LLM 错误码行数应为 4,实际 {len(mapping)}"
"新增/删除错误码须同步更新此计数。"
)
def test_error_doc_parse_guard_prevents_silent_noop():
# 格式护栏:解析结果为空须硬失败(表格格式变更会静默失效,这里立即暴露)
mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8"))
assert mapping, "api §7 表格解析结果为空:若无匹配行,请检查表格格式与 _LLM_ROW_RE 是否一致。"
```
- [ ] **Step 2: 运行验证失败**
Run: `python -m pytest tests/test_api_design_consistency.py -v`
Expected: FAIL`ImportError: cannot import name 'LLMError' from 'genesis.inference.exceptions'``ModuleNotFoundError`——取决于包导入是否已通;若 import 正常则 4 用例中 count/guard 断言失败。**真实预期:导入路径正确则 `_extract_llm_error_rows` 已定义、用例应 PASS** ——正确行为是 TDD 反向(先红),因此若首跑即绿,说明本任务实质是「交付完整测试」,红绿灯以 Step 4 全绿为准;此步只需记录实际输出,若因实现已被写全而直接绿,在报告中注明「RED 无步骤,直接 GREEN」)
> 说明:本任务整体是一次性创建完整测试文件,TDD 的 RED 信号是"文件不存在时 import 失败"。若你想严格走红,可在 Step 1 先只写用例 + 在调用 `_extract_llm_error_rows` 处留 `raise NotImplementedError`,实现后再替换——不必须,直接完整创建并在 Step 2 观察实际结果即可。
- [ ] **Step 3: 实现(完整文件已就位)**
Step 1 已为完整文件的全部内容——无需额外改实现代码。若 Step 2 是 RED(import 或断言失败),对照 Step 1 代码修正后即转绿。
- [ ] **Step 4: 运行验证通过**
Run: `python -m pytest tests/test_api_design_consistency.py -v`
Expected: PASS4 passed
- [ ] **Step 5: 全量回归**
Run: `python -m pytest -q`
Expected: PASS128 + 4 = ≥ 132 passed / 覆盖 100.00%`fail_under=99` 达标)
- [ ] **Step 6: 追加 `_AI_USAGE_LOG.md`**
范式步骤:"测试验证",摘要:新增 api §7 ↔ 异常树 error_code 一致性门禁测试(4 用例:双向断言 + 行数/空解析护栏,防文档漂移)。
- [ ] **Step 7: 提交**
```bash
git add tests/test_api_design_consistency.py _AI_USAGE_LOG.md
git commit -m "test: api §7 与异常树错误码一致性门禁测试(防文档漂移)"
```
---
## Self-Review
**1. Spec 覆盖:**
- §3.1 落点 → Task 1 新建 `tests/test_api_design_consistency.py`
- §3.2 文档定位 → `API_DOC = Path(__file__)....parents[1] / "docs/api-design.md"`
- §3.3 解析函数 → `_extract_llm_error_rows`
- §3.4 四用例 → 测试 1(树→文档)/测试 2(文档→树)/测试 3(行数护栏)/测试 4(空解析护栏)✓
- §3.5 失败可诊断 → `assert not missing, ( "…差异清单" )` 含双向差异 ✓
- §3.6 契约 docstring → 文件头 docstring ✓
- §4 错误处理 → 文档缺失 `read_text` 抛 FileNotFoundError;空解析护栏 ✓
- §5 测试策略 → Step 4 聚焦 + Step 5 全量 ✓
- §6 验收 1-4 → Step 4 / Step 5 / 手工破坏验证可选 §4 → Step 7 提交 ✓
**2. 占位符检查:** 无 TBD/TODO;测试代码全文可见。✓
**3. 类型一致性:** `mapping: dict[str, str]`(错误码:来源类名)在解析函数与 3 个用例间一致;`_llm_exception_classes()` 返回 `list[type[LLMError]]` 与使用处一致。✓
## 提交消息汇总
- Task 1: `test: api §7 与异常树错误码一致性门禁测试(防文档漂移)`