AGC 壳 Rust 单测统一 mock provider 请求预算,修掉分片 4 第二次往返超时
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m1s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m37s
Project CI / Backend tests (pull_request) Successful in 3m54s
Project CI / Frontend tests (pull_request) Successful in 1m54s
Project CI / Native shell tests (pull_request) Successful in 5m53s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m53s
Project CI / Repository checks (pull_request) Successful in 2m28s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m52s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 11m53s

- tests/mod.rs 新增 MOCK_PROVIDER_REQUEST_TIMEOUT(30 秒),作为「等一条 mock provider 请求到达」的唯一预算
- tests/sessions.rs 的 7 处 recv_timeout 由散落的 2 秒 / 10 秒改为引用该常量,确认「不会有下一个请求」的 200ms 负向断言保持不变
- wait_for_captured_mock_request 的内联 10 秒预算改为引用同一常量
- pitfalls 记录该超时是 CI 分片并发放大所致,而非产品缺陷,并附本次复核证据
This commit is contained in:
2026-10-01 17:07:03 +08:00
parent a7cc28134d
commit bdd315f12f
3 changed files with 21 additions and 8 deletions
@@ -427,6 +427,11 @@ fn agent_goal_sidecar_path_for_test(root: &Path, agent_id: &str, session_id: &st
/// 因此统一等「状态离开 running 且 lane 可获取」,并留出远大于单机耗时的硬预算。
const AGENT_RUNTIME_TERMINAL_WAIT_BUDGET: Duration = Duration::from_secs(60);
/// mock provider 是进程内 HTTP 服务,但一轮请求要等 Agent Runtime 后台线程跑到对应步骤才会发出。
/// CI 上多个 rust shard 与前端任务同机并发时,单轮往返可能被拖过数秒;这里只断言请求确实到达,
/// 因此给出远大于单机耗时的硬预算,避免把调度抖动误判成链路故障。请求到达即返回,成功路径零成本。
const MOCK_PROVIDER_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
fn wait_for_agent_runtime_lane_release(
root: &Path,
agent_id: &str,
@@ -483,7 +488,7 @@ async fn wait_for_captured_mock_request(
receiver: &mpsc::Receiver<String>,
description: &str,
) -> String {
let deadline = Instant::now() + Duration::from_secs(10);
let deadline = Instant::now() + MOCK_PROVIDER_REQUEST_TIMEOUT;
loop {
match receiver.try_recv() {
Ok(request) => return request,
@@ -217,7 +217,7 @@ async fn background_agent_runtime_recovers_bound_context_for_the_same_session_an
.expect("resume background task from durable context");
assert_eq!(resumed.len(), 1);
let request = receiver
.recv_timeout(Duration::from_secs(2))
.recv_timeout(MOCK_PROVIDER_REQUEST_TIMEOUT)
.unwrap_or_else(|error| {
let runtime = read_game_creator_agent_runtime_at(&root, "design-director")
.expect("read runtime after recovery timeout")
@@ -396,12 +396,12 @@ async fn background_agent_runtime_can_write_memory_and_project_files() {
.expect("start background task");
let plan_request = receiver
.recv_timeout(Duration::from_secs(2))
.recv_timeout(MOCK_PROVIDER_REQUEST_TIMEOUT)
.expect("plan llm request");
assert!(plan_request.contains("memory.write"));
assert!(plan_request.contains("file.write"));
let second_request = receiver
.recv_timeout(Duration::from_secs(2))
.recv_timeout(MOCK_PROVIDER_REQUEST_TIMEOUT)
.expect("second llm request");
assert!(second_request.contains("memory.write"));
assert!(second_request.contains("file.write"));
@@ -409,7 +409,7 @@ async fn background_agent_runtime_can_write_memory_and_project_files() {
assert!(second_request.contains("已写入 long 记忆"));
assert!(second_request.contains("已写入 game/agent-notes.md"));
let final_request = receiver
.recv_timeout(Duration::from_secs(10))
.recv_timeout(MOCK_PROVIDER_REQUEST_TIMEOUT)
.expect("final llm request after current revision verification");
assert!(final_request.contains("project.verify"));
assert!(final_request.contains("AGENT_RUNTIME_CURRENT_REVISION_OK"));
@@ -557,11 +557,11 @@ async fn background_agent_runtime_blocks_cross_agent_private_memory_write() {
.expect("start background task");
let plan_request = receiver
.recv_timeout(Duration::from_secs(2))
.recv_timeout(MOCK_PROVIDER_REQUEST_TIMEOUT)
.expect("plan llm request");
assert!(plan_request.contains("Agent 私有记忆只能由本人写入"));
let second_request = receiver
.recv_timeout(Duration::from_secs(2))
.recv_timeout(MOCK_PROVIDER_REQUEST_TIMEOUT)
.expect("second llm request");
assert!(second_request.contains("\\\"tool\\\": \\\"memory.write\\\""));
assert!(second_request.contains("\\\"status\\\": \\\"blocked\\\""));
@@ -648,7 +648,7 @@ async fn background_agent_runtime_file_and_memory_writes_respect_project_policy(
.expect("start background task");
let _plan_request = receiver
.recv_timeout(Duration::from_secs(2))
.recv_timeout(MOCK_PROVIDER_REQUEST_TIMEOUT)
.expect("plan llm request");
assert!(receiver.recv_timeout(Duration::from_millis(200)).is_err());
@@ -6192,3 +6192,11 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
- **同时**:分片器的失败复核原先只认 `tests::` / `process_session::tests::` 前缀,而 bin 目标用例名是 `agent::…::tests::…`,于是 3143 条里 2218 条永远进不了复核,偶发假红直接判红;现在按本片名单精确匹配,并且复核也失败时保留首次现场。
- **验证**:本机用 CI 那一片(`--shards=4 --shard-index=1` 的第 3 块)连跑:修复前 5 次红 3 次,修复后 18 次全绿;`node --test apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs` 3 项通过。
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/entrypoints.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/thread_manager/dispatch.rs`、`apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs`。
## 2026-10-01 AGC 壳 Rust 单测里等 mock provider 请求的 2 秒预算,在 CI 分片并发下偏窄
- **现象**:`tests::sessions::background_agent_runtime_can_write_memory_and_project_files` 在 CI `agc-rust-shard-4` 偶发红,`sessions.rs:405` 报 `second llm request: Timeout`——首个请求已经通过,只有第二次 provider 往返超时;失败复核同样红,整条用例 3.6 秒就结束。
- **原因**:该断言只等了 `Duration::from_secs(2)`。一轮 mock 往返要等 Agent Runtime 后台线程跑到发请求那一步,CI 上 4 个 rust shard 与前端任务同机并发时单轮耗时会被拖到数秒;本机空载实测该请求 0.99–1.29 秒,同机负载下曾到 4.87 秒 / 5.18 秒。链路本身是好的,缺的是预算——这是分片并发放大出来的抖动,不是产品缺陷(同一次失败切回基线 commit 照样复现)。
- **处理(现行口径)**:`src/tests/mod.rs` 新增 `MOCK_PROVIDER_REQUEST_TIMEOUT`(30 秒)作为「等一条 mock provider 请求到达」的唯一预算,`wait_for_captured_mock_request` 与 `sessions.rs` 里 7 处 `recv_timeout` 统一引用它;负向断言(确认「不会有下一个请求」的 200ms 窗口)保持不动。请求到达即返回,成功路径零成本,真断链仍会失败,只是失败得更晚。
- **验证**:`cargo test --bin genarrative-ai-game-creator-shell tests::sessions:: -- --test-threads=1` 44 passed;`node apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs --shards=4 --shard-index=4` 787 tests ok in 88.7s(正是原先失败的那一片)。
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/tests/sessions.rs`、`apps/ai-game-creator-shell/src-tauri/src/tests/mod.rs`。