# 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` 为准逐项 处理,不通过删除有效断言或放宽配置收尾。