mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
## Why Holding the session MCP gate during `tools/list` blocks other runtime access. Releasing it requires preventing stale refresh results from publishing after configuration or authentication changes. ## What changed - Capture the Apps client under the session gate, then release the gate before fetching tools. - Validate runtime and authentication state before publishing the shared cache and client catalog; retry a superseded refresh once. - Wait for in-flight Apps refreshes before capturing model tools, preparing Apps calls, or shutting down the runtime. ## Testing Add regression coverage for stale refreshes after runtime invalidation or logout, publication validation after call preparation drains, and a model step that waits for refreshed Apps tools while MCP status queries remain available. GitOrigin-RevId: 42d4be8c4a9218933f8e3d07fc7f32f2da9cbd9d
92 lines
3.0 KiB
Rust
92 lines
3.0 KiB
Rust
#![cfg(test)]
|
|
//! Verifies that an in-flight Apps refresh cannot publish after invalidation.
|
|
|
|
use super::*;
|
|
use tokio::sync::Notify;
|
|
|
|
#[tokio::test]
|
|
async fn stale_apps_refresh_does_not_publish() -> anyhow::Result<()> {
|
|
let home = tempfile::tempdir()?;
|
|
let auth = CodexAuth::from_external_chatgpt_tokens(
|
|
"header.e30.token",
|
|
"account",
|
|
/*chatgpt_plan_type*/ None,
|
|
)?;
|
|
let auth_manager =
|
|
AuthManager::from_auth_for_testing_with_home(auth.clone(), home.path().to_path_buf());
|
|
let cache = ConnectorRuntimeManager::<ToolInfo>::default().context(
|
|
home.path().to_path_buf(),
|
|
ConnectorRuntimeContextKey::personal(
|
|
/*account_id*/ None, /*chatgpt_user_id*/ None,
|
|
),
|
|
);
|
|
let started = Arc::new(Notify::new());
|
|
let release = Arc::new(Notify::new());
|
|
let connections = crate::connection_manager::tests::create_test_manager_with_ready_apps_client(
|
|
cache.clone(),
|
|
"search",
|
|
Some(started.clone()),
|
|
Some(release.clone()),
|
|
)
|
|
.await?;
|
|
connections.list_all_tools().await;
|
|
let revision = connections
|
|
.stable_catalog_revisions(/*required_servers*/ &[], &HashSet::new())
|
|
.await
|
|
.expect("Apps catalog is ready");
|
|
let runtime = McpRuntime::empty(/*prefix_mcp_tool_names*/ true);
|
|
runtime.current.store(Arc::new(PublishedMcpRuntime {
|
|
connections: connections.clone(),
|
|
config: Some(Arc::new(crate::mcp::tests::test_mcp_config(
|
|
home.path().to_path_buf(),
|
|
))),
|
|
auth_token: Some(auth.get_token()?),
|
|
auth: Some(auth),
|
|
auth_manager: Some(auth_manager.clone()),
|
|
auth_owner: None,
|
|
auth_generation: Arc::new(()),
|
|
plugins_available: false,
|
|
ready_selected_capability_roots: Vec::new(),
|
|
environment_selections: Arc::default(),
|
|
ready_environments: HashMap::new(),
|
|
cached_binding: Mutex::new(None),
|
|
}));
|
|
|
|
let refresh = runtime.capture_apps_tool_refresh();
|
|
tokio::pin!(refresh);
|
|
tokio::select! {
|
|
_ = started.notified() => {}
|
|
_ = &mut refresh => panic!("refresh completed before tools/list was released"),
|
|
}
|
|
runtime.invalidate_apps_tool_refresh();
|
|
release.notify_one();
|
|
assert!(
|
|
refresh.await?.is_none(),
|
|
"runtime invalidation supersedes the fetch"
|
|
);
|
|
|
|
let refresh = runtime.capture_apps_tool_refresh();
|
|
tokio::pin!(refresh);
|
|
tokio::select! {
|
|
_ = started.notified() => {}
|
|
_ = &mut refresh => panic!("refresh completed before tools/list was released"),
|
|
}
|
|
auth_manager.logout().await?;
|
|
release.notify_one();
|
|
assert!(refresh.await?.is_none(), "logout supersedes the fetch");
|
|
|
|
assert!(
|
|
!cache.has_current_tools(),
|
|
"stale tools reached the shared cache"
|
|
);
|
|
assert!(
|
|
connections
|
|
.stable_catalog_revisions(/*required_servers*/ &[], &HashSet::new())
|
|
.await
|
|
.as_ref()
|
|
== Some(&revision),
|
|
"a stale fetch invalidated the existing client catalog"
|
|
);
|
|
Ok(())
|
|
}
|