grok-build-upstream-mirror/crates/codegen/xai-grok-shell/tests/test_nonblocking_startup.rs
grokkybara[bot] b41c75a578 Synced from monorepo
Changes:
- Detect the herdr multiplexer
- Mark /gboom as non-production code
- Bound peak memory when loading a large session
- Add a subagent lifecycle soak bounding threads, fds, and heap
- Stream inherited replay to bound fork memory
- Copy full plan from plan approval with y
- Stop armed signature verification from deleting the managed-deny smoke policy
- Add source-tagged terminal version telemetry
- Show the UI instantly and fetch models and settings in the background
- Session test helpers
- computer_reason on the ConversationHistoryDone trailer
2026-07-26 20:03:03 +01:00

183 lines
7.3 KiB
Rust

//! Non-blocking startup regression tests. `#[ignore]`; requires pre-built binary.
//!
//! These exercise leader startup through the persistent-leader fixture: the
//! leader must bind its socket and become ready without blocking on the remote
//! `/settings` + `/v1/models` fetch, and must self-heal its catalog once the
//! endpoint recovers.
//!
//! ```bash
//! cargo test -p xai-grok-shell --test test_nonblocking_startup -- --ignored
//! ```
#![cfg(unix)]
mod common;
use std::time::{Duration, Instant};
use xai_grok_test_support::leader::LeaderFixture;
use xai_grok_test_support::*;
async fn poll_until(ceiling: Duration, interval: Duration, condition: impl Fn() -> bool) -> bool {
let deadline = Instant::now() + ceiling;
while Instant::now() < deadline {
if condition() {
return true;
}
tokio::time::sleep(interval).await;
}
false
}
/// A hanging proxy must not delay leader readiness: the fixture (which waits for
/// the leader socket) must come up and a session must be created well within the
/// blocking-fetch window.
#[tokio::test]
#[ignore] // requires pre-built binary; run with --ignored
async fn leader_ready_while_proxy_hangs() {
tokio::task::LocalSet::new()
.run_until(async {
let server = MockInferenceServer::start().await.unwrap();
server.set_hang(true);
let workdir = git_workdir();
let sandbox = TestSandbox::new();
let started = Instant::now();
let fixture = LeaderFixture::start(&server, workdir.workspace(), &sandbox)
.await
.expect("leader must become ready while the proxy hangs");
let mut clients = Vec::new();
common::leader::run_with_cleanup(&fixture, &mut clients, |fixture, clients| {
Box::pin(async move {
clients.push(
fixture
.spawn_client(&server, workdir.workspace(), &sandbox)
.await
.expect("spawn leader client"),
);
clients[0].initialize().await;
let _session = clients[0].create_session(workdir.workspace()).await;
let elapsed = started.elapsed();
assert!(
elapsed < Duration::from_secs(25),
"startup took {elapsed:?} with a hanging proxy; readiness appears \
to block on the network fetch\nstderr:\n{}",
clients[0].stderr_text(),
);
})
})
.await;
})
.await;
}
/// The background catalog refresh must re-fetch and push `x.ai/models/update`
/// once a previously-hanging endpoint recovers.
#[tokio::test]
#[ignore] // requires pre-built binary; run with --ignored
async fn catalog_self_heals_after_endpoint_recovers() {
tokio::task::LocalSet::new()
.run_until(async {
let server = MockInferenceServer::start().await.unwrap();
server.set_hang(true);
let workdir = git_workdir();
let sandbox = TestSandbox::new();
let fixture = LeaderFixture::start(&server, workdir.workspace(), &sandbox)
.await
.expect("leader must become ready while the proxy hangs");
let mut clients = Vec::new();
common::leader::run_with_cleanup(&fixture, &mut clients, |fixture, clients| {
Box::pin(async move {
clients.push(
fixture
.spawn_client(&server, workdir.workspace(), &sandbox)
.await
.expect("spawn leader client"),
);
clients[0].initialize().await;
// Recover the endpoint. The background catalog refresh
// (5s-base backoff) re-fetches and pushes `x.ai/models/update`.
server.set_hang(false);
let healed =
poll_until(Duration::from_secs(60), Duration::from_millis(500), || {
clients[0].models_update_count() > 0
})
.await;
assert!(
healed,
"no x.ai/models/update after the endpoint recovered\nstderr:\n{}",
clients[0].stderr_text(),
);
})
})
.await;
})
.await;
}
/// Custom-backend reality: a user points at their own backend that serves
/// `/v1/models` + chat but blocks the cli-chat-proxy `/settings` (404). The
/// leader must boot fast, load its catalog from the served models, and run a
/// real prompt, even though remote settings never arrive.
#[tokio::test]
#[ignore] // requires pre-built binary; run with --ignored
async fn leader_usable_when_settings_blocked_but_models_served() {
tokio::task::LocalSet::new()
.run_until(async {
// Models + chat are served; `set_settings` is never called, so
// `/v1/settings` 404s, mimicking a blocked proxy settings endpoint.
let server = MockInferenceServer::start().await.unwrap();
let workdir = git_workdir();
let sandbox = TestSandbox::new();
let started = Instant::now();
let fixture = LeaderFixture::start(&server, workdir.workspace(), &sandbox)
.await
.expect("leader must become ready with /settings blocked");
let mut clients = Vec::new();
common::leader::run_with_cleanup(&fixture, &mut clients, |fixture, clients| {
Box::pin(async move {
clients.push(
fixture
.spawn_client(&server, workdir.workspace(), &sandbox)
.await
.expect("spawn leader client"),
);
clients[0].initialize().await;
let session = clients[0].create_session(workdir.workspace()).await;
// The custom backend serves chat, so a prompt round-trips
// despite the blocked settings endpoint.
clients[0]
.prompt(&session, "ping")
.await
.expect("prompt against the custom backend");
assert!(
server.has_chat_completion_request() || server.has_responses_request(),
"the prompt must reach the served chat endpoint\nstderr:\n{}",
clients[0].stderr_text(),
);
assert!(
started.elapsed() < Duration::from_secs(25),
"startup/usage blocked on the unreachable /settings\nstderr:\n{}",
clients[0].stderr_text(),
);
assert_eq!(
clients[0].settings_update_count(),
0,
"no settings update should land while /settings is blocked",
);
})
})
.await;
})
.await;
}