smoke 与 compat 两个 e2e 文件零兜底:网关抖动被记成库的缺陷,真问题淹没在假红里 #25

Closed
opened 2026-09-09 10:44:49 +08:00 by iomgaa · 1 comment
Owner

一句话

tests/e2e/test_smoke_gateway.py(4 条)与 tests/e2e/test_compat_projects.py(4 条)在网关不可用时一律 FAIL 而非 SKIP,把渠道抖动记成库的缺陷。同目录的 test_embed_probe.py 已有正确写法可直接照抄。

怎么发现的

2026-09-05 发布 1.3.3 后跑 pytest -m slow,12 条失败中 test_smoke_gateway.py::test_structured_model_ladder 是其一。诊断结论:网关当时在抖,重跑即 1 passed;且 git diff v1.3.2..v1.3.3 -- src/polygateway/structured/ src/polygateway/middleware/structured.py 完全为空——结构化通路两版逐字节相同,不可能是那次改动引入的。

同一批失败里 test_thinking_live.py 的 6+2+2 条已按此方向修掉(提交 758a127、c1a06f6),本 issue 是同一形态在另外两个文件里的残留。

现状(2026-09-08 实测)

文件 用例数 外部不可用时的兜底
test_smoke_gateway.py 4(test_stream_chat / test_non_stream_fast_path / test_structured_json_tier / test_structured_model_ladder) 完全没有: 全文无 except、无 pytest.skip
test_compat_projects.py 4(test_call_site_shape_runs_governed / test_structural_protocol_match / test_call_site_shape_with_cache_salt / test_flat_legacy_keys_assemble) 没有: 唯一的 pytest.skip 在第 57 行,兜的是 ImportError(GovDoc 协议不可导入),与网关可用性无关
test_embed_probe.py 1 正确: except PolyGatewayError → 把响应写进 tests/outputs/ 留证据 → pytest.skip 并在 reason 里带上证据路径

为什么值得修

CLAUDE.md §4.6 的原话:「成败取决于外部服务当下状态的测试一律标 slow……判据是『重跑一次可能就绿了』——这种测试留在提交关卡里会污染信号。」这两个文件已经标了 slow,但标记只解决了「不进日常套件」,没解决「进了发布关卡还是假红」。

代价是具体的:发布清单第 4 步要跑 pytest -m slow,假红会让人把「测试红了先怀疑网关」变成惯性,而那正是 CLAUDE.md 点名要防的失败模式——久了真 bug 也会被当成抖动重试掉。1.3.3 那次 12 条红里 11 条是外部环境,唯一那条真问题(test_l9 确定性红)恰恰被淹没在里面。

修法(与 758a127 同形)

  1. 网关不可用类异常(AllSourcesExhausted / SourceDeadError / TransientError,以及 404 model_not_found 这种「型号被渠道摘掉」)→ SKIP 并留证据,不要 FAIL。
  2. 证据不可省: test_embed_probe.py 的做法是把响应写进 tests/outputs/ 再 skip。test_thinking_live.py 修之前的失败形态是异常发生在 _record() 之前,报告里那一行凭空消失,事后无从判断当时发生了什么。
  3. 边界要守住: 只吞外部可用性异常,RequestRejectedError 这类「请求本身有问题」仍须冒泡。别为了绿而放松断言。

顺带

test_flat_legacy_keys_assemble 真正验的是平铺键名能否装配(可离线完成),却搭了一次真实网关调用。这条可以考虑拆成纯离线断言,连 skip 都不需要。

参考

  • 已修的同类: tests/e2e/test_thinking_live.py 的 _rounds_or_skip / _unreachable_verdict(提交 758a127)
  • 正确模板: tests/e2e/test_embed_probe.py:59-74
