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

8.6 KiB
Raw Blame History

错误码一致性测试实施计划(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.pyLLMError.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):

"""错误码一致性门禁: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: FAILImportError: 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: 提交
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 与异常树错误码一致性门禁测试(防文档漂移)