CI 偶发失败:与业务改动无关的 crates.io 依赖缺口与两条用例自身缺陷 #327
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
背景
PR #316(AGC 资源工作台 V3)的一次 push
3bfc3f084上 CI 红了 Backend tests 与 Native shell tests。经排查,两条失败都与该 PR 的业务改动无关,本 Issue 单独跟踪。字节级判据:
git diff 39a96aa41 3bfc3f084只含前端 TS/TSX/CSS 与.md;server-rs树哈希两边完全相同(9edd75582c0f7ea7557a24c9ac9d9dec662e2e55),apps/ai-game-creator-shell/src-tauri树哈希也相同(f6d25066d1a5826a407cfa663783429b55baf6b7)。同一份 Rust 代码在 run 1947(39a96aa41)Backend / Native 全绿,在 run 1950(3bfc3f084)两红 → 同字节、不同结果 = 间歇性。问题 1:CI 基础设施缺口 —— 测试阶段依赖 live crates.io
Native shell tests(run 1950 job 7089)不是测试失败,前端部分全绿(
Test Files 84 passed、Tests 1223 passed),挂在这一步:缺口:
server-rs/Cargo.toml里crates/agent-runtime-core同时出现在members(第 10 行) 与exclude(Cargo 以exclude为准)→ 它不在 workspace 内、没有提交Cargo.lock(该目录下有.gitignore),而 CI 的Prepare native Rust dependencies只对 3 个 manifest 执行cargo fetch --locked(server-rs/Cargo.toml、apps/desktop-shell/src-tauri、apps/ai-game-creator-shell/src-tauri)—— 不含它。于是测试阶段才现场Updating crates.io index,且无锁文件兜底 ⇒ 每次 CI 都在赌网络。同类还需核清:
agent-runtime-orchestration、platform-agent是否也落在exclude且同样未纳入 fetch。建议修法:把这些 crate 的
Cargo.toml加进Prepare native Rust dependencies的cargo fetch列表;或给它们提交Cargo.lock并在 fetch/test 用--locked;或改为离线/预置 cargo 缓存。不要用|| true之类掩盖。问题 2:两条既有用例自身的缺陷(并行执行下间歇红)
Backend tests(run 1950 job 7088):
cargo test --locked --workspace --exclude spacetime-module --no-fail-fast→1038 passed; 2 failed; finished in 31.17s2.1 端口 TOCTOU
辅助函数
reserved_loopback_port()(bgfilter_worker.rs:3561-3568)=bind 127.0.0.1:0→ 取端口 →drop(listener);用例拿到端口后 sleep 700ms 才在:3620重新 bind。这 700ms 内并行用例可抢走该端口(典型 TOCTOU)。该文件最后改动为 2026-07-23a2ee879fc(引入该用例的提交),此后未动。建议修法:把 mock 的
sleep(700ms)挪到bind之后、accept之前(listen backlog 会挂住 connect),彻底消除窗口;或给 mock server 一个可注入TcpListener接缝。不要只调 sleep 时长——那只是改窗口大小。2.2 开放式 mock + 精确次数断言
=
assert_eq!(mock.finish().len(), 4)。该用例使用spawn_open_ended_mock_llm_server(Vec::new(), fallback)(开放式 mock,来多少答多少),却把"精确 4 次请求"钉死;多出的第 5 次来自本模块的重试轮(同文件已有 4 条 retry 用例为证),而用例里设了llm_max_retries: 0期望不重试。文件最后改动 2026-08-083c00bcd2e,用例由281c84b7b(#142)引入。建议修法:断言改成客户端口径("4 次调用都 200 且都不是 429"——用例名本来就只承诺这一点);若要钉"无额外重试",把开放式 mock 换成有界 mock(第 5 次请求直接 panic 并打印捕获到的请求原文),把不确定性变成可诊断的确定性失败。
历史红率(最近 13 次 run)
useUiEditorPage.ts(1211) TS2339)、1949 是另一 PR 的 vitest 失败、1950 是本次的 crates.io 超时(新形态)。pull request head does not contain the latest base commit(分支落后守卫,非测试失败)、1938 是 icon 白名单断言(已在 PR #316 修复)。验收/完成判据
--offline或锁文件 +--locked可过);bgfilter_worker那条用例在默认并行下连续多轮不再出现AddrInUse;editor_background_music_prompt_assist那条用例的断言与其用例名承诺的语义一致,且失败时能给出可诊断信息;关联
3bfc3f084)GET /api/v1/repos/{owner}/{repo}/actions/jobs/{job_id}/logs(不需要 run id)