## 一句话 `tests/e2e/test_smoke_gateway.py`(4 条)与 `tests/e2e/test_compat_projects.py`(4 条)在网关不可用时一律 **FAIL 而非 SKIP**,把渠道抖动记成库的缺陷。同目录的 `test_embed_probe.py` 已有正确写法可直接照抄。 ## 怎么发现的 2026-09-05 发布 1.3.3 后跑 `pytest -m slow`,12 条失败中 `test_smoke_gateway.py::test_structured_model_ladder` 是其一。诊断结论:**网关当时在抖,重跑即 `1 passed`**;且 `git diff v1.3.2..v1.3.3 -- src/polygateway/structured/ src/polygateway/middleware/structured.py` **完全为空**——结构化通路两版逐字节相同,不可能是那次改动引入的。 同一批失败里 `test_thinking_live.py` 的 6+2+2 条已按此方向修掉(提交 `758a127`、`c1a06f6`),本 issue 是同一形态在另外两个文件里的残留。 ## 现状(2026-09-08 实测) | 文件 | 用例数 | 外部不可用时的兜底 | |---|---|---| | `test_smoke_gateway.py` | 4(`test_stream_chat` / `test_non_stream_fast_path` / `test_structured_json_tier` / `test_structured_model_ladder`) | **完全没有**: 全文无 `except`、无 `pytest.skip` | | `test_compat_projects.py` | 4(`test_call_site_shape_runs_governed` / `test_structural_protocol_match` / `test_call_site_shape_with_cache_salt` / `test_flat_legacy_keys_assemble`) | **没有**: 唯一的 `pytest.skip` 在第 57 行,兜的是 `ImportError`(GovDoc 协议不可导入),与网关可用性无关 | | `test_embed_probe.py` | 1 | **正确**: `except PolyGatewayError` → 把响应写进 `tests/outputs/` 留证据 → `pytest.skip` 并在 reason 里带上证据路径 | ## 为什么值得修 CLAUDE.md §4.6 的原话:「成败取决于外部服务当下状态的测试一律标 `slow`……判据是『重跑一次可能就绿了』——这种测试留在提交关卡里会污染信号。」这两个文件已经标了 `slow`,但**标记只解决了「不进日常套件」,没解决「进了发布关卡还是假红」**。 代价是具体的:发布清单第 4 步要跑 `pytest -m slow`,假红会让人把「测试红了先怀疑网关」变成惯性,而那正是 CLAUDE.md 点名要防的失败模式——久了真 bug 也会被当成抖动重试掉。1.3.3 那次 12 条红里 11 条是外部环境,**唯一那条真问题(`test_l9` 确定性红)恰恰被淹没在里面**。 ## 修法(与 `758a127` 同形) 1. 网关不可用类异常(`AllSourcesExhausted` / `SourceDeadError` / `TransientError`,以及 `404 model_not_found` 这种「型号被渠道摘掉」)→ **SKIP 并留证据**,不要 FAIL。 2. **证据不可省**: `test_embed_probe.py` 的做法是把响应写进 `tests/outputs/` 再 skip。`test_thinking_live.py` 修之前的失败形态是异常发生在 `_record()` 之前,报告里那一行**凭空消失**,事后无从判断当时发生了什么。 3. 边界要守住: 只吞外部可用性异常,`RequestRejectedError` 这类「请求本身有问题」仍须冒泡。别为了绿而放松断言。 ## 顺带 `test_flat_legacy_keys_assemble` 真正验的是**平铺键名能否装配**(可离线完成),却搭了一次真实网关调用。这条可以考虑拆成纯离线断言,连 skip 都不需要。 ## 参考 - 已修的同类: `tests/e2e/test_thinking_live.py` 的 `_rounds_or_skip` / `_unreachable_verdict`(提交 `758a127`) - 正确模板: `tests/e2e/test_embed_probe.py:59-74`
Author
Owner

四个 live 消费者采用默认 FAIL 的有限取证分类,不再按整类异常 skip。只有完整独立请求/响应证据、HTTP404、精确 error.type=model_not_found 等全部成立才记 UNCOVERED;429/5xx/网络错误并未自动豁免。逐轮保存、身份关联、UNKNOWN与缺轮不补分母、结构化反馈重问与取消完成数均有离线消费者回归及已复核红绿/变异。此结论替代 issue 初稿的整类 skip 建议,不声称所有渠道故障均可识别。

发布:v1.3.4,main/tag 提交 af57f93adce24b43fd10b6d8e1281ab8ee43c0a8。合并后 make lint 通过;make test:1518 passed / 23 skipped / 108 deselected,95%覆盖率;定向 M2.5/M2.7 AUTO:2 passed(各5轮,并发1);Redis时间语义:18 passed。wheel/sdist 从 registry 独立下载,散列与构建一致,解包及独立 target 安装行为、README元数据通过;Release正文、包页面正文和仓库链接已亲查。

2026-09-09 用户批准不补全模型矩阵:embedding 503仍FAIL、claude-opus-5开启命题仍FAIL,UNKNOWN/身份不足/不可达/缺轮/缺下游现行配置证据保持例外,不改成PASS,不宣称三项目已完成迁移。

Release · 包页面 · 验证与例外记录
原始本轮日志:tests/outputs/134/publish-20260909/;既有红绿与独立审查见上述验证记录。

四个 live 消费者采用默认 FAIL 的有限取证分类,不再按整类异常 skip。只有完整独立请求/响应证据、HTTP404、精确 error.type=model_not_found 等全部成立才记 UNCOVERED;429/5xx/网络错误并未自动豁免。逐轮保存、身份关联、UNKNOWN与缺轮不补分母、结构化反馈重问与取消完成数均有离线消费者回归及已复核红绿/变异。此结论替代 issue 初稿的整类 skip 建议,不声称所有渠道故障均可识别。 发布:v1.3.4,main/tag 提交 af57f93adce24b43fd10b6d8e1281ab8ee43c0a8。合并后 make lint 通过;make test:1518 passed / 23 skipped / 108 deselected,95%覆盖率;定向 M2.5/M2.7 AUTO:2 passed(各5轮,并发1);Redis时间语义:18 passed。wheel/sdist 从 registry 独立下载,散列与构建一致,解包及独立 target 安装行为、README元数据通过;Release正文、包页面正文和仓库链接已亲查。 2026-09-09 用户批准不补全模型矩阵:embedding 503仍FAIL、claude-opus-5开启命题仍FAIL,UNKNOWN/身份不足/不可达/缺轮/缺下游现行配置证据保持例外,不改成PASS,不宣称三项目已完成迁移。 [Release](https://gitea.iomgaa.online/iomgaa/PolyGateway/releases/tag/v1.3.4) · [包页面](https://gitea.iomgaa.online/iomgaa/-/packages/pypi/polygateway/1.3.4) · [验证与例外记录](https://gitea.iomgaa.online/iomgaa/PolyGateway/src/tag/v1.3.4/research-wiki/findings/2026-09-09-134-thinking-contracts-validation.md) 原始本轮日志:tests/outputs/134/publish-20260909/;既有红绿与独立审查见上述验证记录。
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: iomgaa/PolyGateway#25