Parcourir la source

docs: 设计 Python Gateway 100% 覆盖率方案

jackson il y a 1 semaine
Parent
commit
e201c63ceb

+ 111 - 0
docs/superpowers/specs/2026-07-14-mcp-python-coverage-100-design.md

@@ -0,0 +1,111 @@
+# MCP Python Gateway 100% 覆盖率设计
+
+## 背景
+
+Python Gateway 当前执行:
+
+```powershell
+python -m coverage run -m unittest discover -s tests -p "test_*.py"
+python -m coverage report -m
+```
+
+全量 `297` 个测试通过,但 `.coveragerc` 启用了语句和分支覆盖,当前 TOTAL 仅为
+`93.63%`。报告包含 1153 条语句和 418 条分支,其中缺少 52 条语句、48 条分支,
+共有 9 个生产文件未达到 100%。
+
+## 目标
+
+- 保持现有 `.coveragerc` 的 `source`、`omit`、`branch=True` 和报告口径不变。
+- 所有纳入统计的非空生产文件逐个达到 `100.00%`。
+- TOTAL 达到 `100.00%`,并通过 `coverage report --fail-under=100`。
+- 全量 unittest 保持通过,测试不依赖真实 Redis、外部 HTTP 服务或长期运行线程。
+- 不覆盖、不回退当前工作区已有的单号元数据和测试改动。
+
+## 不采用的方案
+
+### 扩大 omit 或添加 pragma
+
+不通过 `.coveragerc omit`、`exclude_lines` 或 `# pragma: no cover` 隐藏当前生产代码
+缺口。这会改变统计边界,无法证明现有代码路径经过测试。
+
+### 为覆盖率改写业务逻辑
+
+当前函数级报告未发现必须重构才能触达的代码。默认只修改测试;若实施中证明某条
+分支结构上不可达,应暂停并重新评审,而不是直接修改生产逻辑或排除该分支。
+
+## 实施结构
+
+### 第一批:叶子模块和数据边界
+
+| 生产文件 | 测试文件 | 覆盖重点 |
+|----------|----------|----------|
+| `tools/query_order_exact.py` | `tests/test_query_order_exact_tool.py` | 非法 scalar ID、逗号字符串 ID 列表、字符串列表去重回边 |
+| `services/token_store.py` | `tests/test_token_store_coverage.py` | 无目录文件、延迟读取、已删除文件清理、bytes 请求、非法 Redis 行 |
+| `services/gateway_session_store.py` | `tests/test_gateway_session_store_unit.py` | 不存在 session 的 touch |
+| `config.py` | `tests/test_config_compat.py` | timeout 环境变量优先级、无效 dotenv 行、带引号值和循环回边 |
+
+这一批不改生产代码,只补可观察的输入、返回值、异常和依赖调用断言。
+
+### 第二批:Gateway 应用和 CLI
+
+| 生产文件 | 测试文件 | 覆盖重点 |
+|----------|----------|----------|
+| `app.py` | `tests/test_app_coverage.py` | 注册工具名称、动态列表错误、request ID、缺少刷新客户端、public 启动、CLI 必填/可选参数及最终防御分支 |
+
+`serve-public` 使用 mock 替换 Redis client、session store、public app 和 server 入口;
+CLI 防御分支通过模拟参数解析结果触发,不启动真实服务。
+
+### 第三批:协议与公网服务
+
+| 生产文件 | 测试文件 | 覆盖重点 |
+|----------|----------|----------|
+| `mcp_protocol.py` | `tests/test_mcp_protocol_coverage.py` | 无 flush 输出、无 input schema、工具响应数据形态、错误 data/meta、空轨迹 tips |
+| `public_gateway.py` | `tests/test_public_gateway_unit.py` | 动态列表无效响应、不支持 touch_session 的 store |
+| `public_server.py` | `tests/test_public_server_coverage.py` | input schema 归一化、启用限流、清理线程、KeyboardInterrupt 关闭 |
+
+HTTP server、线程和无限清理循环都使用 mock 控制。清理循环测试捕获
+`threading.Thread` 的 target,并让受控 `time.sleep` 抛出测试专用异常终止循环,不做
+真实等待。
+
+## 测试质量要求
+
+- 测试名称描述具体行为,不以“覆盖第 N 行”作为唯一目的。
+- 每个异常分支同时断言异常类型和关键消息。
+- 每个成功分支断言返回值或依赖调用参数,不能只调用代码而不验证结果。
+- mock 仅用于 Redis、socket、HTTP server、线程、时钟和 CLI 解析等外部边界。
+- 不添加只为测试存在的生产 API。
+- 每批完成后运行目标测试,再重新执行全量 coverage,依据新的 Missing 列继续收敛。
+
+## 验证流程
+
+每批执行:
+
+```powershell
+python -m unittest <本批目标测试模块>
+python -m coverage run -m unittest discover -s tests -p "test_*.py"
+python -m coverage report -m
+```
+
+最终验收:
+
+```powershell
+python -m coverage run -m unittest discover -s tests -p "test_*.py"
+python -m coverage report -m --fail-under=100
+git diff --check
+```
+
+验收标准:
+
+1. 全量 unittest 返回 0。
+2. 报告中每个非空生产文件为 `100.00%`。
+3. TOTAL 为 `100.00%`,Missing 为空。
+4. `.coveragerc` 的统计范围和分支覆盖设置未被放宽。
+5. 工作区原有单号工具改动保持完整。
+
+## 风险与处理
+
+- 覆盖率 100% 不等于业务逻辑绝对正确;测试仍以可观察行为和关键错误边界为主。
+- 线程/服务器测试若泄漏真实后台线程会导致套件不稳定,因此必须 mock `Thread.start`
+  或捕获 target 后同步执行。
+- 分支覆盖可能在新增测试后暴露新的回边提示;以最新 `coverage report -m` 为准逐项
+  处理,不通过删除有效断言或放宽配置收尾。