From 6ae99fd35ffdc318508fb80062fda0b9ff53579d Mon Sep 17 00:00:00 2001 From: xl-openai Date: Mon, 1 Jun 2026 14:27:18 -0700 Subject: [PATCH 1/3] fix: Deduplicate installed local and remote curated plugins (#25681) ## Summary - Deduplicate installed `openai-curated` and `openai-curated-remote` plugin conflicts by feature flag. - Prefer remote when remote plugins are enabled; otherwise prefer local, while preserving one-sided installs. ## Testing - `just fmt` - `git diff --check` - Targeted `just test` was blocked locally because `cargo-nextest` is not installed. --- codex-rs/app-server/src/request_processors.rs | 1 - .../src/request_processors/plugins.rs | 52 +++++++++ .../app-server/tests/suite/v2/plugin_list.rs | 101 +++++++++++++++++ codex-rs/core-plugins/src/loader.rs | 65 ++++++++++- codex-rs/core-plugins/src/manager.rs | 2 + codex-rs/core-plugins/src/manager_tests.rs | 107 +++++++++++++++++- 6 files changed, 323 insertions(+), 5 deletions(-) diff --git a/codex-rs/app-server/src/request_processors.rs b/codex-rs/app-server/src/request_processors.rs index a110b73191..d24f1ef8c1 100644 --- a/codex-rs/app-server/src/request_processors.rs +++ b/codex-rs/app-server/src/request_processors.rs @@ -302,7 +302,6 @@ use codex_core::windows_sandbox::WindowsSandboxLevelExt; use codex_core::windows_sandbox::WindowsSandboxSetupMode as CoreWindowsSandboxSetupMode; use codex_core::windows_sandbox::WindowsSandboxSetupRequest; use codex_core::windows_sandbox::sandbox_setup_is_complete; -use codex_core_plugins::OPENAI_CURATED_MARKETPLACE_NAME; use codex_core_plugins::PluginInstallError as CorePluginInstallError; use codex_core_plugins::PluginInstallRequest; use codex_core_plugins::PluginLoadOutcome; diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index 7b7b96a00c..4171887cdd 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -6,6 +6,8 @@ use codex_app_server_protocol::PluginInstallPolicy; use codex_app_server_protocol::PluginSharePrincipalRole; use codex_app_server_protocol::PluginShareTargetRole; use codex_config::types::McpServerConfig; +use codex_core_plugins::OPENAI_CURATED_MARKETPLACE_NAME; +use codex_core_plugins::remote::REMOTE_GLOBAL_MARKETPLACE_NAME; use codex_core_plugins::remote::RemotePluginScope; use codex_core_plugins::remote::is_valid_remote_plugin_id; use codex_core_plugins::remote::validate_remote_plugin_id; @@ -156,6 +158,52 @@ fn remote_installed_plugin_visible_scopes(config: &Config) -> Vec, + prefer_remote_curated_conflicts: bool, +) { + let local_installed_plugin_names = marketplaces + .iter() + .find(|marketplace| marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME) + .map(|marketplace| installed_plugin_names(&marketplace.plugins)) + .unwrap_or_default(); + let remote_installed_plugin_names = marketplaces + .iter() + .find(|marketplace| marketplace.name == REMOTE_GLOBAL_MARKETPLACE_NAME) + .map(|marketplace| installed_plugin_names(&marketplace.plugins)) + .unwrap_or_default(); + let conflicting_plugin_names = local_installed_plugin_names + .intersection(&remote_installed_plugin_names) + .cloned() + .collect::>(); + if conflicting_plugin_names.is_empty() { + return; + } + + let marketplace_to_filter = if prefer_remote_curated_conflicts { + OPENAI_CURATED_MARKETPLACE_NAME + } else { + REMOTE_GLOBAL_MARKETPLACE_NAME + }; + for marketplace in marketplaces.iter_mut() { + if marketplace.name != marketplace_to_filter { + continue; + } + marketplace + .plugins + .retain(|plugin| !plugin.installed || !conflicting_plugin_names.contains(&plugin.name)); + } + marketplaces.retain(|marketplace| !marketplace.plugins.is_empty()); +} + +fn installed_plugin_names(plugins: &[PluginSummary]) -> HashSet { + plugins + .iter() + .filter(|plugin| plugin.installed) + .map(|plugin| plugin.name.clone()) + .collect() +} + fn remote_plugin_share_discoverability( discoverability: PluginShareDiscoverability, ) -> codex_core_plugins::remote::RemotePluginShareDiscoverability { @@ -740,6 +788,10 @@ impl PluginRequestProcessor { ) .await, ); + filter_openai_curated_installed_conflicts( + &mut data, + config.features.enabled(Feature::RemotePlugin), + ); Ok(PluginInstalledResponse { marketplaces: data, diff --git a/codex-rs/app-server/tests/suite/v2/plugin_list.rs b/codex-rs/app-server/tests/suite/v2/plugin_list.rs index 93dbfb4773..e5ca8ea62d 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_list.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_list.rs @@ -180,6 +180,107 @@ enabled = true Ok(()) } +#[tokio::test] +async fn plugin_installed_prefers_remote_curated_conflicts_when_remote_plugin_enabled() -> Result<()> +{ + let codex_home = TempDir::new()?; + let server = MockServer::start().await; + write_openai_curated_marketplace(codex_home.path(), &["linear", "calendar"])?; + write_installed_plugin(&codex_home, "openai-curated", "linear")?; + write_installed_plugin(&codex_home, "openai-curated", "calendar")?; + std::fs::write( + codex_home.path().join("config.toml"), + format!( + r#"chatgpt_base_url = "{}/backend-api/" + +[features] +plugins = true +remote_plugin = true +plugin_sharing = false + +[plugins."linear@openai-curated"] +enabled = true + +[plugins."calendar@openai-curated"] +enabled = true +"#, + server.uri() + ), + )?; + write_chatgpt_auth( + codex_home.path(), + ChatGptAuthFixture::new("chatgpt-token") + .account_id("account-123") + .chatgpt_user_id("user-123") + .chatgpt_account_id("account-123"), + AuthCredentialsStoreMode::File, + )?; + let mut global_installed_body: serde_json::Value = serde_json::from_str( + &remote_installed_plugin_body("", "1.2.3", /*enabled*/ true), + )?; + let mut remote_only = global_installed_body["plugins"][0].clone(); + remote_only["id"] = serde_json::json!("plugins~Plugin_11111111111111111111111111111111"); + remote_only["name"] = serde_json::json!("remote-only"); + remote_only["release"]["display_name"] = serde_json::json!("Remote Only"); + global_installed_body["plugins"] + .as_array_mut() + .expect("installed plugins should be an array") + .push(remote_only); + let global_installed_body = serde_json::to_string(&global_installed_body)?; + mount_remote_installed_plugins(&server, "GLOBAL", &global_installed_body).await; + mount_remote_installed_plugins(&server, "WORKSPACE", empty_remote_installed_plugins_body()) + .await; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; + + let request_id = mcp + .send_plugin_installed_request(PluginInstalledParams { + cwds: None, + install_suggestion_plugin_names: None, + }) + .await?; + + let response: JSONRPCResponse = timeout( + DEFAULT_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(request_id)), + ) + .await??; + let response: PluginInstalledResponse = to_response(response)?; + + let local_marketplace = response + .marketplaces + .iter() + .find(|marketplace| marketplace.name == "openai-curated") + .expect("expected openai-curated marketplace entry"); + assert_eq!( + local_marketplace + .plugins + .iter() + .map(|plugin| plugin.id.clone()) + .collect::>(), + vec!["calendar@openai-curated".to_string()] + ); + let remote_marketplace = response + .marketplaces + .iter() + .find(|marketplace| marketplace.name == "openai-curated-remote") + .expect("expected openai-curated-remote marketplace entry"); + assert_eq!( + remote_marketplace + .plugins + .iter() + .map(|plugin| plugin.id.clone()) + .collect::>(), + vec![ + "linear@openai-curated-remote".to_string(), + "remote-only@openai-curated-remote".to_string(), + ] + ); + assert_eq!(response.marketplace_load_errors, Vec::new()); + Ok(()) +} + #[tokio::test] async fn plugin_installed_ignores_local_cache_without_catalog() -> Result<()> { let codex_home = TempDir::new()?; diff --git a/codex-rs/core-plugins/src/loader.rs b/codex-rs/core-plugins/src/loader.rs index 121b583cb4..d51771085d 100644 --- a/codex-rs/core-plugins/src/loader.rs +++ b/codex-rs/core-plugins/src/loader.rs @@ -5,6 +5,7 @@ use crate::manifest::load_plugin_manifest; use crate::marketplace::MarketplacePluginSource; use crate::marketplace::list_marketplaces; use crate::marketplace::load_marketplace; +use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME; use crate::remote::RemoteInstalledPlugin; use crate::store::PluginStore; use crate::store::plugin_version_for_source; @@ -113,10 +114,15 @@ pub async fn load_plugins_from_layer_stack( extra_plugins: HashMap, store: &PluginStore, restriction_product: Option, + prefer_remote_curated_conflicts: bool, ) -> PluginLoadOutcome { let skill_config_rules = skill_config_rules_from_stack(config_layer_stack); - let mut configured_plugins = configured_plugins_from_stack(config_layer_stack); - configured_plugins.extend(extra_plugins); + let configured_plugins = merge_configured_plugins_with_remote_installed( + configured_plugins_from_stack(config_layer_stack), + extra_plugins, + store, + prefer_remote_curated_conflicts, + ); let mut configured_plugins: Vec<_> = configured_plugins.into_iter().collect(); configured_plugins.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); @@ -149,6 +155,61 @@ pub async fn load_plugins_from_layer_stack( PluginLoadOutcome::from_plugins(plugins) } +fn merge_configured_plugins_with_remote_installed( + mut configured_plugins: HashMap, + extra_plugins: HashMap, + store: &PluginStore, + prefer_remote_curated_conflicts: bool, +) -> HashMap { + let local_curated_installed_plugin_keys = configured_plugins + .keys() + .filter_map(|plugin_key| { + installed_plugin_name_for_marketplace( + plugin_key, + OPENAI_CURATED_MARKETPLACE_NAME, + store, + ) + .map(|plugin_name| (plugin_name, plugin_key.clone())) + }) + .collect::>(); + + for (plugin_key, plugin_config) in extra_plugins { + let remote_curated_plugin_name = installed_plugin_name_for_marketplace( + &plugin_key, + REMOTE_GLOBAL_MARKETPLACE_NAME, + store, + ); + let local_curated_plugin_key = remote_curated_plugin_name + .as_ref() + .and_then(|plugin_name| local_curated_installed_plugin_keys.get(plugin_name)); + + if let Some(local_curated_plugin_key) = local_curated_plugin_key { + if prefer_remote_curated_conflicts { + configured_plugins.remove(local_curated_plugin_key); + } else { + continue; + } + } + + configured_plugins.insert(plugin_key, plugin_config); + } + + configured_plugins +} + +fn installed_plugin_name_for_marketplace( + plugin_key: &str, + marketplace_name: &str, + store: &PluginStore, +) -> Option { + let plugin_id = PluginId::parse(plugin_key).ok()?; + if plugin_id.marketplace_name != marketplace_name { + return None; + } + store.active_plugin_root(&plugin_id)?; + Some(plugin_id.plugin_name) +} + pub fn remote_installed_plugins_to_config( plugins: &[RemoteInstalledPlugin], store: &PluginStore, diff --git a/codex-rs/core-plugins/src/manager.rs b/codex-rs/core-plugins/src/manager.rs index ec3e74dd16..ff596273f9 100644 --- a/codex-rs/core-plugins/src/manager.rs +++ b/codex-rs/core-plugins/src/manager.rs @@ -492,6 +492,7 @@ impl PluginsManager { self.remote_installed_plugin_configs(), &self.store, self.restriction_product, + config.remote_plugin_enabled, ) .await; log_plugin_load_errors(&outcome); @@ -537,6 +538,7 @@ impl PluginsManager { self.remote_installed_plugin_configs(), &self.store, self.restriction_product, + config.remote_plugin_enabled, ) .await } diff --git a/codex-rs/core-plugins/src/manager_tests.rs b/codex-rs/core-plugins/src/manager_tests.rs index f9fae56318..f4b6c49da0 100644 --- a/codex-rs/core-plugins/src/manager_tests.rs +++ b/codex-rs/core-plugins/src/manager_tests.rs @@ -133,10 +133,14 @@ async fn load_config(codex_home: &Path, cwd: &Path) -> PluginsConfigInput { } fn remote_installed_linear_plugin() -> RemoteInstalledPlugin { + remote_installed_plugin("linear") +} + +fn remote_installed_plugin(name: &str) -> RemoteInstalledPlugin { RemoteInstalledPlugin { marketplace_name: "openai-curated-remote".to_string(), - id: "plugins~Plugin_linear".to_string(), - name: "linear".to_string(), + id: format!("plugins~Plugin_{name}"), + name: name.to_string(), enabled: true, install_policy: codex_app_server_protocol::PluginInstallPolicy::Available, auth_policy: codex_app_server_protocol::PluginAuthPolicy::OnUse, @@ -146,6 +150,18 @@ fn remote_installed_linear_plugin() -> RemoteInstalledPlugin { } } +fn write_cached_plugin(codex_home: &Path, marketplace_name: &str, plugin_name: &str) { + write_plugin_with_version( + &codex_home + .join("plugins/cache") + .join(marketplace_name) + .join(plugin_name), + "local", + plugin_name, + /*manifest_version*/ Some("local"), + ); +} + #[tokio::test] async fn load_plugins_loads_default_skills_and_mcp_servers() { let codex_home = TempDir::new().unwrap(); @@ -352,6 +368,92 @@ remote_plugin = true assert_eq!(outcome, PluginLoadOutcome::default()); } +#[tokio::test] +async fn remote_installed_cache_prefers_local_curated_conflicts_when_remote_plugin_disabled() { + let codex_home = TempDir::new().unwrap(); + write_file( + &codex_home.path().join(CONFIG_TOML_FILE), + r#"[features] +plugins = true +remote_plugin = false + +[plugins."linear@openai-curated"] +enabled = true + +[plugins."calendar@openai-curated"] +enabled = true +"#, + ); + write_cached_plugin(codex_home.path(), "openai-curated", "linear"); + write_cached_plugin(codex_home.path(), "openai-curated", "calendar"); + write_cached_plugin(codex_home.path(), "openai-curated-remote", "linear"); + write_cached_plugin(codex_home.path(), "openai-curated-remote", "remote-only"); + + let config = load_config(codex_home.path(), codex_home.path()).await; + let manager = PluginsManager::new(codex_home.path().to_path_buf()); + manager.write_remote_installed_plugins_cache(vec![ + remote_installed_plugin("linear"), + remote_installed_plugin("remote-only"), + ]); + + let outcome = manager.plugins_for_config(&config).await; + assert_eq!( + outcome + .plugins() + .iter() + .map(|plugin| plugin.config_name.clone()) + .collect::>(), + vec![ + "calendar@openai-curated".to_string(), + "linear@openai-curated".to_string(), + "remote-only@openai-curated-remote".to_string(), + ] + ); +} + +#[tokio::test] +async fn remote_installed_cache_prefers_remote_curated_conflicts_when_remote_plugin_enabled() { + let codex_home = TempDir::new().unwrap(); + write_file( + &codex_home.path().join(CONFIG_TOML_FILE), + r#"[features] +plugins = true +remote_plugin = true + +[plugins."linear@openai-curated"] +enabled = true + +[plugins."calendar@openai-curated"] +enabled = true +"#, + ); + write_cached_plugin(codex_home.path(), "openai-curated", "linear"); + write_cached_plugin(codex_home.path(), "openai-curated", "calendar"); + write_cached_plugin(codex_home.path(), "openai-curated-remote", "linear"); + write_cached_plugin(codex_home.path(), "openai-curated-remote", "remote-only"); + + let config = load_config(codex_home.path(), codex_home.path()).await; + let manager = PluginsManager::new(codex_home.path().to_path_buf()); + manager.write_remote_installed_plugins_cache(vec![ + remote_installed_plugin("linear"), + remote_installed_plugin("remote-only"), + ]); + + let outcome = manager.plugins_for_config(&config).await; + assert_eq!( + outcome + .plugins() + .iter() + .map(|plugin| plugin.config_name.clone()) + .collect::>(), + vec![ + "calendar@openai-curated".to_string(), + "linear@openai-curated-remote".to_string(), + "remote-only@openai-curated-remote".to_string(), + ] + ); +} + #[tokio::test] async fn build_remote_installed_plugin_marketplaces_from_cache_uses_remote_metadata() { let codex_home = TempDir::new().unwrap(); @@ -3713,6 +3815,7 @@ async fn load_plugins_ignores_project_config_files() { std::collections::HashMap::new(), &PluginStore::new(codex_home.path().to_path_buf()), Some(Product::Codex), + /*prefer_remote_curated_conflicts*/ false, ) .await; From 6536841d894b3425e3dcf638c9b64678a4db8177 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 1 Jun 2026 14:49:38 -0700 Subject: [PATCH 2/3] fix: rename McpServer to TestAppServer (#25701) This PR brought to you via VS Code rather than Codex... - opened `codex-rs/app-server/tests/common/mcp_process.rs` - put the cursor on `McpServer` - hit `F2` and renamed the symbol to `TestAppServer` - went to the file tree - hit enter and renamed `mcp_process.rs` to `test_app_server.rs` - ran **Save All Files** from the Command Palette - ran `just fmt` The End (Admittedly, most of the local variables for `TestAppServer` are still named `mcp`, though.) --- codex-rs/app-server/tests/common/lib.rs | 8 +- .../{mcp_process.rs => test_app_server.rs} | 6 +- codex-rs/app-server/tests/suite/auth.rs | 23 +++--- .../tests/suite/conversation_summary.rs | 6 +- .../tests/suite/fuzzy_file_search.rs | 18 ++--- codex-rs/app-server/tests/suite/v2/account.rs | 64 ++++++++------- .../app-server/tests/suite/v2/app_list.rs | 30 +++---- .../app-server/tests/suite/v2/attestation.rs | 5 +- .../tests/suite/v2/client_metadata.rs | 16 ++-- .../tests/suite/v2/collaboration_mode_list.rs | 4 +- .../app-server/tests/suite/v2/command_exec.rs | 46 +++++------ .../app-server/tests/suite/v2/compaction.rs | 27 ++++--- .../app-server/tests/suite/v2/config_rpc.rs | 36 ++++----- .../tests/suite/v2/dynamic_tools.rs | 18 ++--- .../tests/suite/v2/experimental_api.rs | 18 ++--- .../suite/v2/experimental_feature_list.rs | 28 +++---- .../tests/suite/v2/external_agent_config.rs | 29 ++++--- codex-rs/app-server/tests/suite/v2/fs.rs | 12 +-- .../app-server/tests/suite/v2/hooks_list.rs | 18 ++--- .../app-server/tests/suite/v2/initialize.rs | 16 ++-- .../tests/suite/v2/marketplace_add.rs | 4 +- .../tests/suite/v2/marketplace_remove.rs | 6 +- .../tests/suite/v2/marketplace_upgrade.rs | 12 +-- .../app-server/tests/suite/v2/mcp_resource.rs | 6 +- .../tests/suite/v2/mcp_server_elicitation.rs | 4 +- .../tests/suite/v2/mcp_server_status.rs | 10 +-- .../app-server/tests/suite/v2/mcp_tool.rs | 14 ++-- .../app-server/tests/suite/v2/memory_reset.rs | 4 +- .../app-server/tests/suite/v2/model_list.rs | 13 ++-- .../v2/model_provider_capabilities_read.rs | 6 +- .../tests/suite/v2/output_schema.rs | 6 +- .../tests/suite/v2/permission_profile_list.rs | 10 +-- .../app-server/tests/suite/v2/plan_item.rs | 10 +-- .../tests/suite/v2/plugin_install.rs | 46 +++++------ .../app-server/tests/suite/v2/plugin_list.rs | 70 ++++++++--------- .../app-server/tests/suite/v2/plugin_read.rs | 42 +++++----- .../app-server/tests/suite/v2/plugin_share.rs | 28 +++---- .../tests/suite/v2/plugin_uninstall.rs | 22 +++--- .../app-server/tests/suite/v2/process_exec.rs | 10 +-- .../app-server/tests/suite/v2/rate_limits.rs | 26 ++++--- .../tests/suite/v2/realtime_conversation.rs | 31 ++++---- .../tests/suite/v2/remote_control.rs | 10 +-- .../tests/suite/v2/request_permissions.rs | 4 +- .../tests/suite/v2/request_user_input.rs | 4 +- codex-rs/app-server/tests/suite/v2/review.rs | 18 ++--- .../tests/suite/v2/safety_check_downgrade.rs | 16 ++-- .../app-server/tests/suite/v2/skills_list.rs | 22 +++--- .../tests/suite/v2/thread_archive.rs | 14 ++-- .../app-server/tests/suite/v2/thread_fork.rs | 20 ++--- .../tests/suite/v2/thread_inject_items.rs | 6 +- .../app-server/tests/suite/v2/thread_list.rs | 10 +-- .../tests/suite/v2/thread_loaded_list.rs | 8 +- .../tests/suite/v2/thread_memory_mode_set.rs | 6 +- .../tests/suite/v2/thread_metadata_update.rs | 16 ++-- .../app-server/tests/suite/v2/thread_read.rs | 32 ++++---- .../tests/suite/v2/thread_resume.rs | 78 +++++++++---------- .../tests/suite/v2/thread_rollback.rs | 4 +- .../tests/suite/v2/thread_settings_update.rs | 22 +++--- .../tests/suite/v2/thread_shell_command.rs | 16 ++-- .../app-server/tests/suite/v2/thread_start.rs | 42 +++++----- .../tests/suite/v2/thread_status.rs | 6 +- .../tests/suite/v2/thread_unarchive.rs | 4 +- .../tests/suite/v2/thread_unsubscribe.rs | 14 ++-- .../tests/suite/v2/turn_interrupt.rs | 8 +- .../app-server/tests/suite/v2/turn_start.rs | 70 ++++++++--------- .../tests/suite/v2/turn_start_zsh_fork.rs | 6 +- .../app-server/tests/suite/v2/turn_steer.rs | 10 +-- .../app-server/tests/suite/v2/web_search.rs | 13 ++-- .../tests/suite/v2/windows_sandbox_setup.rs | 6 +- 69 files changed, 666 insertions(+), 627 deletions(-) rename codex-rs/app-server/tests/common/{mcp_process.rs => test_app_server.rs} (99%) diff --git a/codex-rs/app-server/tests/common/lib.rs b/codex-rs/app-server/tests/common/lib.rs index 46eeae7ec1..2285907ad9 100644 --- a/codex-rs/app-server/tests/common/lib.rs +++ b/codex-rs/app-server/tests/common/lib.rs @@ -1,11 +1,11 @@ mod analytics_server; mod auth_fixtures; mod config; -mod mcp_process; mod mock_model_server; mod models_cache; mod responses; mod rollout; +mod test_app_server; pub use analytics_server::start_analytics_events_server; pub use auth_fixtures::ChatGptAuthFixture; @@ -24,9 +24,6 @@ pub use core_test_support::test_absolute_path; pub use core_test_support::test_path_buf_with_windows; pub use core_test_support::test_tmp_path; pub use core_test_support::test_tmp_path_buf; -pub use mcp_process::DEFAULT_CLIENT_NAME; -pub use mcp_process::DISABLE_PLUGIN_STARTUP_TASKS_ARG; -pub use mcp_process::McpProcess; pub use mock_model_server::create_mock_responses_server_repeating_assistant; pub use mock_model_server::create_mock_responses_server_sequence; pub use mock_model_server::create_mock_responses_server_sequence_unchecked; @@ -45,6 +42,9 @@ pub use rollout::create_fake_rollout_with_text_elements; pub use rollout::create_fake_rollout_with_token_usage; pub use rollout::rollout_path; use serde::de::DeserializeOwned; +pub use test_app_server::DEFAULT_CLIENT_NAME; +pub use test_app_server::DISABLE_PLUGIN_STARTUP_TASKS_ARG; +pub use test_app_server::TestAppServer; pub fn to_response(response: JSONRPCResponse) -> anyhow::Result { let value = serde_json::to_value(response.result)?; diff --git a/codex-rs/app-server/tests/common/mcp_process.rs b/codex-rs/app-server/tests/common/test_app_server.rs similarity index 99% rename from codex-rs/app-server/tests/common/mcp_process.rs rename to codex-rs/app-server/tests/common/test_app_server.rs index d3bbfbf180..3a3787d234 100644 --- a/codex-rs/app-server/tests/common/mcp_process.rs +++ b/codex-rs/app-server/tests/common/test_app_server.rs @@ -106,7 +106,7 @@ use codex_app_server_protocol::WindowsSandboxSetupStartParams; use codex_login::default_client::CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR; use tokio::process::Command; -pub struct McpProcess { +pub struct TestAppServer { next_request_id: AtomicI64, /// Retain this child process until the client is dropped. The Tokio runtime /// will make a "best effort" to reap the process after it exits, but it is @@ -122,7 +122,7 @@ pub const DEFAULT_CLIENT_NAME: &str = "codex-app-server-tests"; pub const DISABLE_PLUGIN_STARTUP_TASKS_ARG: &str = "--disable-plugin-startup-tasks-for-tests"; const DISABLE_MANAGED_CONFIG_ENV_VAR: &str = "CODEX_APP_SERVER_DISABLE_MANAGED_CONFIG"; -impl McpProcess { +impl TestAppServer { pub async fn new(codex_home: &Path) -> anyhow::Result { Self::new_with_env_and_args(codex_home, &[], &[DISABLE_PLUGIN_STARTUP_TASKS_ARG]).await } @@ -1513,7 +1513,7 @@ impl McpProcess { } } -impl Drop for McpProcess { +impl Drop for TestAppServer { fn drop(&mut self) { // These tests spawn a `codex-app-server` child process. // diff --git a/codex-rs/app-server/tests/suite/auth.rs b/codex-rs/app-server/tests/suite/auth.rs index 1e60871012..b1ef99cb71 100644 --- a/codex-rs/app-server/tests/suite/auth.rs +++ b/codex-rs/app-server/tests/suite/auth.rs @@ -1,6 +1,6 @@ use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use chrono::Duration; @@ -92,7 +92,7 @@ shell_snapshot = false std::fs::write(config_toml, contents) } -async fn login_with_api_key_via_request(mcp: &mut McpProcess, api_key: &str) -> Result<()> { +async fn login_with_api_key_via_request(mcp: &mut TestAppServer, api_key: &str) -> Result<()> { let request_id = mcp.send_login_account_api_key_request(api_key).await?; let resp: JSONRPCResponse = timeout( @@ -110,7 +110,8 @@ async fn get_auth_status_no_auth() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path())?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -136,7 +137,7 @@ async fn get_auth_status_with_api_key() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; login_with_api_key_via_request(&mut mcp, "sk-test-key").await?; @@ -164,7 +165,7 @@ async fn get_auth_status_with_api_key_when_auth_not_required() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml_custom_provider(codex_home.path(), /*requires_openai_auth*/ false)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; login_with_api_key_via_request(&mut mcp, "sk-test-key").await?; @@ -197,7 +198,7 @@ async fn get_auth_status_with_api_key_no_include_token() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; login_with_api_key_via_request(&mut mcp, "sk-test-key").await?; @@ -225,7 +226,7 @@ async fn get_auth_status_with_api_key_refresh_requested() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; login_with_api_key_via_request(&mut mcp, "sk-test-key").await?; @@ -281,7 +282,7 @@ async fn get_auth_status_omits_token_after_permanent_refresh_failure() -> Result .await; let refresh_url = format!("{}/oauth/token", server.uri()); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -363,7 +364,7 @@ async fn get_auth_status_omits_token_after_proactive_refresh_failure() -> Result .await; let refresh_url = format!("{}/oauth/token", server.uri()); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -430,7 +431,7 @@ async fn get_auth_status_returns_token_after_proactive_refresh_recovery() -> Res .await; let refresh_url = format!("{}/oauth/token", server.uri()); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -507,7 +508,7 @@ async fn login_api_key_rejected_when_forced_chatgpt() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml_forced_login(codex_home.path(), "chatgpt")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/conversation_summary.rs b/codex-rs/app-server/tests/suite/conversation_summary.rs index eb534e517f..4ca483c883 100644 --- a/codex-rs/app-server/tests/suite/conversation_summary.rs +++ b/codex-rs/app-server/tests/suite/conversation_summary.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_rollout; use app_test_support::rollout_path; use app_test_support::to_response; @@ -93,7 +93,7 @@ async fn get_conversation_summary_by_thread_id_reads_rollout() -> Result<()> { ))?, ); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -212,7 +212,7 @@ async fn get_conversation_summary_by_relative_rollout_path_resolves_from_codex_h let relative_path = rollout_path.strip_prefix(codex_home.path())?.to_path_buf(); let expected = expected_summary(thread_id, normalized_canonical_path(rollout_path)?); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/fuzzy_file_search.rs b/codex-rs/app-server/tests/suite/fuzzy_file_search.rs index f508e0c987..2c30d05fde 100644 --- a/codex-rs/app-server/tests/suite/fuzzy_file_search.rs +++ b/codex-rs/app-server/tests/suite/fuzzy_file_search.rs @@ -1,6 +1,6 @@ use anyhow::Result; use anyhow::anyhow; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use codex_app_server_protocol::FuzzyFileSearchSessionCompletedNotification; use codex_app_server_protocol::FuzzyFileSearchSessionUpdatedNotification; use codex_app_server_protocol::JSONRPCResponse; @@ -44,15 +44,15 @@ shell_snapshot = false ) } -async fn initialized_mcp(codex_home: &TempDir) -> Result { +async fn initialized_mcp(codex_home: &TempDir) -> Result { create_config_toml(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; Ok(mcp) } async fn wait_for_session_updated( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, session_id: &str, query: &str, file_expectation: FileExpectation, @@ -99,7 +99,7 @@ async fn wait_for_session_updated( } async fn wait_for_session_completed( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, session_id: &str, ) -> Result { let description = format!("session completion for sessionId={session_id}"); @@ -140,7 +140,7 @@ async fn wait_for_session_completed( } async fn assert_update_request_fails_for_missing_session( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, session_id: &str, query: &str, ) -> Result<()> { @@ -161,7 +161,7 @@ async fn assert_update_request_fails_for_missing_session( } async fn assert_no_session_updates_for( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, session_id: &str, grace_period: std::time::Duration, duration: std::time::Duration, @@ -236,7 +236,7 @@ async fn test_fuzzy_file_search_sorts_and_includes_indices() -> Result<()> { .to_string(); // Start MCP server and initialize. - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let root_path = root.path().to_string_lossy().to_string(); @@ -302,7 +302,7 @@ async fn test_fuzzy_file_search_accepts_cancellation_token() -> Result<()> { std::fs::write(root.path().join("alpha.txt"), "contents")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let root_path = root.path().to_string_lossy().to_string(); diff --git a/codex-rs/app-server/tests/suite/v2/account.rs b/codex-rs/app-server/tests/suite/v2/account.rs index 8eb55e8715..1b95bc7d5b 100644 --- a/codex-rs/app-server/tests/suite/v2/account.rs +++ b/codex-rs/app-server/tests/suite/v2/account.rs @@ -1,6 +1,6 @@ use anyhow::Result; use anyhow::bail; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::ChatGptAuthFixture; @@ -201,7 +201,8 @@ async fn logout_account_removes_auth_and_notifies() -> Result<()> { )?; assert!(codex_home.path().join("auth.json").exists()); - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let id = mcp.send_logout_account_request().await?; @@ -268,7 +269,8 @@ async fn set_auth_token_updates_account_and_notifies() -> Result<()> { .chatgpt_account_id(WORKSPACE_ID_EMBEDDED), )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let set_id = mcp @@ -342,7 +344,8 @@ async fn account_read_refresh_token_is_noop_in_external_mode() -> Result<()> { .chatgpt_account_id(WORKSPACE_ID_EMBEDDED), )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let set_id = mcp @@ -401,7 +404,7 @@ async fn account_read_refresh_token_is_noop_in_external_mode() -> Result<()> { } async fn respond_to_refresh_request( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, access_token: &str, chatgpt_account_id: &str, chatgpt_plan_type: Option<&str>, @@ -467,7 +470,8 @@ async fn external_auth_refreshes_on_unauthorized() -> Result<()> { .chatgpt_account_id(WORKSPACE_ID_REFRESHED), )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let set_id = mcp @@ -574,7 +578,8 @@ async fn external_auth_refresh_error_fails_turn() -> Result<()> { .chatgpt_account_id(WORKSPACE_ID_INITIAL), )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let set_id = mcp @@ -697,7 +702,8 @@ async fn external_auth_refresh_mismatched_workspace_fails_turn() -> Result<()> { .chatgpt_account_id(WORKSPACE_ID_DISALLOWED), )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let set_id = mcp @@ -813,7 +819,8 @@ async fn external_auth_refresh_invalid_access_token_fails_turn() -> Result<()> { .chatgpt_account_id(WORKSPACE_ID_INITIAL), )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let set_id = mcp @@ -906,7 +913,7 @@ async fn login_account_api_key_succeeds_and_notifies() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), CreateConfigTomlParams::default())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -960,7 +967,7 @@ async fn login_account_api_key_rejected_when_forced_chatgpt() -> Result<()> { }, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -990,7 +997,7 @@ async fn login_account_chatgpt_rejected_when_forced_api() -> Result<()> { }, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_login_account_chatgpt_request().await?; @@ -1023,7 +1030,7 @@ async fn login_account_chatgpt_device_code_returns_error_when_disabled() -> Resu mock_device_code_usercode_failure(&mock_server, /*status*/ 404).await; let issuer = mock_server.uri(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -1088,7 +1095,7 @@ async fn login_account_chatgpt_device_code_succeeds_and_notifies() -> Result<()> mock_device_code_oauth_token(&mock_server, &id_token).await; let issuer = mock_server.uri(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -1165,7 +1172,7 @@ async fn login_account_chatgpt_device_code_failure_notifies_without_account_upda mock_device_code_token_failure(&mock_server, /*status*/ 500).await; let issuer = mock_server.uri(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -1240,7 +1247,7 @@ async fn login_account_chatgpt_device_code_can_be_cancelled() -> Result<()> { mock_device_code_token_failure(&mock_server, /*status*/ 404).await; let issuer = mock_server.uri(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -1313,7 +1320,7 @@ async fn login_account_chatgpt_start_can_be_cancelled() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), CreateConfigTomlParams::default())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_login_account_chatgpt_request().await?; @@ -1379,7 +1386,7 @@ async fn set_auth_token_cancels_active_chatgpt_login() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), CreateConfigTomlParams::default())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Initiate the ChatGPT login flow @@ -1454,7 +1461,7 @@ async fn login_account_chatgpt_includes_forced_workspace_query_param() -> Result }, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_login_account_chatgpt_request().await?; @@ -1491,7 +1498,7 @@ async fn login_account_chatgpt_includes_forced_workspace_allowlist_query_param() }, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_login_account_chatgpt_request().await?; @@ -1530,7 +1537,8 @@ async fn get_account_no_auth() -> Result<()> { }, )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let params = GetAccountParams { @@ -1561,7 +1569,7 @@ async fn get_account_with_api_key() -> Result<()> { }, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -1605,7 +1613,7 @@ async fn get_account_when_auth_not_required() -> Result<()> { }, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let params = GetAccountParams { @@ -1646,7 +1654,7 @@ region = "us-west-2" }, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let params = GetAccountParams { @@ -1687,7 +1695,8 @@ async fn get_account_with_chatgpt() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let params = GetAccountParams { @@ -1747,7 +1756,7 @@ async fn get_account_omits_chatgpt_after_permanent_refresh_failure() -> Result<( .await; let refresh_url = format!("{}/oauth/token", server.uri()); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -1813,7 +1822,8 @@ async fn get_account_with_chatgpt_missing_plan_claim_returns_unknown() -> Result AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let params = GetAccountParams { diff --git a/codex-rs/app-server/tests/suite/v2/app_list.rs b/codex-rs/app-server/tests/suite/v2/app_list.rs index 039fffe5cd..ea7ff8fa4b 100644 --- a/codex-rs/app-server/tests/suite/v2/app_list.rs +++ b/codex-rs/app-server/tests/suite/v2/app_list.rs @@ -8,7 +8,7 @@ use std::time::Duration; use anyhow::Result; use anyhow::bail; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use axum::Json; @@ -63,7 +63,7 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60); #[tokio::test] async fn list_apps_returns_empty_when_connectors_disabled() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; @@ -124,7 +124,7 @@ async fn list_apps_returns_empty_with_api_key_auth() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -186,7 +186,7 @@ async fn list_apps_returns_empty_when_workspace_codex_plugins_disabled() -> Resu AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -245,7 +245,7 @@ async fn list_apps_uses_thread_feature_flag_when_thread_id_is_provided() -> Resu AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let start_request = mcp @@ -355,7 +355,7 @@ async fn list_apps_keeps_apps_with_app_only_tools_accessible() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -428,7 +428,7 @@ enabled = false AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -547,7 +547,7 @@ async fn list_apps_emits_updates_and_returns_after_both_lists_load() -> Result<( AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -687,7 +687,7 @@ async fn list_apps_waits_for_accessible_data_before_emitting_directory_updates() AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -793,7 +793,7 @@ async fn list_apps_does_not_emit_empty_interim_updates() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -902,7 +902,7 @@ async fn list_apps_paginates_results() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let first_request = mcp @@ -1022,7 +1022,7 @@ async fn list_apps_force_refetch_preserves_previous_cache_on_failure() -> Result AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let initial_request = mcp @@ -1148,7 +1148,7 @@ async fn list_apps_force_refetch_patches_updates_from_cached_snapshots() -> Resu AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let warm_request = mcp @@ -1371,7 +1371,7 @@ async fn experimental_feature_enablement_set_refreshes_apps_list_when_apps_turn_ AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let disable_request = mcp @@ -1438,7 +1438,7 @@ async fn experimental_feature_enablement_set_refreshes_apps_list_when_apps_turn_ } async fn read_app_list_updated_notification( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { let notification = timeout( DEFAULT_TIMEOUT, diff --git a/codex-rs/app-server/tests/suite/v2/attestation.rs b/codex-rs/app-server/tests/suite/v2/attestation.rs index a662755be9..567f37397a 100644 --- a/codex-rs/app-server/tests/suite/v2/attestation.rs +++ b/codex-rs/app-server/tests/suite/v2/attestation.rs @@ -1,7 +1,7 @@ use anyhow::Result; use anyhow::bail; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use codex_app_server_protocol::AttestationGenerateResponse; @@ -76,7 +76,8 @@ async fn attestation_generate_round_trip_adds_header_to_responses_websocket_hand AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; let initialized = timeout( DEFAULT_READ_TIMEOUT, mcp.initialize_with_capabilities( diff --git a/codex-rs/app-server/tests/suite/v2/client_metadata.rs b/codex-rs/app-server/tests/suite/v2/client_metadata.rs index 2e6ede73d7..873a1cc044 100644 --- a/codex-rs/app-server/tests/suite/v2/client_metadata.rs +++ b/codex-rs/app-server/tests/suite/v2/client_metadata.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_parented_rollout_with_source; use app_test_support::create_fake_rollout; use app_test_support::to_response; @@ -59,7 +59,7 @@ async fn turn_start_forwards_client_metadata_to_responses_request_v2() -> Result /*supports_websockets*/ false, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -152,7 +152,7 @@ async fn turn_start_sends_fork_lineage_in_turn_metadata_for_thread_fork_v2() -> /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let ThreadForkResponse { thread, .. } = @@ -236,7 +236,7 @@ async fn review_start_sends_parent_lineage_in_turn_metadata_for_thread_fork_v2() /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let ThreadForkResponse { thread, .. } = @@ -333,7 +333,7 @@ async fn turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2() - parent_thread_id, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_req = mcp @@ -423,7 +423,7 @@ async fn turn_steer_updates_client_metadata_on_follow_up_responses_request_v2() /*supports_websockets*/ false, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -548,7 +548,7 @@ async fn turn_start_forwards_client_metadata_to_responses_websocket_request_body /*supports_websockets*/ true, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -650,7 +650,7 @@ supports_websockets = {supports_websockets} } async fn fork_fake_rollout_thread( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, source_thread_id: String, ) -> Result { let fork_req = mcp diff --git a/codex-rs/app-server/tests/suite/v2/collaboration_mode_list.rs b/codex-rs/app-server/tests/suite/v2/collaboration_mode_list.rs index f5914f0449..31dd810fbd 100644 --- a/codex-rs/app-server/tests/suite/v2/collaboration_mode_list.rs +++ b/codex-rs/app-server/tests/suite/v2/collaboration_mode_list.rs @@ -8,7 +8,7 @@ use std::time::Duration; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::CollaborationModeListParams; use codex_app_server_protocol::CollaborationModeListResponse; @@ -28,7 +28,7 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60); #[tokio::test] async fn list_collaboration_modes_returns_presets() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; diff --git a/codex-rs/app-server/tests/suite/v2/command_exec.rs b/codex-rs/app-server/tests/suite/v2/command_exec.rs index 9757767c9f..7cad6c0388 100644 --- a/codex-rs/app-server/tests/suite/v2/command_exec.rs +++ b/codex-rs/app-server/tests/suite/v2/command_exec.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; use base64::Engine; @@ -42,7 +42,7 @@ async fn command_exec_without_streams_can_be_terminated() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let process_id = "sleep-1".to_string(); @@ -92,7 +92,7 @@ async fn command_exec_without_process_id_keeps_buffered_compatibility() -> Resul let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -140,7 +140,7 @@ async fn command_exec_env_overrides_merge_with_server_environment_and_support_un let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[("COMMAND_EXEC_BASELINE", Some("server"))], ) @@ -198,7 +198,7 @@ async fn command_exec_accepts_permission_profile() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -249,7 +249,7 @@ async fn command_exec_permission_profile_starts_selected_network_proxy() -> Resu codex_home.path(), /*default_permissions*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -297,7 +297,7 @@ async fn command_exec_permission_profile_does_not_reuse_default_network_proxy() let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; insert_networked_permission_profile_config(codex_home.path(), Some("networked"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -355,7 +355,7 @@ async fn command_exec_permission_profile_project_roots_use_command_cwd() -> Resu ":workspace_roots" = "write" "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -406,7 +406,7 @@ async fn command_exec_returns_error_when_local_environment_is_disabled() -> Resu let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(CODEX_EXEC_SERVER_URL_ENV_VAR, Some("none"))], ) @@ -445,7 +445,7 @@ async fn command_exec_rejects_sandbox_policy_with_permission_profile() -> Result let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -483,7 +483,7 @@ async fn command_exec_rejects_disable_timeout_with_timeout_ms() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -521,7 +521,7 @@ async fn command_exec_rejects_disable_output_cap_with_output_bytes_cap() -> Resu let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -559,7 +559,7 @@ async fn command_exec_rejects_negative_timeout_ms() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -597,7 +597,7 @@ async fn command_exec_without_process_id_rejects_streaming() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -635,7 +635,7 @@ async fn command_exec_non_streaming_respects_output_cap() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let command_request_id = mcp @@ -682,7 +682,7 @@ async fn command_exec_streaming_does_not_buffer_output() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let process_id = "stream-cap-1".to_string(); @@ -746,7 +746,7 @@ async fn command_exec_pipe_streams_output_and_accepts_write() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let process_id = "pipe-1".to_string(); @@ -822,7 +822,7 @@ async fn command_exec_tty_implies_streaming_and_reports_pty_output() -> Result<( let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let process_id = "tty-1".to_string(); @@ -893,7 +893,7 @@ async fn command_exec_tty_supports_initial_size_and_resize() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let process_id = "tty-size-1".to_string(); @@ -1062,7 +1062,7 @@ async fn command_exec_process_ids_are_connection_scoped_and_disconnect_terminate } async fn read_command_exec_delta( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { let notification = mcp .read_stream_until_notification_message("command/exec/outputDelta") @@ -1071,7 +1071,7 @@ async fn read_command_exec_delta( } async fn wait_for_command_exec_output_contains( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, process_id: &str, stream: CommandExecOutputStream, expected: &str, @@ -1094,7 +1094,7 @@ async fn wait_for_command_exec_output_contains( } async fn wait_for_command_exec_outputs_contains( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, process_id: &str, stdout_expected: &str, stderr_expected: &str, @@ -1112,7 +1112,7 @@ async fn wait_for_command_exec_outputs_contains( } enum CommandExecDeltaReader<'a> { - Mcp(&'a mut McpProcess), + Mcp(&'a mut TestAppServer), Websocket(&'a mut super::connection_handling_websocket::WsClient), } diff --git a/codex-rs/app-server/tests/suite/v2/compaction.rs b/codex-rs/app-server/tests/suite/v2/compaction.rs index ca46c88a28..690c6d8bff 100644 --- a/codex-rs/app-server/tests/suite/v2/compaction.rs +++ b/codex-rs/app-server/tests/suite/v2/compaction.rs @@ -9,7 +9,7 @@ use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use app_test_support::write_mock_responses_config_toml; @@ -82,7 +82,7 @@ async fn auto_compaction_local_emits_started_and_completed_items() -> Result<()> COMPACT_PROMPT, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_thread(&mut mcp).await?; @@ -162,7 +162,8 @@ async fn auto_compaction_remote_emits_started_and_completed_items() -> Result<() AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_thread(&mut mcp).await?; @@ -268,7 +269,7 @@ async fn thread_compact_start_triggers_compaction_and_returns_empty_response() - COMPACT_PROMPT, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_thread(&mut mcp).await?; @@ -318,7 +319,7 @@ async fn thread_compact_start_rejects_invalid_thread_id() -> Result<()> { COMPACT_PROMPT, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -354,7 +355,7 @@ async fn thread_compact_start_rejects_unknown_thread_id() -> Result<()> { COMPACT_PROMPT, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -374,7 +375,7 @@ async fn thread_compact_start_rejects_unknown_thread_id() -> Result<()> { Ok(()) } -async fn start_thread(mcp: &mut McpProcess) -> Result { +async fn start_thread(mcp: &mut TestAppServer) -> Result { let thread_id = mcp .send_thread_start_request(ThreadStartParams { model: Some("mock-model".to_string()), @@ -390,7 +391,11 @@ async fn start_thread(mcp: &mut McpProcess) -> Result { Ok(thread.id) } -async fn send_turn_and_wait(mcp: &mut McpProcess, thread_id: &str, text: &str) -> Result { +async fn send_turn_and_wait( + mcp: &mut TestAppServer, + thread_id: &str, + text: &str, +) -> Result { let turn_id = mcp .send_turn_start_request(TurnStartParams { thread_id: thread_id.to_string(), @@ -412,7 +417,7 @@ async fn send_turn_and_wait(mcp: &mut McpProcess, thread_id: &str, text: &str) - Ok(turn.id) } -async fn wait_for_turn_completed(mcp: &mut McpProcess, turn_id: &str) -> Result<()> { +async fn wait_for_turn_completed(mcp: &mut TestAppServer, turn_id: &str) -> Result<()> { loop { let notification: JSONRPCNotification = timeout( DEFAULT_READ_TIMEOUT, @@ -428,7 +433,7 @@ async fn wait_for_turn_completed(mcp: &mut McpProcess, turn_id: &str) -> Result< } async fn wait_for_context_compaction_started( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { loop { let notification: JSONRPCNotification = timeout( @@ -445,7 +450,7 @@ async fn wait_for_context_compaction_started( } async fn wait_for_context_compaction_completed( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { loop { let notification: JSONRPCNotification = timeout( diff --git a/codex-rs/app-server/tests/suite/v2/config_rpc.rs b/codex-rs/app-server/tests/suite/v2/config_rpc.rs index 0bfd8ec876..dbcc12084a 100644 --- a/codex-rs/app-server/tests/suite/v2/config_rpc.rs +++ b/codex-rs/app-server/tests/suite/v2/config_rpc.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::test_path_buf_with_windows; use app_test_support::test_tmp_path_buf; use app_test_support::to_response; @@ -58,7 +58,7 @@ sandbox_mode = "workspace-write" let codex_home_path = codex_home.path().canonicalize()?; let user_file = AbsolutePathBuf::try_from(codex_home_path.join("config.toml"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -108,7 +108,7 @@ allowed_domains = ["example.com"] let codex_home_path = codex_home.path().canonicalize()?; let user_file = AbsolutePathBuf::try_from(codex_home_path.join("config.toml"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -179,7 +179,7 @@ forced_chatgpt_workspace_id = "{WORKSPACE_ID}" ), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -218,7 +218,7 @@ forced_chatgpt_workspace_id = ["{WORKSPACE_ID_A}", "{WORKSPACE_ID_B}"] ), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -260,7 +260,7 @@ location = { country = "US", city = "New York", timezone = "America/New_York" } "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -304,7 +304,7 @@ web_search = true "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -340,7 +340,7 @@ default_tools_approval_mode = "prompt" let codex_home_path = codex_home.path().canonicalize()?; let user_file = AbsolutePathBuf::try_from(codex_home_path.join("config.toml"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -427,7 +427,7 @@ width = 320 "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -474,7 +474,7 @@ model_reasoning_effort = "high" set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; let project_config = AbsolutePathBuf::try_from(project_config_dir)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -544,7 +544,7 @@ writable_roots = [{}] let managed_path_str = managed_path.display().to_string(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[( "CODEX_APP_SERVER_MANAGED_CONFIG_PATH", @@ -640,7 +640,7 @@ model = "gpt-old" "#, )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -701,7 +701,7 @@ async fn config_value_write_updates_desktop_settings() -> Result<()> { let codex_home = temp_dir.path().canonicalize()?; write_config(&temp_dir, "")?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let write_id = mcp @@ -750,7 +750,7 @@ model = "gpt-old" "#, )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let write_id = mcp @@ -798,7 +798,7 @@ model = "gpt-old" "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let write_id = mcp @@ -833,7 +833,7 @@ async fn config_batch_write_applies_multiple_edits() -> Result<()> { let codex_home = tmp_dir.path().canonicalize()?; write_config(&tmp_dir, "")?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let writable_root = test_tmp_path_buf(); @@ -905,7 +905,7 @@ model = "gpt-5.3-spark" "#, )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let batch_id = mcp @@ -961,7 +961,7 @@ async fn config_batch_write_updates_multiple_desktop_settings() -> Result<()> { let codex_home = tmp_dir.path().canonicalize()?; write_config(&tmp_dir, "")?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let batch_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs b/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs index 333086c722..79c5b801bd 100644 --- a/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs +++ b/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; @@ -51,7 +51,7 @@ async fn thread_start_injects_dynamic_tools_into_model_requests() -> Result<()> let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Use a minimal JSON schema so we can assert the tool payload round-trips. @@ -135,7 +135,7 @@ async fn thread_start_keeps_hidden_dynamic_tools_out_of_model_requests() -> Resu let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let dynamic_tool = DynamicToolSpec { @@ -208,7 +208,7 @@ async fn thread_start_rejects_hidden_dynamic_tools_without_namespace() -> Result let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let dynamic_tool = DynamicToolSpec { @@ -248,7 +248,7 @@ async fn thread_start_rejects_dynamic_tools_not_supported_by_responses() -> Resu let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let dynamic_tool = DynamicToolSpec { @@ -313,7 +313,7 @@ async fn dynamic_tool_call_round_trip_sends_text_content_items_to_model() -> Res let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let dynamic_tool = DynamicToolSpec { @@ -489,7 +489,7 @@ async fn dynamic_tool_call_round_trip_sends_content_items_to_model() -> Result<( let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let dynamic_tool = DynamicToolSpec { @@ -708,7 +708,7 @@ fn function_call_output_raw_output(body: &Value, call_id: &str) -> Option } async fn wait_for_dynamic_tool_started( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, call_id: &str, ) -> Result { loop { @@ -728,7 +728,7 @@ async fn wait_for_dynamic_tool_started( } async fn wait_for_dynamic_tool_completed( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, call_id: &str, ) -> Result { loop { diff --git a/codex-rs/app-server/tests/suite/v2/experimental_api.rs b/codex-rs/app-server/tests/suite/v2/experimental_api.rs index 45b2e45e3e..fc1100d17b 100644 --- a/codex-rs/app-server/tests/suite/v2/experimental_api.rs +++ b/codex-rs/app-server/tests/suite/v2/experimental_api.rs @@ -1,6 +1,6 @@ use anyhow::Result; use app_test_support::DEFAULT_CLIENT_NAME; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; use codex_app_server_protocol::AskForApproval; @@ -30,7 +30,7 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); #[tokio::test] async fn mock_experimental_method_requires_experimental_api_capability() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( @@ -61,7 +61,7 @@ async fn mock_experimental_method_requires_experimental_api_capability() -> Resu #[tokio::test] async fn realtime_conversation_start_requires_experimental_api_capability() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( @@ -99,7 +99,7 @@ async fn realtime_conversation_start_requires_experimental_api_capability() -> R #[tokio::test] async fn thread_memory_mode_set_requires_experimental_api_capability() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( @@ -133,7 +133,7 @@ async fn thread_memory_mode_set_requires_experimental_api_capability() -> Result #[tokio::test] async fn thread_settings_update_requires_experimental_api_capability() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( @@ -167,7 +167,7 @@ async fn thread_settings_update_requires_experimental_api_capability() -> Result #[tokio::test] async fn realtime_webrtc_start_requires_experimental_api_capability() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( @@ -210,7 +210,7 @@ async fn thread_start_mock_field_requires_experimental_api_capability() -> Resul let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( default_client_info(), @@ -248,7 +248,7 @@ async fn thread_start_without_dynamic_tools_allows_without_experimental_api_capa let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( default_client_info(), @@ -285,7 +285,7 @@ async fn thread_start_granular_approval_policy_requires_experimental_api_capabil let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let init = mcp .initialize_with_capabilities( default_client_info(), diff --git a/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs index 782dba3edf..60a0f94839 100644 --- a/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs +++ b/codex-rs/app-server/tests/suite/v2/experimental_feature_list.rs @@ -2,7 +2,7 @@ use std::time::Duration; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; @@ -50,7 +50,7 @@ async fn experimental_feature_list_returns_feature_metadata_with_stage() -> Resu )) .build() .await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; @@ -134,7 +134,7 @@ async fn experimental_feature_list_marks_apps_and_plugins_disabled_by_workspace_ .mount(&server) .await; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -195,7 +195,7 @@ memories = true "#, )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -229,7 +229,7 @@ memories = true #[tokio::test] async fn experimental_feature_list_rejects_unknown_thread_id() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -264,7 +264,7 @@ async fn experimental_feature_enablement_set_applies_to_global_and_thread_config let project_cwd = codex_home.path().join("project"); std::fs::create_dir_all(&project_cwd)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let actual = @@ -299,7 +299,7 @@ async fn experimental_feature_enablement_set_does_not_override_user_config() -> codex_home.path().join("config.toml"), "[features]\nmemories = false\n", )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let actual = set_experimental_feature_enablement( @@ -330,7 +330,7 @@ async fn experimental_feature_enablement_set_does_not_override_user_config() -> #[tokio::test] async fn experimental_feature_enablement_set_only_updates_named_features() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; set_experimental_feature_enablement(&mut mcp, BTreeMap::from([("apps".to_string(), true)])) @@ -411,7 +411,7 @@ async fn experimental_feature_enablement_set_only_updates_named_features() -> Re #[tokio::test] async fn experimental_feature_enablement_set_allows_remote_control() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let remote_control_enabled = false; let enablement = BTreeMap::from([("remote_control".to_string(), remote_control_enabled)]); @@ -429,7 +429,7 @@ async fn experimental_feature_enablement_set_allows_remote_control() -> Result<( #[tokio::test] async fn experimental_feature_enablement_set_empty_map_is_no_op() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; set_experimental_feature_enablement(&mut mcp, BTreeMap::from([("apps".to_string(), true)])) @@ -459,7 +459,7 @@ async fn experimental_feature_enablement_set_empty_map_is_no_op() -> Result<()> #[tokio::test] async fn experimental_feature_enablement_set_rejects_non_allowlisted_feature() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -493,7 +493,7 @@ async fn experimental_feature_enablement_set_rejects_non_allowlisted_feature() - } async fn set_experimental_feature_enablement( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, enablement: BTreeMap, ) -> Result { let request_id = mcp @@ -504,7 +504,7 @@ async fn set_experimental_feature_enablement( read_response(mcp, request_id).await } -async fn read_config(mcp: &mut McpProcess, cwd: Option) -> Result { +async fn read_config(mcp: &mut TestAppServer, cwd: Option) -> Result { let request_id = mcp .send_config_read_request(ConfigReadParams { include_layers: false, @@ -514,7 +514,7 @@ async fn read_config(mcp: &mut McpProcess, cwd: Option) -> Result(mcp: &mut McpProcess, request_id: i64) -> Result { +async fn read_response(mcp: &mut TestAppServer, request_id: i64) -> Result { let response: JSONRPCResponse = timeout( DEFAULT_TIMEOUT, mcp.read_stream_until_response_message(RequestId::Integer(request_id)), diff --git a/codex-rs/app-server/tests/suite/v2/external_agent_config.rs b/codex-rs/app-server/tests/suite/v2/external_agent_config.rs index 22790f94f7..fdb996c2f4 100644 --- a/codex-rs/app-server/tests/suite/v2/external_agent_config.rs +++ b/codex-rs/app-server/tests/suite/v2/external_agent_config.rs @@ -1,7 +1,7 @@ use std::time::Duration; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use app_test_support::write_mock_responses_config_toml; @@ -38,7 +38,8 @@ async fn external_agent_config_import_sends_completion_notification_for_sync_onl let codex_home = TempDir::new()?; let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -117,7 +118,8 @@ async fn external_agent_config_import_sends_completion_notification_for_local_pl let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -205,7 +207,8 @@ async fn external_agent_config_import_sends_completion_notification_after_pendin let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -283,7 +286,8 @@ async fn external_agent_config_import_creates_session_rollouts() -> Result<()> { let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -456,7 +460,8 @@ async fn external_agent_config_import_accepts_detected_session_payload_after_res let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -541,7 +546,8 @@ async fn external_agent_config_import_skips_already_imported_session_versions() let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -637,7 +643,8 @@ async fn external_agent_config_import_returns_before_background_session_import_f let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -766,7 +773,8 @@ async fn external_agent_config_import_rejects_undetected_session_paths() -> Resu let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -885,7 +893,8 @@ async fn external_agent_config_import_compacts_huge_session_before_first_follow_ let home_dir = codex_home.path().display().to_string(); let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("HOME", Some(home_dir.as_str()))]) + .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/fs.rs b/codex-rs/app-server/tests/suite/v2/fs.rs index 3ca511a482..1f04c847d2 100644 --- a/codex-rs/app-server/tests/suite/v2/fs.rs +++ b/codex-rs/app-server/tests/suite/v2/fs.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use base64::Engine; use base64::engine::general_purpose::STANDARD; @@ -36,14 +36,14 @@ const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(60); const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(10); const OPTIONAL_FS_CHANGE_TIMEOUT: Duration = Duration::from_secs(2); -async fn initialized_mcp(codex_home: &TempDir) -> Result { - let mut mcp = McpProcess::new(codex_home.path()).await?; +async fn initialized_mcp(codex_home: &TempDir) -> Result { + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; Ok(mcp) } async fn expect_error_message( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, request_id: i64, expected_message: &str, ) -> Result<()> { @@ -125,7 +125,7 @@ async fn fs_methods_return_error_when_local_environment_is_disabled() -> Result< let codex_home = TempDir::new()?; let absolute_file = codex_home.path().join("absolute.txt"); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(CODEX_EXEC_SERVER_URL_ENV_VAR, Some("none"))], ) @@ -853,7 +853,7 @@ fn fs_changed_notification(notification: JSONRPCNotification) -> Result Result> { match timeout( OPTIONAL_FS_CHANGE_TIMEOUT, diff --git a/codex-rs/app-server/tests/suite/v2/hooks_list.rs b/codex-rs/app-server/tests/suite/v2/hooks_list.rs index 7e0b39f0c6..b7270f317e 100644 --- a/codex-rs/app-server/tests/suite/v2/hooks_list.rs +++ b/codex-rs/app-server/tests/suite/v2/hooks_list.rs @@ -1,7 +1,7 @@ use std::time::Duration; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; @@ -135,7 +135,7 @@ async fn hooks_list_shows_discovered_hook() -> Result<()> { let cwd = TempDir::new()?; write_user_hook_config(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -211,7 +211,7 @@ async fn hooks_list_shows_discovered_plugin_hook() -> Result<()> { }"#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -270,7 +270,7 @@ async fn hooks_list_shows_plugin_hook_load_warnings() -> Result<()> { let cwd = TempDir::new()?; write_plugin_hook_config(codex_home.path(), "{ not-json")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -326,7 +326,7 @@ timeout = 5 )?; set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -408,7 +408,7 @@ async fn hooks_list_uses_root_repo_hooks_for_linked_worktrees() -> Result<()> { write_project_hook_config(&worktree_root.join(".codex"), "echo worktree hook")?; set_project_trust_level(codex_home.path(), &repo_root, TrustLevel::Trusted)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let list_id = mcp @@ -478,7 +478,7 @@ async fn config_batch_write_toggles_user_hook() -> Result<()> { let cwd = TempDir::new()?; write_user_hook_config(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -629,7 +629,7 @@ command = "python3 {hook_script_path}" ), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let hook_list_id = mcp @@ -879,7 +879,7 @@ command = "python3 {hook_script_path}" ), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let hook_list_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/initialize.rs b/codex-rs/app-server/tests/suite/v2/initialize.rs index 3a9b22cf16..0cf8504d3b 100644 --- a/codex-rs/app-server/tests/suite/v2/initialize.rs +++ b/codex-rs/app-server/tests/suite/v2/initialize.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; @@ -33,7 +33,7 @@ async fn initialize_uses_client_info_name_as_originator() -> Result<()> { let codex_home = TempDir::new()?; let expected_codex_home = AbsolutePathBuf::try_from(codex_home.path().canonicalize()?)?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let message = timeout( DEFAULT_READ_TIMEOUT, @@ -68,7 +68,7 @@ async fn initialize_probe_does_not_override_originator() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(responses).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let message = timeout( DEFAULT_READ_TIMEOUT, @@ -95,7 +95,7 @@ async fn initialize_codex_backend_does_not_override_originator() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(responses).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let message = timeout( DEFAULT_READ_TIMEOUT, @@ -123,7 +123,7 @@ async fn initialize_respects_originator_override_env_var() -> Result<()> { let codex_home = TempDir::new()?; let expected_codex_home = AbsolutePathBuf::try_from(codex_home.path().canonicalize()?)?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[( "CODEX_INTERNAL_ORIGINATOR_OVERRIDE", @@ -165,7 +165,7 @@ async fn initialize_rejects_invalid_client_name() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(responses).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[("CODEX_INTERNAL_ORIGINATOR_OVERRIDE", None)], ) @@ -200,7 +200,7 @@ async fn initialize_opt_out_notification_methods_filters_notifications() -> Resu let server = create_mock_responses_server_sequence_unchecked(responses).await; let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let message = timeout( DEFAULT_READ_TIMEOUT, @@ -282,7 +282,7 @@ async fn turn_start_notify_payload_includes_initialize_client_name() -> Result<( ), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout( DEFAULT_READ_TIMEOUT, mcp.initialize_with_client_info(ClientInfo { diff --git a/codex-rs/app-server/tests/suite/v2/marketplace_add.rs b/codex-rs/app-server/tests/suite/v2/marketplace_add.rs index 5f470f617d..7dc9f745af 100644 --- a/codex-rs/app-server/tests/suite/v2/marketplace_add.rs +++ b/codex-rs/app-server/tests/suite/v2/marketplace_add.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::MarketplaceAddParams; @@ -28,7 +28,7 @@ async fn marketplace_add_local_directory_source() -> Result<()> { r#"{"name":"sample"}"#, )?; std::fs::write(source.join("plugins/sample/marker.txt"), "local ref")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/marketplace_remove.rs b/codex-rs/app-server/tests/suite/v2/marketplace_remove.rs index dc438499f3..c6808b8271 100644 --- a/codex-rs/app-server/tests/suite/v2/marketplace_remove.rs +++ b/codex-rs/app-server/tests/suite/v2/marketplace_remove.rs @@ -2,7 +2,7 @@ use std::time::Duration; use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::MarketplaceRemoveParams; @@ -53,7 +53,7 @@ async fn marketplace_remove_deletes_config_and_installed_root() -> Result<()> { write_installed_marketplace(codex_home.path(), "debug")?; let installed_root = marketplace_install_root(codex_home.path()).join("debug"); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -91,7 +91,7 @@ async fn marketplace_remove_deletes_config_and_installed_root() -> Result<()> { async fn marketplace_remove_rejects_unknown_marketplace() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/marketplace_upgrade.rs b/codex-rs/app-server/tests/suite/v2/marketplace_upgrade.rs index 8660497da5..5ed1f2c6f5 100644 --- a/codex-rs/app-server/tests/suite/v2/marketplace_upgrade.rs +++ b/codex-rs/app-server/tests/suite/v2/marketplace_upgrade.rs @@ -4,7 +4,7 @@ use std::time::Duration; use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::MarketplaceUpgradeParams; @@ -127,7 +127,7 @@ fn expected_installed_root(codex_home: &Path, marketplace_name: &str) -> Result< } async fn send_marketplace_upgrade( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, marketplace_name: Option<&str>, ) -> Result { let request_id = mcp @@ -169,7 +169,7 @@ async fn marketplace_upgrade_all_configured_git_marketplaces() -> Result<()> { )?; disable_plugin_startup_tasks(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let debug_root = expected_installed_root(codex_home.path(), "debug")?; @@ -223,7 +223,7 @@ async fn marketplace_upgrade_named_marketplace_only() -> Result<()> { )?; disable_plugin_startup_tasks(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let tools_root = expected_installed_root(codex_home.path(), "tools")?; @@ -264,7 +264,7 @@ async fn marketplace_upgrade_returns_empty_roots_when_already_up_to_date() -> Re )?; disable_plugin_startup_tasks(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let first_response = send_marketplace_upgrade(&mut mcp, Some("debug")).await?; assert!(first_response.errors.is_empty()); @@ -292,7 +292,7 @@ async fn marketplace_upgrade_rejects_unknown_or_non_git_marketplace() -> Result< &configured_local_marketplace_update(&local_source.path().display().to_string()), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; for marketplace_name in ["missing", "local-only"] { diff --git a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs index db16aded20..37b7a10d9b 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use axum::Router; @@ -92,7 +92,7 @@ stream_max_retries = 0 AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -157,7 +157,7 @@ apps = true AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs b/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs index 7357e3f178..8d0387ee97 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use axum::Json; @@ -116,7 +116,7 @@ async fn mcp_server_elicitation_round_trip() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs index 32f98a1738..2c34684ada 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use std::time::Duration; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; use app_test_support::write_mock_responses_config_toml; @@ -67,7 +67,7 @@ url = "{mcp_server_url}/mcp" )); std::fs::write(config_path, config_toml)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -132,7 +132,7 @@ async fn mcp_server_status_list_uses_thread_project_local_config() -> Result<()> std::fs::create_dir_all(workspace.path().join(".git"))?; set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -336,7 +336,7 @@ url = "{mcp_server_url}/mcp" )); std::fs::write(config_path, config_toml)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -401,7 +401,7 @@ url = "{underscore_server_url}/mcp" )); std::fs::write(config_path, config_toml)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/mcp_tool.rs b/codex-rs/app-server/tests/suite/v2/mcp_tool.rs index 9ba09b078f..10c8a6ef04 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_tool.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_tool.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use std::time::Duration; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::to_response; @@ -94,7 +94,7 @@ url = "{mcp_server_url}/mcp" )); std::fs::write(config_path, config_toml)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -161,7 +161,7 @@ url = "{mcp_server_url}/mcp" #[tokio::test] async fn mcp_server_tool_call_returns_error_for_unknown_thread() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -212,7 +212,7 @@ url = "{mcp_server_url}/mcp" )); std::fs::write(config_path, config_toml)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -322,7 +322,7 @@ url = "{mcp_server_url}/mcp" )); std::fs::write(config_path, config_toml)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -442,7 +442,7 @@ url = "{mcp_server_url}/mcp" )); std::fs::write(config_path, config_toml)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -683,7 +683,7 @@ async fn start_mcp_server() -> Result<(String, JoinHandle<()>)> { } async fn wait_for_mcp_tool_call_completed( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, call_id: &str, ) -> Result { loop { diff --git a/codex-rs/app-server/tests/suite/v2/memory_reset.rs b/codex-rs/app-server/tests/suite/v2/memory_reset.rs index 7b2eddeb53..07e16a3ba6 100644 --- a/codex-rs/app-server/tests/suite/v2/memory_reset.rs +++ b/codex-rs/app-server/tests/suite/v2/memory_reset.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use chrono::Utc; use codex_app_server_protocol::JSONRPCResponse; @@ -36,7 +36,7 @@ async fn memory_reset_clears_memory_files_and_rows_preserves_threads() -> Result let thread_id = seed_stage1_output(&state_db, codex_home.path()).await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/model_list.rs b/codex-rs/app-server/tests/suite/v2/model_list.rs index 94dcca4377..778577f857 100644 --- a/codex-rs/app-server/tests/suite/v2/model_list.rs +++ b/codex-rs/app-server/tests/suite/v2/model_list.rs @@ -2,7 +2,7 @@ use std::time::Duration; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use app_test_support::write_models_cache; @@ -95,7 +95,7 @@ fn expected_visible_models() -> Vec { async fn list_models_returns_all_models_with_large_limit() -> Result<()> { let codex_home = TempDir::new()?; write_models_cache(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; @@ -129,7 +129,7 @@ async fn list_models_returns_all_models_with_large_limit() -> Result<()> { async fn list_models_includes_hidden_models() -> Result<()> { let codex_home = TempDir::new()?; write_models_cache(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; @@ -214,7 +214,8 @@ openai_base_url = "{server_uri}/v1" AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -256,7 +257,7 @@ openai_base_url = "{server_uri}/v1" async fn list_models_pagination_works() -> Result<()> { let codex_home = TempDir::new()?; write_models_cache(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; @@ -305,7 +306,7 @@ async fn list_models_pagination_works() -> Result<()> { async fn list_models_rejects_invalid_cursor() -> Result<()> { let codex_home = TempDir::new()?; write_models_cache(codex_home.path())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; diff --git a/codex-rs/app-server/tests/suite/v2/model_provider_capabilities_read.rs b/codex-rs/app-server/tests/suite/v2/model_provider_capabilities_read.rs index 529f238798..4143e825ea 100644 --- a/codex-rs/app-server/tests/suite/v2/model_provider_capabilities_read.rs +++ b/codex-rs/app-server/tests/suite/v2/model_provider_capabilities_read.rs @@ -1,7 +1,7 @@ use std::time::Duration; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::ModelProviderCapabilitiesReadParams; @@ -16,7 +16,7 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30); #[tokio::test] async fn read_default_provider_capabilities() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -46,7 +46,7 @@ async fn read_amazon_bedrock_provider_capabilities() -> Result<()> { r#"model_provider = "amazon-bedrock" "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/output_schema.rs b/codex-rs/app-server/tests/suite/v2/output_schema.rs index 86d12edcfc..bcd95068b3 100644 --- a/codex-rs/app-server/tests/suite/v2/output_schema.rs +++ b/codex-rs/app-server/tests/suite/v2/output_schema.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::RequestId; @@ -32,7 +32,7 @@ async fn turn_start_accepts_output_schema_v2() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -115,7 +115,7 @@ async fn turn_start_output_schema_is_per_turn_v2() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp diff --git a/codex-rs/app-server/tests/suite/v2/permission_profile_list.rs b/codex-rs/app-server/tests/suite/v2/permission_profile_list.rs index 5939065280..63e92af578 100644 --- a/codex-rs/app-server/tests/suite/v2/permission_profile_list.rs +++ b/codex-rs/app-server/tests/suite/v2/permission_profile_list.rs @@ -1,7 +1,7 @@ use std::time::Duration; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::PermissionProfileListParams; @@ -41,7 +41,7 @@ description = "Inspect without writes." "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -108,7 +108,7 @@ description = "Project-scoped profile." )?; set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let first_request_id = mcp @@ -181,7 +181,7 @@ description = "Project-scoped profile." )?; set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -221,7 +221,7 @@ description = "Project-scoped profile." } async fn read_response( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, request_id: i64, ) -> Result { let response: JSONRPCResponse = timeout( diff --git a/codex-rs/app-server/tests/suite/v2/plan_item.rs b/codex-rs/app-server/tests/suite/v2/plan_item.rs index 63f7d9fba2..b5464231da 100644 --- a/codex-rs/app-server/tests/suite/v2/plan_item.rs +++ b/codex-rs/app-server/tests/suite/v2/plan_item.rs @@ -1,7 +1,7 @@ use anyhow::Result; use anyhow::anyhow; use anyhow::bail; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; use codex_app_server_protocol::ItemCompletedNotification; @@ -53,7 +53,7 @@ async fn plan_mode_uses_proposed_plan_block_for_plan_item() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let turn = start_plan_mode_turn(&mut mcp).await?; @@ -111,7 +111,7 @@ async fn plan_mode_without_proposed_plan_does_not_emit_plan_item() -> Result<()> let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let _turn = start_plan_mode_turn(&mut mcp).await?; @@ -127,7 +127,7 @@ async fn plan_mode_without_proposed_plan_does_not_emit_plan_item() -> Result<()> Ok(()) } -async fn start_plan_mode_turn(mcp: &mut McpProcess) -> Result { +async fn start_plan_mode_turn(mcp: &mut TestAppServer) -> Result { let thread_req = mcp .send_thread_start_request(ThreadStartParams { model: Some("mock-model".to_string()), @@ -170,7 +170,7 @@ async fn start_plan_mode_turn(mcp: &mut McpProcess) -> Result Result<( Vec, Vec, diff --git a/codex-rs/app-server/tests/suite/v2/plugin_install.rs b/codex-rs/app-server/tests/suite/v2/plugin_install.rs index 61582438af..2de63f1451 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_install.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_install.rs @@ -9,7 +9,7 @@ use anyhow::Result; use anyhow::bail; use app_test_support::ChatGptAuthFixture; use app_test_support::DEFAULT_CLIENT_NAME; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::start_analytics_events_server; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; @@ -72,7 +72,7 @@ const TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS: &str = #[tokio::test] async fn plugin_install_rejects_relative_marketplace_paths() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -99,7 +99,7 @@ async fn plugin_install_rejects_relative_marketplace_paths() -> Result<()> { #[tokio::test] async fn plugin_install_rejects_missing_install_source() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -128,7 +128,7 @@ async fn plugin_install_rejects_missing_install_source() -> Result<()> { #[tokio::test] async fn plugin_install_rejects_multiple_install_sources() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -165,7 +165,7 @@ async fn plugin_install_rejects_remote_marketplace_when_plugins_are_disabled() - plugins = false "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -231,7 +231,7 @@ async fn plugin_install_writes_remote_plugin_to_cloud_and_cache() -> Result<()> ) .await; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS, Some("1"))], ) @@ -302,7 +302,7 @@ async fn plugin_install_rejects_missing_remote_bundle_url() -> Result<()> { .await; mount_empty_remote_installed_plugins(&server).await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = send_remote_plugin_install_request(&mut mcp, REMOTE_PLUGIN_ID).await?; @@ -343,7 +343,7 @@ async fn plugin_install_rejects_plain_http_remote_bundle_url() -> Result<()> { mount_remote_plugin_detail(&server, REMOTE_PLUGIN_ID, "1.2.3", Some(&bundle_url)).await; mount_empty_remote_installed_plugins(&server).await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = send_remote_plugin_install_request(&mut mcp, REMOTE_PLUGIN_ID).await?; @@ -389,7 +389,7 @@ async fn plugin_install_rejects_invalid_remote_release_version() -> Result<()> { .await; mount_empty_remote_installed_plugins(&server).await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = send_remote_plugin_install_request(&mut mcp, REMOTE_PLUGIN_ID).await?; @@ -421,7 +421,7 @@ async fn plugin_install_rejects_invalid_remote_release_version() -> Result<()> { async fn plugin_install_rejects_invalid_remote_plugin_name() -> Result<()> { let codex_home = TempDir::new()?; write_remote_plugin_catalog_config(codex_home.path(), "https://example.invalid/backend-api/")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -464,7 +464,7 @@ async fn plugin_install_rejects_remote_plugin_disabled_by_admin_before_download( .await; mount_empty_remote_installed_plugins(&server).await; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS, Some("1"))], ) @@ -544,7 +544,7 @@ async fn plugin_install_rejects_when_workspace_codex_plugins_disabled() -> Resul .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -573,7 +573,7 @@ async fn plugin_install_rejects_when_workspace_codex_plugins_disabled() -> Resul #[tokio::test] async fn plugin_install_returns_invalid_request_for_missing_marketplace_file() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -614,7 +614,7 @@ async fn plugin_install_returns_invalid_request_for_not_available_plugin() -> Re let marketplace_path = AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -664,7 +664,7 @@ async fn plugin_install_returns_invalid_request_for_disallowed_product_plugin() AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; let mut mcp = - McpProcess::new_with_args(codex_home.path(), &["--session-source", "atlas"]).await?; + TestAppServer::new_with_args(codex_home.path(), &["--session-source", "atlas"]).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -713,7 +713,7 @@ async fn plugin_install_tracks_analytics_event() -> Result<()> { let marketplace_path = AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -768,7 +768,7 @@ async fn plugin_install_tracks_remote_plugin_analytics_event() -> Result<()> { mount_remote_plugin_install(&server, REMOTE_PLUGIN_ID).await; mount_backend_analytics_events(&server).await; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS, Some("1"))], ) @@ -820,7 +820,7 @@ async fn plugin_install_errors_when_remote_bundle_download_fails() -> Result<()> mount_empty_remote_installed_plugins(&server).await; mount_remote_plugin_install(&server, REMOTE_PLUGIN_ID).await; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS, Some("1"))], ) @@ -920,7 +920,7 @@ async fn plugin_install_returns_apps_needing_auth() -> Result<()> { let marketplace_path = AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let directory_requests_before_install = server_control.directory_request_count(); @@ -1007,7 +1007,7 @@ async fn plugin_install_filters_disallowed_apps_needing_auth() -> Result<()> { let marketplace_path = AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let directory_requests_before_install = warm_app_directory_cache(&mut mcp, &server_control, "Alpha").await?; @@ -1080,7 +1080,7 @@ async fn plugin_install_makes_bundled_mcp_servers_available_to_followup_requests let marketplace_path = AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1141,7 +1141,7 @@ impl AppsServerControl { } async fn warm_app_directory_cache( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, server_control: &AppsServerControl, expected_app_name: &str, ) -> Result { @@ -1584,7 +1584,7 @@ async fn mount_remote_plugin_install_after_cache_write( } async fn send_remote_plugin_install_request( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, remote_plugin_id: &str, ) -> Result { mcp.send_plugin_install_request(PluginInstallParams { diff --git a/codex-rs/app-server/tests/suite/v2/plugin_list.rs b/codex-rs/app-server/tests/suite/v2/plugin_list.rs index e5ca8ea62d..99e0dec515 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_list.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_list.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::Result; use anyhow::bail; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use codex_app_server_protocol::JSONRPCResponse; @@ -81,7 +81,7 @@ async fn plugin_list_skips_invalid_marketplace_file_and_reports_error() -> Resul std::fs::write(marketplace_path.as_path(), "{not json")?; let home = codex_home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home.as_str())), @@ -146,7 +146,7 @@ enabled = true "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -295,7 +295,7 @@ enabled = true "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -320,7 +320,7 @@ enabled = true #[tokio::test] async fn plugin_list_rejects_relative_cwds() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -397,7 +397,7 @@ async fn plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_ std::fs::write(invalid_marketplace_path.as_path(), "{not json")?; let home = codex_home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home.as_str())), @@ -514,7 +514,7 @@ async fn plugin_list_returns_empty_when_workspace_codex_plugins_disabled() -> Re .await; let home = codex_home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_without_managed_config_with_env( + let mut mcp = TestAppServer::new_without_managed_config_with_env( codex_home.path(), &[ ("HOME", Some(home.as_str())), @@ -605,7 +605,7 @@ async fn plugin_list_reuses_cached_workspace_codex_plugins_setting() -> Result<( .await; let home = codex_home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_without_managed_config_with_env( + let mut mcp = TestAppServer::new_without_managed_config_with_env( codex_home.path(), &[ ("HOME", Some(home.as_str())), @@ -690,7 +690,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab )?; let home = codex_home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home.as_str())), @@ -803,7 +803,7 @@ async fn plugin_list_accepts_omitted_cwds() -> Result<()> { }"#, )?; let home = codex_home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home.as_str())), @@ -863,7 +863,7 @@ async fn plugin_list_returns_share_context_for_shared_local_plugin() -> Result<( &AbsolutePathBuf::try_from(plugin_root)?, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -955,7 +955,7 @@ enabled = false "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1102,7 +1102,7 @@ enabled = false let workspace_default = TempDir::new()?; let home = codex_home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home.as_str())), @@ -1196,7 +1196,7 @@ async fn plugin_list_returns_plugin_interface_with_absolute_asset_paths() -> Res }"##, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1309,7 +1309,7 @@ async fn plugin_list_accepts_legacy_string_default_prompt() -> Result<()> { }"##, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1397,7 +1397,7 @@ enabled = true "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1500,7 +1500,7 @@ async fn app_server_startup_remote_plugin_sync_runs_once() -> Result<()> { .join(STARTUP_REMOTE_PLUGIN_SYNC_MARKER_FILE); { - let mut mcp = McpProcess::new_with_plugin_startup_tasks(codex_home.path()).await?; + let mut mcp = TestAppServer::new_with_plugin_startup_tasks(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; wait_for_path_exists(&marker_path).await?; @@ -1539,7 +1539,7 @@ async fn app_server_startup_remote_plugin_sync_runs_once() -> Result<()> { assert!(config.contains(r#"[plugins."linear@openai-curated"]"#)); { - let mut mcp = McpProcess::new_with_plugin_startup_tasks(codex_home.path()).await?; + let mut mcp = TestAppServer::new_with_plugin_startup_tasks(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; } @@ -1591,7 +1591,7 @@ async fn app_server_startup_sync_downloads_remote_installed_plugin_bundles() -> let installed_path = codex_home .path() .join("plugins/cache/openai-curated-remote/linear/1.2.3"); - let mut mcp = McpProcess::new_with_env_and_plugin_startup_tasks( + let mut mcp = TestAppServer::new_with_env_and_plugin_startup_tasks( codex_home.path(), &[(TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS, Some("1"))], ) @@ -1669,7 +1669,7 @@ async fn plugin_list_sync_upgrades_and_removes_remote_installed_plugin_bundles() .path() .join("plugins/cache/openai-curated-remote/stale"); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS, Some("1"))], ) @@ -1839,7 +1839,7 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() - .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1963,7 +1963,7 @@ async fn plugin_list_includes_openai_curated_remote_collection_when_requested() mount_remote_installed_plugins(&server, "WORKSPACE", empty_remote_installed_plugins_body()) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2050,7 +2050,7 @@ async fn plugin_list_fail_opens_openai_curated_remote_collection_errors() -> Res mount_remote_installed_plugins(&server, "WORKSPACE", empty_remote_installed_plugins_body()) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2092,7 +2092,7 @@ async fn plugin_list_does_not_query_openai_curated_remote_collection_by_default( AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2145,7 +2145,7 @@ async fn plugin_list_vertical_kind_noops_when_remote_plugin_enabled() -> Result< AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2199,7 +2199,7 @@ async fn plugin_list_does_not_append_global_remote_when_marketplace_kinds_are_ex AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2276,7 +2276,7 @@ plugin_sharing = true mount_remote_installed_plugins(&server, "GLOBAL", &global_installed_body).await; mount_remote_installed_plugins(&server, "WORKSPACE", &workspace_installed_body).await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2365,7 +2365,7 @@ plugin_sharing = false mount_remote_installed_plugins(&server, "WORKSPACE", empty_remote_installed_plugins_body()) .await; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS, Some("1"))], ) @@ -2439,7 +2439,7 @@ async fn plugin_list_fetches_workspace_directory_kind_without_remote_plugin_flag mount_remote_plugin_list(&server, "WORKSPACE", &workspace_plugin_body).await; mount_remote_installed_plugins(&server, "WORKSPACE", &workspace_installed_body).await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2556,7 +2556,7 @@ async fn plugin_list_fetches_shared_with_me_kind() -> Result<()> { mount_remote_installed_plugins(&server, "GLOBAL", empty_remote_installed_plugins_body()).await; mount_remote_installed_plugins(&server, "WORKSPACE", &workspace_installed_body).await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2713,7 +2713,7 @@ plugin_sharing = false AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2848,7 +2848,7 @@ async fn plugin_list_marks_remote_plugin_disabled_by_admin() -> Result<()> { .await; } - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2908,7 +2908,7 @@ remote_plugin = true AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2944,7 +2944,7 @@ async fn plugin_list_fetches_featured_plugin_ids_without_chatgpt_auth() -> Resul .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -2983,7 +2983,7 @@ async fn plugin_list_uses_warmed_featured_plugin_ids_cache_on_first_request() -> .mount(&server) .await; - let mut mcp = McpProcess::new_with_plugin_startup_tasks(codex_home.path()).await?; + let mut mcp = TestAppServer::new_with_plugin_startup_tasks(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; wait_for_featured_plugin_request_count(&server, /*expected_count*/ 1).await?; diff --git a/codex-rs/app-server/tests/suite/v2/plugin_read.rs b/codex-rs/app-server/tests/suite/v2/plugin_read.rs index 26a8548166..1f1c217ee5 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_read.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_read.rs @@ -5,7 +5,7 @@ use std::time::Duration; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use axum::Json; @@ -64,7 +64,7 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); #[tokio::test] async fn plugin_read_rejects_missing_read_source() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -93,7 +93,7 @@ async fn plugin_read_rejects_missing_read_source() -> Result<()> { #[tokio::test] async fn plugin_read_rejects_multiple_read_sources() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -190,7 +190,7 @@ plugins = true .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -297,7 +297,7 @@ async fn plugin_read_returns_share_context_for_shared_remote_plugin() -> Result< .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; for remote_marketplace_name in [ @@ -478,7 +478,7 @@ async fn plugin_read_reads_remote_plugin_details_when_remote_plugin_enabled() -> .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -563,7 +563,7 @@ async fn plugin_skill_read_reads_remote_skill_contents_when_remote_plugin_enable .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -615,7 +615,7 @@ async fn plugin_read_maps_missing_remote_plugin_to_invalid_request() -> Result<( .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -667,7 +667,7 @@ remote_plugin = true AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -697,7 +697,7 @@ remote_plugin = true async fn plugin_read_rejects_invalid_remote_plugin_name() -> Result<()> { let codex_home = TempDir::new()?; write_remote_plugin_catalog_config(codex_home.path(), "https://example.invalid/backend-api/")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -755,7 +755,7 @@ enabled = true )?; write_installed_plugin(&codex_home, "openai-curated", "demo-plugin")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let marketplace_path = @@ -855,7 +855,7 @@ async fn plugin_read_returns_share_context_for_shared_local_plugin() -> Result<( .expect(1) .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -981,7 +981,7 @@ async fn plugin_read_keeps_remote_version_when_share_principals_are_missing() -> .expect(1) .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1037,7 +1037,7 @@ async fn plugin_read_falls_back_to_local_share_context_without_remote_auth() -> let plugin_path = AbsolutePathBuf::try_from(repo_root.path().join("demo-plugin"))?; write_plugin_share_local_path_mapping(codex_home.path(), "plugins_123", &plugin_path)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1095,7 +1095,7 @@ async fn plugin_read_fails_on_malformed_share_mapping() -> Result<()> { "not valid json\n", )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1287,7 +1287,7 @@ enabled = false )?; write_installed_plugin(&codex_home, "codex-curated", "demo-plugin")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let marketplace_path = @@ -1458,7 +1458,7 @@ async fn plugin_read_returns_app_needs_auth() -> Result<()> { let marketplace_path = AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1525,7 +1525,7 @@ async fn plugin_read_accepts_legacy_string_default_prompt() -> Result<()> { )?; write_plugins_enabled_config(&codex_home)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1587,7 +1587,7 @@ async fn plugin_read_describes_uninstalled_git_source_without_cloning() -> Resul )?; write_plugins_enabled_config(&codex_home)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1650,7 +1650,7 @@ async fn plugin_read_returns_invalid_request_when_plugin_is_missing() -> Result< )?; write_plugins_enabled_config(&codex_home)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1703,7 +1703,7 @@ async fn plugin_read_returns_invalid_request_when_plugin_manifest_is_missing() - )?; write_plugins_enabled_config(&codex_home)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/plugin_share.rs b/codex-rs/app-server/tests/suite/v2/plugin_share.rs index 69f93f0b7e..99fc1289d4 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_share.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_share.rs @@ -4,7 +4,7 @@ use std::time::Duration; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use codex_app_server_protocol::JSONRPCError; @@ -102,7 +102,7 @@ async fn plugin_share_save_uploads_local_plugin() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let expected_plugin_path = AbsolutePathBuf::try_from(plugin_path.clone())?; let request_id = mcp @@ -251,7 +251,7 @@ async fn plugin_share_save_forwards_access_policy() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let expected_plugin_path = AbsolutePathBuf::try_from(plugin_path)?; let request_id = mcp @@ -304,7 +304,7 @@ async fn plugin_share_save_rejects_listed_discoverability() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request( @@ -359,7 +359,7 @@ plugin_sharing = false AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request( @@ -404,7 +404,7 @@ async fn plugin_share_rejects_workspace_targets_from_client() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request( @@ -482,7 +482,7 @@ async fn plugin_share_save_rejects_access_policy_for_existing_plugin() -> Result AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request( @@ -555,7 +555,7 @@ async fn plugin_share_list_returns_created_workspace_plugins() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request("plugin/share/list", Some(json!({}))) @@ -626,7 +626,7 @@ async fn plugin_share_checkout_adds_personal_marketplace_entry() -> Result<()> { mount_empty_remote_installed_plugins(&server, "WORKSPACE").await; let home_env = home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home_env.as_str())), @@ -789,7 +789,7 @@ async fn plugin_share_checkout_rejects_non_share_remote_plugin() -> Result<()> { mount_empty_remote_installed_plugins(&server, "GLOBAL").await; let home_env = home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home_env.as_str())), @@ -880,7 +880,7 @@ async fn plugin_share_checkout_cleans_up_path_when_marketplace_update_fails() -> mount_empty_remote_installed_plugins(&server, "WORKSPACE").await; let home_env = home.path().to_string_lossy().into_owned(); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("HOME", Some(home_env.as_str())), @@ -983,7 +983,7 @@ async fn plugin_share_update_targets_updates_share_targets() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request( @@ -1065,7 +1065,7 @@ plugin_sharing = false AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request( @@ -1114,7 +1114,7 @@ async fn plugin_share_delete_removes_created_workspace_plugin() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp .send_raw_request( diff --git a/codex-rs/app-server/tests/suite/v2/plugin_uninstall.rs b/codex-rs/app-server/tests/suite/v2/plugin_uninstall.rs index 4feede934d..46299d1186 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_uninstall.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_uninstall.rs @@ -4,7 +4,7 @@ use anyhow::Result; use anyhow::bail; use app_test_support::ChatGptAuthFixture; use app_test_support::DEFAULT_CLIENT_NAME; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::start_analytics_events_server; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; @@ -42,7 +42,7 @@ enabled = true "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let params = PluginUninstallParams { @@ -100,7 +100,7 @@ async fn plugin_uninstall_tracks_analytics_event() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -161,7 +161,7 @@ async fn plugin_uninstall_rejects_remote_plugin_when_plugins_are_disabled() -> R plugins = false "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -230,7 +230,7 @@ async fn plugin_uninstall_writes_remote_plugin_to_cloud_when_remote_plugin_enabl )); std::fs::create_dir_all(legacy_remote_plugin_cache_root.join("local/.codex-plugin"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -302,7 +302,7 @@ async fn plugin_uninstall_uses_detail_scope_for_cache_namespace() -> Result<()> .join("plugins/cache/openai-curated-remote/linear"); std::fs::create_dir_all(global_cache_root.join("1.0.0/.codex-plugin"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -376,7 +376,7 @@ async fn plugin_uninstall_accepts_workspace_remote_plugin_id_shape() -> Result<( r#"{"name":"skill-improver","version":"1.0.0"}"#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -425,7 +425,7 @@ async fn plugin_uninstall_rejects_before_post_when_remote_detail_fetch_fails() - )); std::fs::create_dir_all(legacy_remote_plugin_cache_root.join("local/.codex-plugin"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -467,7 +467,7 @@ async fn plugin_uninstall_rejects_remote_plugin_id_with_spaces_before_network_ca codex_home.path(), &format!("{}/backend-api/", server.uri()), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -502,7 +502,7 @@ async fn plugin_uninstall_rejects_invalid_remote_plugin_id_before_network_call() codex_home.path(), &format!("{}/backend-api/", server.uri()), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -537,7 +537,7 @@ async fn plugin_uninstall_rejects_empty_remote_plugin_id() -> Result<()> { codex_home.path(), &format!("{}/backend-api/", server.uri()), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/process_exec.rs b/codex-rs/app-server/tests/suite/v2/process_exec.rs index d273257e6e..ac4f6eeda3 100644 --- a/codex-rs/app-server/tests/suite/v2/process_exec.rs +++ b/codex-rs/app-server/tests/suite/v2/process_exec.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_sequence_unchecked; use codex_app_server_protocol::ProcessExitedNotification; use codex_app_server_protocol::ProcessKillParams; @@ -108,7 +108,7 @@ async fn process_spawn_returns_error_when_local_environment_is_disabled() -> Res let codex_home = TempDir::new()?; let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; create_config_toml(codex_home.path(), &server.uri(), "never")?; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(CODEX_EXEC_SERVER_URL_ENV_VAR, Some("none"))], ) @@ -230,10 +230,10 @@ async fn process_kill_terminates_running_process() -> Result<()> { Ok(()) } -async fn initialized_mcp(codex_home: &Path) -> Result<(MockServer, McpProcess)> { +async fn initialized_mcp(codex_home: &Path) -> Result<(MockServer, TestAppServer)> { let server = create_mock_responses_server_sequence_unchecked(Vec::new()).await; create_config_toml(codex_home, &server.uri(), "never")?; - let mut mcp = McpProcess::new(codex_home).await?; + let mut mcp = TestAppServer::new(codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; Ok((server, mcp)) } @@ -257,7 +257,7 @@ fn process_spawn_params( }) } -async fn read_process_exited(mcp: &mut McpProcess) -> Result { +async fn read_process_exited(mcp: &mut TestAppServer) -> Result { let notification = mcp .read_stream_until_notification_message("process/exited") .await?; diff --git a/codex-rs/app-server/tests/suite/v2/rate_limits.rs b/codex-rs/app-server/tests/suite/v2/rate_limits.rs index b15960c1e9..0eca044991 100644 --- a/codex-rs/app-server/tests/suite/v2/rate_limits.rs +++ b/codex-rs/app-server/tests/suite/v2/rate_limits.rs @@ -1,6 +1,6 @@ use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use codex_app_server_protocol::AddCreditsNudgeCreditType; @@ -37,7 +37,8 @@ const INTERNAL_ERROR_CODE: i64 = -32603; async fn get_account_rate_limits_requires_auth() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_get_account_rate_limits_request().await?; @@ -62,7 +63,7 @@ async fn get_account_rate_limits_requires_auth() -> Result<()> { async fn get_account_rate_limits_requires_chatgpt_auth() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -153,7 +154,8 @@ async fn get_account_rate_limits_returns_snapshot() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_get_account_rate_limits_request().await?; @@ -238,7 +240,8 @@ async fn get_account_rate_limits_returns_snapshot() -> Result<()> { async fn send_add_credits_nudge_email_requires_auth() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -267,7 +270,7 @@ async fn send_add_credits_nudge_email_requires_auth() -> Result<()> { async fn send_add_credits_nudge_email_requires_chatgpt_auth() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -321,7 +324,8 @@ async fn send_add_credits_nudge_email_posts_expected_body() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -364,7 +368,8 @@ async fn send_add_credits_nudge_email_maps_cooldown() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -407,7 +412,8 @@ async fn send_add_credits_nudge_email_surfaces_backend_failure() -> Result<()> { .mount(&server) .await; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -437,7 +443,7 @@ async fn send_add_credits_nudge_email_surfaces_backend_failure() -> Result<()> { Ok(()) } -async fn login_with_api_key(mcp: &mut McpProcess, api_key: &str) -> Result<()> { +async fn login_with_api_key(mcp: &mut TestAppServer, api_key: &str) -> Result<()> { let request_id = mcp.send_login_account_api_key_request(api_key).await?; let response: JSONRPCResponse = timeout( DEFAULT_READ_TIMEOUT, diff --git a/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs b/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs index 975819dc7f..f88fb54929 100644 --- a/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs +++ b/codex-rs/app-server/tests/suite/v2/realtime_conversation.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::create_shell_command_sse_response; @@ -191,7 +191,7 @@ struct RealtimeSidebandScript { } struct RealtimeE2eHarness { - mcp: McpProcess, + mcp: TestAppServer, _codex_home: TempDir, main_loop_responses_server: MockServer, realtime_server: WebSocketTestServer, @@ -280,7 +280,7 @@ impl RealtimeE2eHarness { sandbox, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -539,7 +539,7 @@ async fn realtime_conversation_streams_v2_notifications() -> Result<()> { StartupContextConfig::Generated, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -788,7 +788,7 @@ async fn realtime_text_output_modality_requests_text_output_and_final_transcript StartupContextConfig::Generated, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -890,7 +890,7 @@ async fn realtime_list_voices_returns_supported_names() -> Result<()> { StartupContextConfig::Generated, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -962,7 +962,7 @@ async fn realtime_conversation_stop_emits_closed_notification() -> Result<()> { StartupContextConfig::Generated, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -1058,7 +1058,7 @@ async fn realtime_webrtc_start_emits_sdp_notification() -> Result<()> { StartupContextConfig::Override("startup context"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -1973,7 +1973,7 @@ async fn realtime_webrtc_start_surfaces_backend_error() -> Result<()> { StartupContextConfig::Override("startup context"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; login_with_api_key(&mut mcp, "sk-test-key").await?; @@ -2034,7 +2034,7 @@ async fn realtime_conversation_requires_feature_flag() -> Result<()> { StartupContextConfig::Generated, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let thread_start_request_id = mcp @@ -2074,7 +2074,10 @@ async fn realtime_conversation_requires_feature_flag() -> Result<()> { Ok(()) } -async fn read_notification(mcp: &mut McpProcess, method: &str) -> Result { +async fn read_notification( + mcp: &mut TestAppServer, + method: &str, +) -> Result { let notification = timeout( DEFAULT_TIMEOUT, mcp.read_stream_until_notification_message(method), @@ -2086,7 +2089,7 @@ async fn read_notification(mcp: &mut McpProcess, method: &s Ok(serde_json::from_value(params)?) } -async fn login_with_api_key(mcp: &mut McpProcess, api_key: &str) -> Result<()> { +async fn login_with_api_key(mcp: &mut TestAppServer, api_key: &str) -> Result<()> { let request_id = mcp.send_login_account_api_key_request(api_key).await?; let response: JSONRPCResponse = timeout( DEFAULT_TIMEOUT, @@ -2100,7 +2103,7 @@ async fn login_with_api_key(mcp: &mut McpProcess, api_key: &str) -> Result<()> { } async fn wait_for_started_command_execution( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { loop { let started = read_notification::(mcp, "item/started").await?; @@ -2111,7 +2114,7 @@ async fn wait_for_started_command_execution( } async fn wait_for_completed_command_execution( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { loop { let completed = diff --git a/codex-rs/app-server/tests/suite/v2/remote_control.rs b/codex-rs/app-server/tests/suite/v2/remote_control.rs index d71f05bf58..d15f2d4187 100644 --- a/codex-rs/app-server/tests/suite/v2/remote_control.rs +++ b/codex-rs/app-server/tests/suite/v2/remote_control.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::Context; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use app_test_support::write_mock_responses_config_toml_with_chatgpt_base_url; @@ -29,7 +29,7 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); #[tokio::test] async fn remote_control_disable_returns_disabled_status() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_remote_control_disable_request().await?; @@ -50,7 +50,7 @@ async fn remote_control_disable_returns_disabled_status() -> Result<()> { #[tokio::test] async fn remote_control_status_read_returns_disabled_status() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_remote_control_status_read_request().await?; @@ -72,7 +72,7 @@ async fn remote_control_status_read_returns_disabled_status() -> Result<()> { async fn remote_control_enable_returns_connecting_status() -> Result<()> { let codex_home = TempDir::new()?; let _backend = BlockingRemoteControlBackend::start(codex_home.path()).await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_remote_control_enable_request().await?; @@ -94,7 +94,7 @@ async fn remote_control_enable_returns_connecting_status() -> Result<()> { async fn remote_control_status_read_returns_connecting_status_after_enable() -> Result<()> { let codex_home = TempDir::new()?; let mut backend = BlockingRemoteControlBackend::start(codex_home.path()).await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_remote_control_enable_request().await?; diff --git a/codex-rs/app-server/tests/suite/v2/request_permissions.rs b/codex-rs/app-server/tests/suite/v2/request_permissions.rs index 8fd44d26f0..bea78901f5 100644 --- a/codex-rs/app-server/tests/suite/v2/request_permissions.rs +++ b/codex-rs/app-server/tests/suite/v2/request_permissions.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::create_request_permissions_sse_response; @@ -30,7 +30,7 @@ async fn request_permissions_round_trip() -> Result<()> { let server = create_mock_responses_server_sequence(responses).await; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/request_user_input.rs b/codex-rs/app-server/tests/suite/v2/request_user_input.rs index a80b5527a7..da61d34112 100644 --- a/codex-rs/app-server/tests/suite/v2/request_user_input.rs +++ b/codex-rs/app-server/tests/suite/v2/request_user_input.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::create_request_user_input_sse_response; @@ -32,7 +32,7 @@ async fn request_user_input_round_trip() -> Result<()> { let server = create_mock_responses_server_sequence(responses).await; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/review.rs b/codex-rs/app-server/tests/suite/v2/review.rs index bb89a68077..427a9f288b 100644 --- a/codex-rs/app-server/tests/suite/v2/review.rs +++ b/codex-rs/app-server/tests/suite/v2/review.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::create_mock_responses_server_sequence; @@ -59,7 +59,7 @@ async fn review_start_runs_review_turn_and_emits_code_review_item() -> Result<() let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_default_thread(&mut mcp).await?; @@ -173,7 +173,7 @@ async fn review_start_exec_approval_item_id_matches_command_execution_item() -> let codex_home = TempDir::new()?; create_config_toml_with_approval_policy(codex_home.path(), &server.uri(), "untrusted")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_default_thread(&mut mcp).await?; @@ -256,7 +256,7 @@ async fn review_start_rejects_empty_base_branch() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_default_thread(&mut mcp).await?; @@ -299,7 +299,7 @@ async fn review_start_with_detached_delivery_returns_new_thread_id() -> Result<( let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_default_thread(&mut mcp).await?; @@ -377,7 +377,7 @@ async fn review_start_rejects_empty_commit_sha() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_default_thread(&mut mcp).await?; @@ -412,7 +412,7 @@ async fn review_start_rejects_empty_custom_instructions() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_default_thread(&mut mcp).await?; @@ -443,7 +443,7 @@ async fn review_start_rejects_empty_custom_instructions() -> Result<()> { Ok(()) } -async fn start_default_thread(mcp: &mut McpProcess) -> Result { +async fn start_default_thread(mcp: &mut TestAppServer) -> Result { let thread_req = mcp .send_thread_start_request(ThreadStartParams { model: Some("mock-model".to_string()), @@ -464,7 +464,7 @@ async fn start_default_thread(mcp: &mut McpProcess) -> Result { Ok(thread.id) } -async fn materialize_thread_rollout(mcp: &mut McpProcess, thread_id: &str) -> Result<()> { +async fn materialize_thread_rollout(mcp: &mut TestAppServer, thread_id: &str) -> Result<()> { let turn_req = mcp .send_turn_start_request(TurnStartParams { thread_id: thread_id.to_string(), diff --git a/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs b/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs index a7f1bf3729..c5b70b2334 100644 --- a/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs +++ b/codex-rs/app-server/tests/suite/v2/safety_check_downgrade.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::CodexErrorInfo; use codex_app_server_protocol::ErrorNotification; @@ -48,7 +48,7 @@ async fn openai_model_header_mismatch_emits_model_rerouted_notification_v2() -> let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -115,7 +115,7 @@ async fn cyber_policy_response_emits_typed_error_notification_v2() -> Result<()> let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -192,7 +192,7 @@ async fn response_model_field_mismatch_emits_model_rerouted_notification_v2_when let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -261,7 +261,7 @@ async fn model_verification_emits_typed_notification_and_warning_v2() -> Result< let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -310,7 +310,7 @@ async fn model_verification_emits_typed_notification_and_warning_v2() -> Result< } async fn collect_turn_notifications_and_validate_no_warning_item( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { let mut rerouted = None; @@ -352,7 +352,7 @@ async fn collect_turn_notifications_and_validate_no_warning_item( } async fn collect_model_verification_notifications_and_validate_no_warning_item( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { let mut verification = None; @@ -403,7 +403,7 @@ async fn collect_model_verification_notifications_and_validate_no_warning_item( } async fn collect_cyber_policy_error_and_validate_no_reroute( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { let mut error = None; diff --git a/codex-rs/app-server/tests/suite/v2/skills_list.rs b/codex-rs/app-server/tests/suite/v2/skills_list.rs index 2ad0b2006a..8405aaf308 100644 --- a/codex-rs/app-server/tests/suite/v2/skills_list.rs +++ b/codex-rs/app-server/tests/suite/v2/skills_list.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::Context; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; @@ -44,7 +44,7 @@ fn write_skill(root: &TempDir, name: &str) -> Result<()> { } async fn expect_skills_changed_notification( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, timeout_duration: Duration, ) -> Result<()> { let notification = timeout( @@ -242,7 +242,7 @@ async fn skills_list_loads_remote_installed_plugin_skills_from_cache() -> Result .mount(&server) .await; } - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let stale_skills_list_request_id = mcp @@ -366,7 +366,7 @@ async fn skills_list_excludes_plugin_skills_when_workspace_codex_plugins_disable .mount(&server) .await; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -412,7 +412,7 @@ async fn skills_list_skips_cwd_roots_when_environment_disabled() -> Result<()> { "---\nname: repo-skill\ndescription: from repo root\n---\n\n# Body\n", )?; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[(CODEX_EXEC_SERVER_URL_ENV_VAR, Some("none"))], ) @@ -456,7 +456,7 @@ async fn skills_list_accepts_relative_cwds() -> Result<()> { let relative_cwd = std::path::PathBuf::from("relative-cwd"); std::fs::create_dir_all(codex_home.path().join(&relative_cwd))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -484,7 +484,7 @@ async fn skills_list_preserves_requested_cwd_order() -> Result<()> { let first_cwd = TempDir::new()?; let second_cwd = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -520,7 +520,7 @@ async fn skills_list_uses_cached_result_until_force_reload() -> Result<()> { let codex_home = TempDir::new()?; let cwd = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; // Seed the cwd cache before the cwd-local skill exists. @@ -606,7 +606,7 @@ async fn skills_extra_roots_set_updates_process_runtime_roots() -> Result<()> { "---\nname: runtime-skill\ndescription: runtime skill\n---\n\n# Body\n", )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let set_request_id = mcp @@ -712,7 +712,7 @@ async fn skills_extra_roots_set_updates_process_runtime_roots() -> Result<()> { ); drop(mcp); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let skills_request_id = mcp .send_skills_list_request(SkillsListParams { @@ -749,7 +749,7 @@ async fn skills_changed_notification_is_emitted_after_skill_change() -> Result<( write_skill(&codex_home, "demo")?; let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[(CODEX_EXEC_SERVER_URL_ENV_VAR, None)]) + TestAppServer::new_with_env(codex_home.path(), &[(CODEX_EXEC_SERVER_URL_ENV_VAR, None)]) .await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let initial_skills_request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/thread_archive.rs b/codex-rs/app-server/tests/suite/v2/thread_archive.rs index 6eadb1131b..7bf6fc9bc5 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_archive.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_archive.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_rollout; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; @@ -38,7 +38,7 @@ async fn thread_archive_requires_materialized_rollout() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Start a thread. @@ -221,7 +221,7 @@ async fn thread_archive_archives_spawned_descendants() -> Result<()> { ) .await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let archive_id = mcp @@ -342,7 +342,7 @@ async fn thread_archive_succeeds_when_descendant_archive_fails() -> Result<()> { .join(child_rollout_path.file_name().expect("rollout file name")); std::fs::create_dir_all(&archived_child_path)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let archive_id = mcp @@ -446,7 +446,7 @@ async fn thread_archive_succeeds_when_spawned_descendant_is_missing() -> Result< ) .await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let archive_id = mcp @@ -499,7 +499,7 @@ async fn thread_archive_clears_stale_subscriptions_before_resume() -> Result<()> let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -539,7 +539,7 @@ async fn thread_archive_clears_stale_subscriptions_before_resume() -> Result<()> .await??; primary.clear_message_buffer(); - let mut secondary = McpProcess::new(codex_home.path()).await?; + let mut secondary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, secondary.initialize()).await??; let archive_id = primary diff --git a/codex-rs/app-server/tests/suite/v2/thread_fork.rs b/codex-rs/app-server/tests/suite/v2/thread_fork.rs index 5c4c637f4b..57a24fc9e7 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_fork.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_fork.rs @@ -1,6 +1,6 @@ use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_rollout; use app_test_support::create_fake_rollout_with_token_usage; use app_test_support::create_mock_responses_server_repeating_assistant; @@ -83,7 +83,7 @@ async fn thread_fork_creates_new_thread_and_emits_started() -> Result<()> { ); let original_contents = std::fs::read_to_string(&original_path)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp @@ -241,7 +241,7 @@ async fn thread_fork_can_load_source_by_path() -> Result<()> { "rollout-2025-01-05T12-00-00-{conversation_id}.jsonl" )); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp @@ -281,7 +281,7 @@ async fn thread_fork_emits_restored_token_usage_before_next_turn() -> Result<()> Some("mock_provider"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp @@ -335,7 +335,7 @@ async fn thread_fork_can_exclude_turns_and_skip_restored_token_usage() -> Result Some("mock_provider"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp @@ -386,7 +386,7 @@ async fn thread_fork_tracks_thread_initialized_analytics() -> Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp @@ -422,7 +422,7 @@ async fn thread_fork_rejects_unmaterialized_thread() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -476,7 +476,7 @@ async fn thread_fork_with_empty_path_uses_thread_id() -> Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp @@ -550,7 +550,7 @@ async fn thread_fork_surfaces_cloud_requirements_load_errors() -> Result<()> { )?; let refresh_token_url = format!("{}/oauth/token", server.uri()); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -613,7 +613,7 @@ async fn thread_fork_ephemeral_remains_pathless_and_omits_listing() -> Result<() /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs b/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs index c543b50498..809358cc84 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::RequestId; @@ -36,7 +36,7 @@ async fn thread_inject_items_adds_raw_response_items_to_thread_history() -> Resu let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -152,7 +152,7 @@ async fn thread_inject_items_adds_raw_response_items_after_a_turn() -> Result<() let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp diff --git a/codex-rs/app-server/tests/suite/v2/thread_list.rs b/codex-rs/app-server/tests/suite/v2/thread_list.rs index 77e2e281f9..0b49647128 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_list.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_list.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_parented_rollout_with_source; use app_test_support::create_fake_rollout; use app_test_support::create_fake_rollout_with_source; @@ -48,14 +48,14 @@ use uuid::Uuid; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); -async fn init_mcp(codex_home: &Path) -> Result { - let mut mcp = McpProcess::new(codex_home).await?; +async fn init_mcp(codex_home: &Path) -> Result { + let mut mcp = TestAppServer::new(codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; Ok(mcp) } async fn list_threads( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, cursor: Option, limit: Option, providers: Option>, @@ -75,7 +75,7 @@ async fn list_threads( } async fn list_threads_with_sort( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, cursor: Option, limit: Option, providers: Option>, diff --git a/codex-rs/app-server/tests/suite/v2/thread_loaded_list.rs b/codex-rs/app-server/tests/suite/v2/thread_loaded_list.rs index 245cfafb49..ef0c5d986b 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_loaded_list.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_loaded_list.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use codex_app_server_protocol::JSONRPCResponse; @@ -21,7 +21,7 @@ async fn thread_loaded_list_returns_loaded_thread_ids() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_thread(&mut mcp).await?; @@ -51,7 +51,7 @@ async fn thread_loaded_list_paginates() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let first = start_thread(&mut mcp).await?; @@ -122,7 +122,7 @@ stream_max_retries = 0 ) } -async fn start_thread(mcp: &mut McpProcess) -> Result { +async fn start_thread(mcp: &mut TestAppServer) -> Result { let req_id = mcp .send_thread_start_request(ThreadStartParams { model: Some("gpt-5.2".to_string()), diff --git a/codex-rs/app-server/tests/suite/v2/thread_memory_mode_set.rs b/codex-rs/app-server/tests/suite/v2/thread_memory_mode_set.rs index bf9bba7b2f..c93e966cc3 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_memory_mode_set.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_memory_mode_set.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_rollout; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; @@ -27,7 +27,7 @@ async fn thread_memory_mode_set_updates_loaded_thread_state() -> Result<()> { create_config_toml(codex_home.path(), &server.uri())?; let state_db = init_state_db(codex_home.path()).await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -79,7 +79,7 @@ async fn thread_memory_mode_set_updates_stored_thread_state() -> Result<()> { )?; let thread_uuid = ThreadId::from_string(&thread_id)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; for mode in [ThreadMemoryMode::Disabled, ThreadMemoryMode::Enabled] { diff --git a/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs b/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs index c78e9b8152..fb61973466 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_metadata_update.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_rollout; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::rollout_path; @@ -41,7 +41,7 @@ async fn thread_metadata_update_patches_git_branch_and_returns_updated_thread() let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -136,7 +136,7 @@ async fn thread_metadata_update_rejects_empty_git_info_patch() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -182,7 +182,7 @@ async fn thread_metadata_update_rejects_ephemeral_thread() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -244,7 +244,7 @@ async fn thread_metadata_update_repairs_missing_sqlite_row_for_stored_thread() - /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let update_id = mcp @@ -309,7 +309,7 @@ async fn thread_metadata_update_repairs_loaded_thread_without_resetting_summary( ) .await; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -387,7 +387,7 @@ async fn thread_metadata_update_repairs_missing_sqlite_row_for_archived_thread() ); fs::rename(&archived_source, &archived_dest)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let update_id = mcp @@ -443,7 +443,7 @@ async fn thread_metadata_update_can_clear_stored_git_fields() -> Result<()> { )?; let _state_db = init_state_db(codex_home.path()).await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let update_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/thread_read.rs b/codex-rs/app-server/tests/suite/v2/thread_read.rs index df79aea9b4..773b85f4a5 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_read.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_read.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_fake_rollout_with_text_elements; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::rollout_path; @@ -104,7 +104,7 @@ async fn thread_read_returns_summary_without_turns() -> Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -159,7 +159,7 @@ async fn thread_read_can_include_turns() -> Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -217,7 +217,7 @@ async fn thread_turns_list_can_page_backward_and_forward() -> Result<()> { append_user_message(rollout_path.as_path(), "2025-01-05T12:01:00Z", "second")?; append_user_message(rollout_path.as_path(), "2025-01-05T12:02:00Z", "third")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -306,7 +306,7 @@ async fn thread_turns_list_supports_requested_items_view() -> Result<()> { append_agent_message(rollout_path.as_path(), "2025-01-05T12:01:00Z", "draft")?; append_agent_message(rollout_path.as_path(), "2025-01-05T12:02:00Z", "final")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let full = read_single_turn_items_view( @@ -608,7 +608,7 @@ async fn thread_read_can_return_archived_threads_by_id() -> Result<()> { archived_dir.join(active_rollout_path.file_name().expect("rollout file name")); std::fs::rename(&active_rollout_path, &archived_rollout_path)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -652,7 +652,7 @@ async fn thread_resume_initial_turns_page_matches_requested_turns_list_page() -> append_user_message(rollout_path.as_path(), "2025-01-05T12:01:00Z", "second")?; append_user_message(rollout_path.as_path(), "2025-01-05T12:02:00Z", "third")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let turns_list_id = mcp @@ -723,7 +723,7 @@ async fn thread_turns_list_rejects_cursor_when_anchor_turn_is_rolled_back() -> R append_user_message(rollout_path.as_path(), "2025-01-05T12:01:00Z", "second")?; append_user_message(rollout_path.as_path(), "2025-01-05T12:02:00Z", "third")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -790,7 +790,7 @@ async fn thread_read_returns_forked_from_id_for_forked_threads() -> Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let fork_id = mcp @@ -830,7 +830,7 @@ async fn thread_read_loaded_thread_returns_precomputed_path_before_materializati let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -890,7 +890,7 @@ async fn thread_name_set_is_reflected_in_read_list_and_resume() -> Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Set a user-facing thread title. @@ -1036,7 +1036,7 @@ async fn thread_read_include_turns_rejects_unmaterialized_loaded_thread() -> Res let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -1087,7 +1087,7 @@ async fn thread_turns_list_rejects_unmaterialized_loaded_thread() -> Result<()> let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -1141,7 +1141,7 @@ async fn thread_turns_items_list_returns_unsupported() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -1179,7 +1179,7 @@ async fn thread_read_reports_system_error_idle_flag_after_failed_turn() -> Resul let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -1289,7 +1289,7 @@ fn append_thread_rollback(path: &Path, timestamp: &str, num_turns: u32) -> std:: } async fn read_single_turn_items_view( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, thread_id: &str, items_view: Option, ) -> anyhow::Result { diff --git a/codex-rs/app-server/tests/suite/v2/thread_resume.rs b/codex-rs/app-server/tests/suite/v2/thread_resume.rs index e0263d77f3..ff5d537225 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_resume.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_resume.rs @@ -1,6 +1,6 @@ use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_apply_patch_sse_response; use app_test_support::create_fake_rollout; use app_test_support::create_fake_rollout_with_text_elements; @@ -146,7 +146,7 @@ async fn thread_resume_rejects_unmaterialized_thread() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Start a thread. @@ -193,7 +193,7 @@ async fn thread_resume_with_empty_path_uses_running_thread_id() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -262,7 +262,7 @@ async fn turn_start_updates_runtime_workspace_roots_for_loaded_thread() -> Resul let extra_root = extra_root_tmp.path().join("extra-root"); std::fs::create_dir_all(&extra_root)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -338,7 +338,7 @@ async fn thread_goal_get_rejects_unmaterialized_thread() -> Result<()> { config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -403,7 +403,7 @@ async fn thread_resume_tracks_thread_initialized_analytics() -> Result<()> { "user", )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -481,7 +481,7 @@ async fn thread_resume_returns_rollout_history() -> Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -653,7 +653,7 @@ async fn resume_redaction_fixture(client_name: Option<&str>) -> Result Result<()> { /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -805,7 +805,7 @@ async fn thread_resume_rejects_archived_session_by_id() -> Result<()> { archived_dir.join(active_rollout_path.file_name().expect("rollout file name")), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -844,7 +844,7 @@ async fn thread_resume_keeps_paused_goal_paused() -> Result<()> { config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -949,7 +949,7 @@ async fn thread_goal_set_preserves_budget_limited_same_objective() -> Result<()> config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -1048,7 +1048,7 @@ async fn thread_goal_set_persists_resumable_stopped_statuses() -> Result<()> { config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -1143,7 +1143,7 @@ async fn thread_goal_set_edits_objective_without_resetting_usage() -> Result<()> /*git_info*/ None, )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let goal_id = mcp @@ -1244,7 +1244,7 @@ async fn thread_goal_clear_deletes_goal_and_notifies() -> Result<()> { config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -1374,7 +1374,7 @@ async fn thread_resume_emits_restored_token_usage_before_next_turn() -> Result<( Some("mock_provider"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -1427,7 +1427,7 @@ async fn thread_resume_skips_restored_token_usage_when_turns_are_excluded() -> R Some("mock_provider"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let first_resume_id = mcp @@ -1535,7 +1535,7 @@ async fn thread_resume_token_usage_replay_ignores_stale_interrupted_tail_turn() format!("{persisted_rollout}{appended_rollout}\n"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -1659,7 +1659,7 @@ async fn thread_resume_token_usage_replay_can_belong_to_interrupted_turn() -> Re format!("{persisted_rollout}{appended_rollout}\n"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -1848,7 +1848,7 @@ stream_max_retries = 0 .mark_backfill_complete(/*last_watermark*/ None) .await?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let update_id = mcp @@ -1942,7 +1942,7 @@ async fn thread_resume_and_read_interrupt_incomplete_rollout_turn_when_thread_is format!("{persisted_rollout}{appended_rollout}\n"), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -2016,7 +2016,7 @@ async fn thread_resume_defers_updated_at_until_turn_start() -> Result<()> { let rollout = setup_rollout_fixture(codex_home.path(), &server.uri())?; let thread_id = rollout.conversation_id.clone(); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let read_id = mcp @@ -2114,7 +2114,7 @@ async fn thread_resume_keeps_in_flight_turn_streaming() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -2153,7 +2153,7 @@ async fn thread_resume_keeps_in_flight_turn_streaming() -> Result<()> { .await??; primary.clear_message_buffer(); - let mut secondary = McpProcess::new(codex_home.path()).await?; + let mut secondary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, secondary.initialize()).await??; let turn_id = primary @@ -2223,7 +2223,7 @@ async fn thread_resume_rejects_history_when_thread_is_running() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -2341,7 +2341,7 @@ async fn thread_resume_rejects_mismatched_path_for_running_thread_id() -> Result let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -2479,7 +2479,7 @@ async fn thread_resume_rejoins_running_thread_even_with_override_mismatch() -> R let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -2610,7 +2610,7 @@ async fn thread_resume_can_skip_turns_when_thread_is_running() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -2648,7 +2648,7 @@ async fn thread_resume_can_skip_turns_when_thread_is_running() -> Result<()> { ) .await??; - let mut secondary = McpProcess::new(codex_home.path()).await?; + let mut secondary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, secondary.initialize()).await??; let resume_id = secondary @@ -2694,7 +2694,7 @@ async fn thread_resume_replays_pending_command_execution_request_approval() -> R let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -2833,7 +2833,7 @@ async fn thread_resume_replays_pending_file_change_request_approval() -> Result< let server = create_mock_responses_server_sequence_unchecked(responses).await; create_config_toml(&codex_home, &server.uri())?; - let mut primary = McpProcess::new(&codex_home).await?; + let mut primary = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -3054,7 +3054,7 @@ async fn thread_resume_fails_when_required_mcp_server_fails_to_initialize() -> R let rollout = setup_rollout_fixture(codex_home.path(), &server.uri())?; create_config_toml_with_required_broken_mcp(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp @@ -3133,7 +3133,7 @@ async fn thread_resume_surfaces_cloud_requirements_load_errors() -> Result<()> { /*git_info*/ None, )?; let refresh_token_url = format!("{}/oauth/token", server.uri()); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -3227,7 +3227,7 @@ async fn thread_resume_can_load_source_by_external_path() -> Result<()> { )?; let thread_path = rollout_path(external_home.path(), "2025-01-05T12-00-00", &thread_id); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let resume_id = mcp .send_thread_resume_request(ThreadResumeParams { @@ -3306,7 +3306,7 @@ async fn thread_resume_supports_history_and_overrides() -> Result<()> { } struct RestartedThreadFixture { - mcp: McpProcess, + mcp: TestAppServer, thread_id: String, rollout_file_path: PathBuf, updated_at: i64, @@ -3316,7 +3316,7 @@ async fn start_materialized_thread_and_restart( codex_home: &Path, seed_text: &str, ) -> Result { - let mut first_mcp = McpProcess::new(codex_home).await?; + let mut first_mcp = TestAppServer::new(codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, first_mcp.initialize()).await??; let start_id = first_mcp @@ -3375,7 +3375,7 @@ async fn start_materialized_thread_and_restart( drop(first_mcp); - let mut second_mcp = McpProcess::new(codex_home).await?; + let mut second_mcp = TestAppServer::new(codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, second_mcp.initialize()).await??; Ok(RestartedThreadFixture { @@ -3406,7 +3406,7 @@ async fn thread_resume_accepts_personality_override() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut primary = McpProcess::new(codex_home.path()).await?; + let mut primary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??; let start_id = primary @@ -3444,7 +3444,7 @@ async fn thread_resume_accepts_personality_override() -> Result<()> { ) .await??; - let mut secondary = McpProcess::new(codex_home.path()).await?; + let mut secondary = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, secondary.initialize()).await??; let resume_id = secondary diff --git a/codex-rs/app-server/tests/suite/v2/thread_rollback.rs b/codex-rs/app-server/tests/suite/v2/thread_rollback.rs index 80feacb4fe..4f45aa496b 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_rollback.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_rollback.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; @@ -35,7 +35,7 @@ async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<() let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Start a thread. diff --git a/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs b/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs index 5f7b8dbf21..98ad587326 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; @@ -44,7 +44,7 @@ async fn thread_settings_update_emits_notification_and_updates_future_turns() -> write_models_cache(codex_home.path())?; let (model_id, service_tier_id) = service_tier_model_and_tier_id()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let thread = start_thread(&mut mcp).await?.thread; @@ -104,7 +104,7 @@ async fn thread_settings_update_while_turn_is_active_emits_notification() -> Res let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let thread = start_thread(&mut mcp).await?.thread; start_text_turn(&mut mcp, thread.id.clone()).await?; @@ -147,7 +147,7 @@ async fn thread_settings_update_null_service_tier_uses_default() -> Result<()> { write_models_cache(codex_home.path())?; let (model_id, service_tier_id) = service_tier_model_and_tier_id()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let thread = start_thread(&mut mcp).await?.thread; @@ -213,7 +213,7 @@ async fn thread_settings_update_rejects_sandbox_policy_with_permissions() -> Res let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let thread = start_thread(&mut mcp).await?.thread; @@ -247,7 +247,7 @@ async fn turn_start_settings_override_emits_thread_settings_updated() -> Result< let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??; let thread = start_thread(&mut mcp).await?.thread; timeout( @@ -289,7 +289,7 @@ async fn turn_start_settings_override_emits_thread_settings_updated() -> Result< } async fn send_thread_settings_update( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, params: ThreadSettingsUpdateParams, ) -> Result<()> { let request_id = mcp.send_thread_settings_update_request(params).await?; @@ -302,7 +302,7 @@ async fn send_thread_settings_update( Ok(()) } -async fn start_text_turn(mcp: &mut McpProcess, thread_id: String) -> Result<()> { +async fn start_text_turn(mcp: &mut TestAppServer, thread_id: String) -> Result<()> { let turn_request_id = mcp .send_turn_start_request(TurnStartParams { thread_id, @@ -323,7 +323,7 @@ async fn start_text_turn(mcp: &mut McpProcess, thread_id: String) -> Result<()> Ok(()) } -async fn start_thread(mcp: &mut McpProcess) -> Result { +async fn start_thread(mcp: &mut TestAppServer) -> Result { let request_id = mcp .send_thread_start_request(ThreadStartParams { model: Some("mock-model".to_string()), @@ -339,7 +339,7 @@ async fn start_thread(mcp: &mut McpProcess) -> Result { } async fn read_thread_with_turns( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, thread_id: &str, ) -> Result { let request_id = mcp @@ -357,7 +357,7 @@ async fn read_thread_with_turns( } async fn read_thread_settings_updated( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, ) -> Result { let notification: JSONRPCNotification = timeout( DEFAULT_TIMEOUT, diff --git a/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs b/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs index 368368badc..975ef3b3a7 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_shell_command.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::create_shell_command_sse_response; @@ -60,7 +60,7 @@ async fn thread_shell_command_history_responses_exclude_persisted_command_execut &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.as_path()).await?; + let mut mcp = TestAppServer::new(codex_home.as_path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -196,7 +196,7 @@ async fn thread_shell_command_returns_error_when_local_environment_is_disabled() &BTreeMap::default(), )?; - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.as_path(), &[(CODEX_EXEC_SERVER_URL_ENV_VAR, Some("none"))], ) @@ -255,7 +255,7 @@ async fn thread_shell_command_uses_existing_active_turn() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.as_path()).await?; + let mut mcp = TestAppServer::new(codex_home.as_path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -410,7 +410,7 @@ fn current_shell_output_command(text: &str) -> Result<(String, String)> { } async fn wait_for_command_execution_started( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, expected_id: Option<&str>, ) -> Result { loop { @@ -432,7 +432,7 @@ async fn wait_for_command_execution_started( } async fn wait_for_command_execution_started_by_source( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, expected_source: CommandExecutionSource, ) -> Result { loop { @@ -447,7 +447,7 @@ async fn wait_for_command_execution_started_by_source( } async fn wait_for_command_execution_completed( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, expected_id: Option<&str>, ) -> Result { loop { @@ -469,7 +469,7 @@ async fn wait_for_command_execution_completed( } async fn wait_for_command_execution_output_delta( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, item_id: &str, ) -> Result { loop { diff --git a/codex-rs/app-server/tests/suite/v2/thread_start.rs b/codex-rs/app-server/tests/suite/v2/thread_start.rs index 6be6d838ba..b55cf997df 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_start.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_start.rs @@ -1,7 +1,7 @@ use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; use app_test_support::PathBufExt; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; @@ -58,7 +58,7 @@ async fn thread_start_deprecates_persist_extended_history_true() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -101,7 +101,7 @@ async fn thread_start_creates_thread_and_emits_started() -> Result<()> { create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; // Start server and initialize. - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Start a v2 thread with an explicit model override. @@ -248,7 +248,7 @@ async fn thread_start_resolves_runtime_workspace_roots_against_cwd() -> Result<( let relative_root = PathBuf::from("extra-root"); std::fs::create_dir_all(cwd.join(&relative_root))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -292,7 +292,7 @@ async fn thread_start_excludes_profile_workspace_roots_from_runtime_workspace_ro profile_root.path(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -327,7 +327,7 @@ async fn thread_start_rejects_unknown_environment_as_invalid_request() -> Result let codex_home = TempDir::new()?; create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -364,7 +364,7 @@ async fn thread_start_response_includes_loaded_instruction_sources() -> Result<( let project_agents_path = workspace.path().join("AGENTS.md"); std::fs::write(&project_agents_path, "project instructions")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -420,7 +420,7 @@ async fn thread_start_tracks_thread_initialized_analytics() -> Result<()> { create_config_toml_with_chatgpt_base_url(codex_home.path(), &server.uri(), &server.uri())?; mount_analytics_capture(&server, codex_home.path()).await?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -468,7 +468,7 @@ model_reasoning_effort = "high" )?; set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -498,7 +498,7 @@ async fn thread_start_drops_unsupported_service_tier_id() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let service_tier_id = "experimental-tier-id".to_string(); @@ -528,7 +528,7 @@ async fn thread_start_accepts_default_service_tier() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -559,7 +559,7 @@ async fn thread_start_accepts_metrics_service_name() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -586,7 +586,7 @@ async fn thread_start_ephemeral_remains_pathless() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -632,7 +632,7 @@ async fn thread_start_fails_when_required_mcp_server_fails_to_initialize() -> Re let codex_home = TempDir::new()?; create_config_toml_with_required_broken_mcp(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -668,7 +668,7 @@ async fn thread_start_emits_mcp_server_status_updated_notifications() -> Result< let codex_home = TempDir::new()?; create_config_toml_with_optional_broken_mcp(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let req_id = mcp @@ -798,7 +798,7 @@ async fn thread_start_surfaces_cloud_requirements_load_errors() -> Result<()> { )?; let refresh_token_url = format!("{}/oauth/token", server.uri()); - let mut mcp = McpProcess::new_with_env( + let mut mcp = TestAppServer::new_with_env( codex_home.path(), &[ ("OPENAI_API_KEY", None), @@ -858,7 +858,7 @@ model_reasoning_effort = "high" "#, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let first_request = mcp @@ -918,7 +918,7 @@ async fn thread_start_with_nested_git_cwd_trusts_repo_root() -> Result<()> { let nested = repo_root.path().join("nested/project"); std::fs::create_dir_all(&nested)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -956,7 +956,7 @@ async fn thread_start_with_read_only_sandbox_does_not_persist_project_trust() -> let workspace = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -994,7 +994,7 @@ async fn thread_start_preserves_untrusted_project_trust() -> Result<()> { std::fs::write(&config_path, config_toml.to_string())?; let config_before = std::fs::read_to_string(&config_path)?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -1035,7 +1035,7 @@ model_reasoning_effort = "high" set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; let config_before = std::fs::read_to_string(codex_home.path().join("config.toml"))?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/thread_status.rs b/codex-rs/app-server/tests/suite/v2/thread_status.rs index 51dda1bde7..b8349a77f1 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_status.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_status.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::to_response; @@ -29,7 +29,7 @@ async fn thread_status_changed_emits_runtime_updates() -> Result<()> { create_config_toml(codex_home.path(), &server.uri())?; let mut mcp = - McpProcess::new_with_env(codex_home.path(), &[("RUST_LOG", Some("info"))]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("RUST_LOG", Some("info"))]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_start_id = mcp @@ -135,7 +135,7 @@ async fn thread_status_changed_can_be_opted_out() -> Result<()> { let server = create_mock_responses_server_sequence(responses).await; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; let message = timeout( DEFAULT_READ_TIMEOUT, mcp.initialize_with_capabilities( diff --git a/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs b/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs index 46ce38f48d..350d18a5b5 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_unarchive.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use codex_app_server::in_process; @@ -60,7 +60,7 @@ async fn thread_unarchive_moves_rollout_back_into_sessions_directory() -> Result let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp diff --git a/codex-rs/app-server/tests/suite/v2/thread_unsubscribe.rs b/codex-rs/app-server/tests/suite/v2/thread_unsubscribe.rs index ebd3c9b95d..55aac670f8 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_unsubscribe.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_unsubscribe.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_repeating_assistant; use app_test_support::to_response; use codex_app_server_protocol::DynamicToolCallOutputContentItem; @@ -41,7 +41,7 @@ async fn thread_unsubscribe_keeps_thread_loaded_until_idle_timeout() -> Result<( let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_thread(&mut mcp).await?; @@ -120,7 +120,7 @@ async fn thread_unsubscribe_during_turn_keeps_turn_running() -> Result<()> { let final_response_completed = completions.remove(0); create_config_toml(&codex_home, server.uri())?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -253,7 +253,7 @@ async fn thread_unsubscribe_preserves_cached_status_before_idle_unload() -> Resu let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_thread(&mut mcp).await?; @@ -340,7 +340,7 @@ async fn thread_unsubscribe_reports_not_subscribed_before_idle_unload() -> Resul let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), &server.uri())?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_id = start_thread(&mut mcp).await?; @@ -379,7 +379,7 @@ async fn thread_unsubscribe_reports_not_subscribed_before_idle_unload() -> Resul } async fn wait_for_dynamic_tool_started( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, call_id: &str, ) -> Result { loop { @@ -419,7 +419,7 @@ stream_max_retries = 0 ) } -async fn start_thread(mcp: &mut McpProcess) -> Result { +async fn start_thread(mcp: &mut TestAppServer) -> Result { let req_id = mcp .send_thread_start_request(ThreadStartParams { model: Some("mock-model".to_string()), diff --git a/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs b/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs index 78079ea148..335d32776d 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_interrupt.rs @@ -1,7 +1,7 @@ #![cfg(unix)] use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::create_mock_responses_server_sequence_unchecked; @@ -57,7 +57,7 @@ async fn turn_interrupt_aborts_running_turn() -> Result<()> { .await; create_config_toml(&codex_home, &server.uri(), "never", "workspace-write")?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Start a v2 thread and capture its id. @@ -141,7 +141,7 @@ async fn turn_interrupt_rejects_completed_turn() -> Result<()> { .await; create_config_toml(&codex_home, &server.uri(), "never", "workspace-write")?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -236,7 +236,7 @@ async fn turn_interrupt_resolves_pending_command_approval_request() -> Result<() .await; create_config_toml(&codex_home, &server.uri(), "untrusted", "read-only")?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 2ae4561d86..0c2c7b71cd 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -1,7 +1,7 @@ use anyhow::Context; use anyhow::Result; use app_test_support::DEFAULT_CLIENT_NAME; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_apply_patch_sse_response; use app_test_support::create_exec_command_sse_response; use app_test_support::create_fake_rollout; @@ -119,7 +119,7 @@ async fn run_local_image_turn(detail: Option) -> Result> &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -216,7 +216,7 @@ async fn turn_start_with_empty_input_runs_model_request() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -318,7 +318,7 @@ async fn turn_start_additional_context_flows_to_model_input() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -395,7 +395,7 @@ async fn turn_start_sends_originator_header() -> Result<()> { &BTreeMap::from([(Feature::Personality, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout( DEFAULT_READ_TIMEOUT, mcp.initialize_with_client_info(ClientInfo { @@ -472,7 +472,7 @@ async fn turn_start_emits_user_message_item_with_text_elements() -> Result<()> { &BTreeMap::from([(Feature::Personality, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -587,7 +587,7 @@ async fn turn_start_emits_thread_scoped_warning_notification_for_trimmed_skills( write_test_skill(codex_home.path(), "alpha-skill")?; write_test_skill(codex_home.path(), "beta-skill")?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -680,7 +680,7 @@ async fn turn_start_sends_service_tier_id_to_model_request() -> Result<()> { .expect("bundled model catalog should include a picker model with service tiers"); let service_tier_id = service_tier_model.service_tiers[0].id.clone(); - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -744,7 +744,7 @@ async fn thread_start_omits_empty_instruction_overrides_from_model_request() -> &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -830,7 +830,7 @@ async fn turn_start_tracks_turn_event_analytics() -> Result<()> { )?; mount_analytics_capture(&server, codex_home.path()).await?; - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; + let mut mcp = TestAppServer::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -920,7 +920,7 @@ async fn turn_start_accepts_text_at_limit_with_mention_item() -> Result<()> { &BTreeMap::from([(Feature::Personality, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -980,7 +980,7 @@ async fn turn_start_rejects_combined_oversized_text_input() -> Result<()> { &BTreeMap::from([(Feature::Personality, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -1060,7 +1060,7 @@ async fn turn_start_rejects_invalid_permission_selection_before_starting_turn() "sandbox_mode = \"read-only\"\n", )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -1132,7 +1132,7 @@ async fn turn_start_rejects_unknown_environment_before_starting_turn() -> Result &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -1204,7 +1204,7 @@ async fn turn_start_emits_notifications_and_accepts_model_override() -> Result<( &BTreeMap::from([(Feature::Personality, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // Start a thread (v2) and capture its id. @@ -1349,7 +1349,7 @@ async fn turn_start_accepts_collaboration_mode_override_v2() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -1435,7 +1435,7 @@ async fn turn_start_uses_thread_feature_overrides_for_request_user_input_tool_de &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -1520,7 +1520,7 @@ async fn turn_start_accepts_personality_override_v2() -> Result<()> { &BTreeMap::from([(Feature::Personality, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -1602,7 +1602,7 @@ async fn turn_start_change_personality_mid_thread_v2() -> Result<()> { &BTreeMap::from([(Feature::Personality, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -1718,7 +1718,7 @@ async fn turn_start_uses_migrated_pragmatic_personality_without_override_v2() -> /*git_info*/ None, )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let persisted_toml: ConfigToml = toml::from_str(&std::fs::read_to_string( @@ -1849,7 +1849,7 @@ async fn turn_start_exec_approval_toggle_v2() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.as_path()).await?; + let mut mcp = TestAppServer::new(codex_home.as_path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // thread/start @@ -1994,7 +1994,7 @@ async fn turn_start_exec_approval_decline_v2() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(codex_home.as_path()).await?; + let mut mcp = TestAppServer::new(codex_home.as_path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -2147,7 +2147,7 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; // thread/start @@ -2337,7 +2337,7 @@ stream_max_retries = 0 ), )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -2448,7 +2448,7 @@ url = "ws://127.0.0.1:1" "#, )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; for case in [ @@ -2491,7 +2491,7 @@ struct EnvironmentSelectionCase { } async fn run_environment_selection_case( - mcp: &mut McpProcess, + mcp: &mut TestAppServer, workspace: &Path, case: EnvironmentSelectionCase, ) -> Result<()> { @@ -2609,7 +2609,7 @@ async fn turn_start_file_change_approval_v2() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_req = mcp @@ -2800,7 +2800,7 @@ async fn turn_start_does_not_stream_apply_patch_change_updates_without_feature_v let server = create_mock_responses_server_sequence(responses).await; create_config_toml(&codex_home, &server.uri(), "never", &BTreeMap::default())?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_req = mcp @@ -2938,7 +2938,7 @@ async fn turn_start_streams_apply_patch_change_updates_v2() -> Result<()> { model["apply_patch_tool_type"] = serde_json::Value::from("freeform"); std::fs::write(&cache_path, serde_json::to_string_pretty(&cache)?)?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_req = mcp @@ -3070,7 +3070,7 @@ async fn turn_start_emits_spawn_agent_item_with_model_metadata_v2() -> Result<() &BTreeMap::from([(Feature::Collab, true)]), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -3290,7 +3290,7 @@ config_file = "./custom-role.toml" ), )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -3434,7 +3434,7 @@ async fn turn_start_file_change_approval_accept_for_session_persists_v2() -> Res &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_req = mcp @@ -3607,7 +3607,7 @@ async fn turn_start_file_change_approval_decline_v2() -> Result<()> { &BTreeMap::default(), )?; - let mut mcp = McpProcess::new(&codex_home).await?; + let mut mcp = TestAppServer::new(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_req = mcp @@ -3754,7 +3754,7 @@ async fn command_execution_notifications_include_process_id() -> Result<()> { "danger-full-access", )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let start_id = mcp @@ -3888,7 +3888,7 @@ async fn turn_start_with_elevated_override_does_not_persist_project_trust() -> R let workspace = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_request = mcp diff --git a/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs b/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs index de30da1ba4..dfb76ecbc2 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start_zsh_fork.rs @@ -7,7 +7,7 @@ // network access are required the first time the artifact is fetched. use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence; use app_test_support::create_mock_responses_server_sequence_unchecked; @@ -743,10 +743,10 @@ async fn create_zsh_test_mcp_process( codex_home: &Path, zdotdir: &Path, zsh_path: &Path, -) -> Result { +) -> Result { let app_server = create_test_package_app_server(codex_home, zsh_path)?; let zdotdir = zdotdir.to_string_lossy().into_owned(); - McpProcess::new_with_program_and_env( + TestAppServer::new_with_program_and_env( codex_home, &app_server, &[("ZDOTDIR", Some(zdotdir.as_str()))], diff --git a/codex-rs/app-server/tests/suite/v2/turn_steer.rs b/codex-rs/app-server/tests/suite/v2/turn_steer.rs index 3d4b46b91d..ed0ea2def5 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_steer.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_steer.rs @@ -2,7 +2,7 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_sequence; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::create_shell_command_sse_response; @@ -50,7 +50,7 @@ async fn turn_steer_requires_active_turn() -> Result<()> { )?; mount_analytics_capture(&server, &codex_home).await?; - let mut mcp = McpProcess::new_without_managed_config(&codex_home).await?; + let mut mcp = TestAppServer::new_without_managed_config(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -136,7 +136,7 @@ async fn turn_steer_rejects_oversized_text_input() -> Result<()> { )?; mount_analytics_capture(&server, &codex_home).await?; - let mut mcp = McpProcess::new_without_managed_config(&codex_home).await?; + let mut mcp = TestAppServer::new_without_managed_config(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -250,7 +250,7 @@ async fn turn_steer_returns_active_turn_id() -> Result<()> { )?; mount_analytics_capture(&server, &codex_home).await?; - let mut mcp = McpProcess::new_without_managed_config(&codex_home).await?; + let mut mcp = TestAppServer::new_without_managed_config(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -387,7 +387,7 @@ async fn turn_steer_rejects_context_only_input_without_merging_context() -> Resu )?; mount_analytics_capture(&server, &codex_home).await?; - let mut mcp = McpProcess::new_without_managed_config(&codex_home).await?; + let mut mcp = TestAppServer::new_without_managed_config(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp diff --git a/codex-rs/app-server/tests/suite/v2/web_search.rs b/codex-rs/app-server/tests/suite/v2/web_search.rs index 0d2d995328..13c595d148 100644 --- a/codex-rs/app-server/tests/suite/v2/web_search.rs +++ b/codex-rs/app-server/tests/suite/v2/web_search.rs @@ -4,7 +4,7 @@ use std::time::Duration; use anyhow::Context; use anyhow::Result; use app_test_support::ChatGptAuthFixture; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::to_response; use app_test_support::write_chatgpt_auth; use codex_app_server_protocol::ItemCompletedNotification; @@ -78,7 +78,8 @@ async fn standalone_web_search_round_trips_encrypted_output() -> Result<()> { AuthCredentialsStoreMode::File, )?; - let mut mcp = McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + let mut mcp = + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let thread_req = mcp @@ -194,7 +195,7 @@ async fn standalone_web_search_round_trips_encrypted_output() -> Result<()> { drop(mcp); let mut reloaded_mcp = - McpProcess::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; + TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, reloaded_mcp.initialize()).await??; let read_req = reloaded_mcp .send_thread_read_request(ThreadReadParams { @@ -219,7 +220,7 @@ async fn standalone_web_search_round_trips_encrypted_output() -> Result<()> { Ok(()) } -async fn wait_for_web_search_started(mcp: &mut McpProcess) -> Result { +async fn wait_for_web_search_started(mcp: &mut TestAppServer) -> Result { loop { let notification = mcp .read_stream_until_notification_message("item/started") @@ -235,7 +236,9 @@ async fn wait_for_web_search_started(mcp: &mut McpProcess) -> Result Result { +async fn wait_for_web_search_completed( + mcp: &mut TestAppServer, +) -> Result { loop { let notification = mcp .read_stream_until_notification_message("item/completed") diff --git a/codex-rs/app-server/tests/suite/v2/windows_sandbox_setup.rs b/codex-rs/app-server/tests/suite/v2/windows_sandbox_setup.rs index a0466a459b..afb2b2ab33 100644 --- a/codex-rs/app-server/tests/suite/v2/windows_sandbox_setup.rs +++ b/codex-rs/app-server/tests/suite/v2/windows_sandbox_setup.rs @@ -1,6 +1,6 @@ use anyhow::Context; use anyhow::Result; -use app_test_support::McpProcess; +use app_test_support::TestAppServer; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::to_response; use app_test_support::write_mock_responses_config_toml; @@ -31,7 +31,7 @@ async fn windows_sandbox_setup_start_emits_completion_notification() -> Result<( "mock_provider", "compact prompt", )?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp @@ -66,7 +66,7 @@ async fn windows_sandbox_setup_start_emits_completion_notification() -> Result<( #[tokio::test] async fn windows_sandbox_setup_start_rejects_relative_cwd() -> Result<()> { let codex_home = TempDir::new()?; - let mut mcp = McpProcess::new(codex_home.path()).await?; + let mut mcp = TestAppServer::new(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp From ef771ccc19bbf461365e33250a73d07f4ec500b8 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 1 Jun 2026 14:52:31 -0700 Subject: [PATCH 3/3] core: stop threading SandboxPolicy through exec Migrate the exec-side Windows sandbox override plumbing to pass PermissionProfile plus the split filesystem/network policies instead of accepting a legacy SandboxPolicy. This keeps the remaining compatibility projection local to the writable-root comparison that still needs it, and removes ExecRequest::compatibility_sandbox_policy without touching the broader config, protocol, app-server, telemetry, or session surfaces from #25450. --- codex-rs/core/src/exec.rs | 62 ++++++++--------- codex-rs/core/src/exec_tests.rs | 101 +++++++++++++++++++++++----- codex-rs/core/src/sandboxing/mod.rs | 11 --- codex-rs/core/src/spawn.rs | 2 +- 4 files changed, 115 insertions(+), 61 deletions(-) diff --git a/codex-rs/core/src/exec.rs b/codex-rs/core/src/exec.rs index e050552246..620e15eff1 100644 --- a/codex-rs/core/src/exec.rs +++ b/codex-rs/core/src/exec.rs @@ -38,12 +38,12 @@ use codex_protocol::protocol::Event; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::ExecCommandOutputDeltaEvent; use codex_protocol::protocol::ExecOutputStream; -use codex_protocol::protocol::SandboxPolicy; use codex_sandboxing::SandboxCommand; use codex_sandboxing::SandboxManager; use codex_sandboxing::SandboxTransformRequest; use codex_sandboxing::SandboxType; use codex_sandboxing::SandboxablePreference; +use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_pty::DEFAULT_OUTPUT_BYTES_CAP; use codex_utils_pty::process_group::kill_child_process_group; @@ -419,11 +419,10 @@ pub fn build_exec_request( exec_req.windows_sandbox_level, exec_req.network.is_some(), ); - let sandbox_policy = exec_req.compatibility_sandbox_policy(); exec_req.windows_sandbox_filesystem_overrides = if use_windows_elevated_backend { resolve_windows_elevated_filesystem_overrides( exec_req.sandbox, - &sandbox_policy, + &exec_req.permission_profile, &exec_req.file_system_sandbox_policy, exec_req.network_sandbox_policy, sandbox_cwd, @@ -432,7 +431,7 @@ pub fn build_exec_request( } else { resolve_windows_restricted_token_filesystem_overrides( exec_req.sandbox, - &sandbox_policy, + &exec_req.permission_profile, &exec_req.file_system_sandbox_policy, exec_req.network_sandbox_policy, sandbox_cwd, @@ -1006,21 +1005,17 @@ async fn exec( #[cfg_attr(not(target_os = "windows"), allow(dead_code))] fn should_use_windows_restricted_token_sandbox( sandbox: SandboxType, - sandbox_policy: &SandboxPolicy, file_system_sandbox_policy: &FileSystemSandboxPolicy, ) -> bool { sandbox == SandboxType::WindowsRestrictedToken && file_system_sandbox_policy.kind == FileSystemSandboxKind::Restricted - && !matches!( - sandbox_policy, - SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } - ) + && !file_system_sandbox_policy.has_full_disk_write_access() } #[cfg_attr(not(test), allow(dead_code))] pub(crate) fn unsupported_windows_restricted_token_sandbox_reason( sandbox: SandboxType, - sandbox_policy: &SandboxPolicy, + permission_profile: &PermissionProfile, file_system_sandbox_policy: &FileSystemSandboxPolicy, network_sandbox_policy: NetworkSandboxPolicy, sandbox_policy_cwd: &AbsolutePathBuf, @@ -1029,7 +1024,7 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason( if windows_sandbox_level == WindowsSandboxLevel::Elevated { resolve_windows_elevated_filesystem_overrides( sandbox, - sandbox_policy, + permission_profile, file_system_sandbox_policy, network_sandbox_policy, sandbox_policy_cwd, @@ -1039,7 +1034,7 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason( } else { resolve_windows_restricted_token_filesystem_overrides( sandbox, - sandbox_policy, + permission_profile, file_system_sandbox_policy, network_sandbox_policy, sandbox_policy_cwd, @@ -1051,7 +1046,7 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason( pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( sandbox: SandboxType, - sandbox_policy: &SandboxPolicy, + permission_profile: &PermissionProfile, file_system_sandbox_policy: &FileSystemSandboxPolicy, network_sandbox_policy: NetworkSandboxPolicy, sandbox_policy_cwd: &AbsolutePathBuf, @@ -1066,22 +1061,15 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( let needs_direct_runtime_enforcement = file_system_sandbox_policy .needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd); - if should_use_windows_restricted_token_sandbox( - sandbox, - sandbox_policy, - file_system_sandbox_policy, - ) && !needs_direct_runtime_enforcement + if should_use_windows_restricted_token_sandbox(sandbox, file_system_sandbox_policy) + && !needs_direct_runtime_enforcement { return Ok(None); } - if !should_use_windows_restricted_token_sandbox( - sandbox, - sandbox_policy, - file_system_sandbox_policy, - ) { + if !should_use_windows_restricted_token_sandbox(sandbox, file_system_sandbox_policy) { return Err(format!( - "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, legacy_policy={sandbox_policy:?}; refusing to run unsandboxed", + "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, permission_profile={permission_profile:?}; refusing to run unsandboxed", file_system_sandbox_policy.kind, )); } @@ -1108,7 +1096,13 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( ); } - let legacy_writable_roots = sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd); + let legacy_projection = compatibility_sandbox_policy_for_permission_profile( + permission_profile, + file_system_sandbox_policy, + network_sandbox_policy, + sandbox_policy_cwd.as_path(), + ); + let legacy_writable_roots = legacy_projection.get_writable_roots_with_cwd(sandbox_policy_cwd); let split_writable_roots = file_system_sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd); let legacy_root_paths: BTreeSet = legacy_writable_roots @@ -1204,7 +1198,7 @@ fn windows_policy_has_root_read_access( pub(crate) fn resolve_windows_elevated_filesystem_overrides( sandbox: SandboxType, - sandbox_policy: &SandboxPolicy, + permission_profile: &PermissionProfile, file_system_sandbox_policy: &FileSystemSandboxPolicy, network_sandbox_policy: NetworkSandboxPolicy, sandbox_policy_cwd: &AbsolutePathBuf, @@ -1214,13 +1208,9 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides( return Ok(None); } - if !should_use_windows_restricted_token_sandbox( - sandbox, - sandbox_policy, - file_system_sandbox_policy, - ) { + if !should_use_windows_restricted_token_sandbox(sandbox, file_system_sandbox_policy) { return Err(format!( - "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, legacy_policy={sandbox_policy:?}; refusing to run unsandboxed", + "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, permission_profile={permission_profile:?}; refusing to run unsandboxed", file_system_sandbox_policy.kind, )); } @@ -1242,7 +1232,13 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides( let needs_direct_runtime_enforcement = file_system_sandbox_policy .needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd); let normalize_path = |path: PathBuf| dunce::canonicalize(&path).unwrap_or(path); - let legacy_writable_roots = sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd); + let legacy_projection = compatibility_sandbox_policy_for_permission_profile( + permission_profile, + file_system_sandbox_policy, + network_sandbox_policy, + sandbox_policy_cwd.as_path(), + ); + let legacy_writable_roots = legacy_projection.get_writable_roots_with_cwd(sandbox_policy_cwd); let legacy_root_paths: BTreeSet = legacy_writable_roots .iter() .map(|root| normalize_path(root.root.to_path_buf())) diff --git a/codex-rs/core/src/exec_tests.rs b/codex-rs/core/src/exec_tests.rs index 1c71d02651..c0433cbaec 100644 --- a/codex-rs/core/src/exec_tests.rs +++ b/codex-rs/core/src/exec_tests.rs @@ -1,6 +1,8 @@ use super::*; use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::models::PermissionProfile; +use codex_protocol::models::SandboxEnforcement; +use codex_protocol::protocol::SandboxPolicy; use codex_sandboxing::SandboxType; use core_test_support::PathBufExt; use core_test_support::PathExt; @@ -26,6 +28,17 @@ fn make_exec_output( } } +fn permission_profile_for_runtime_permissions( + policy: &SandboxPolicy, + file_system_policy: &FileSystemSandboxPolicy, +) -> PermissionProfile { + PermissionProfile::from_runtime_permissions_with_enforcement( + SandboxEnforcement::from_legacy_sandbox_policy(policy), + file_system_policy, + NetworkSandboxPolicy::from(policy), + ) +} + #[test] fn sandbox_detection_requires_keywords() { let output = make_exec_output(/*exit_code*/ 1, "", "", ""); @@ -387,7 +400,6 @@ fn windows_restricted_token_skips_external_sandbox_policies() { assert_eq!( should_use_windows_restricted_token_sandbox( SandboxType::WindowsRestrictedToken, - &policy, &file_system_policy, ), false @@ -402,7 +414,6 @@ fn windows_restricted_token_runs_for_legacy_restricted_policies() { assert_eq!( should_use_windows_restricted_token_sandbox( SandboxType::WindowsRestrictedToken, - &policy, &file_system_policy, ), true @@ -431,33 +442,69 @@ fn windows_restricted_token_rejects_network_only_restrictions() { network_access: codex_protocol::protocol::NetworkAccess::Restricted, }; let file_system_policy = FileSystemSandboxPolicy::unrestricted(); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &sandbox_policy_cwd, WindowsSandboxLevel::RestrictedToken, ), Some( - "windows sandbox backend cannot enforce file_system=Unrestricted, network=Restricted, legacy_policy=ExternalSandbox { network_access: Restricted }; refusing to run unsandboxed".to_string() + "windows sandbox backend cannot enforce file_system=Unrestricted, network=Restricted, permission_profile=Managed { file_system: Unrestricted, network: Restricted }; refusing to run unsandboxed".to_string() ) ); } +#[test] +fn windows_restricted_token_rejects_full_write_split_profiles() { + let policy = SandboxPolicy::ExternalSandbox { + network_access: codex_protocol::protocol::NetworkAccess::Restricted, + }; + let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ + codex_protocol::permissions::FileSystemSandboxEntry { + path: codex_protocol::permissions::FileSystemPath::Special { + value: codex_protocol::permissions::FileSystemSpecialPath::Root, + }, + access: codex_protocol::permissions::FileSystemAccessMode::Write, + }, + ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); + let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); + + assert_eq!( + unsupported_windows_restricted_token_sandbox_reason( + SandboxType::WindowsRestrictedToken, + &permission_profile, + &file_system_policy, + NetworkSandboxPolicy::Restricted, + &sandbox_policy_cwd, + WindowsSandboxLevel::RestrictedToken, + ), + Some(format!( + "windows sandbox backend cannot enforce file_system=Restricted, network=Restricted, permission_profile={permission_profile:?}; refusing to run unsandboxed", + )) + ); +} + #[test] fn windows_restricted_token_allows_legacy_restricted_policies() { let policy = SandboxPolicy::new_read_only_policy(); let file_system_policy = FileSystemSandboxPolicy::from(&policy); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &sandbox_policy_cwd, @@ -476,12 +523,14 @@ fn windows_restricted_token_allows_legacy_workspace_write_policies() { exclude_slash_tmp: true, }; let file_system_policy = FileSystemSandboxPolicy::from(&policy); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &sandbox_policy_cwd, @@ -508,11 +557,13 @@ fn windows_elevated_allows_split_restricted_read_policies() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), @@ -550,11 +601,13 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), @@ -593,11 +646,13 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), @@ -644,6 +699,8 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); // The legacy workspace-write root already protects top-level `.codex`, so // the restricted-token overlay only needs the extra read-only docs carveout. @@ -652,7 +709,7 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() { assert_eq!( resolve_windows_restricted_token_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &cwd, @@ -702,11 +759,13 @@ fn windows_restricted_token_rejects_unreadable_split_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Deny, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( resolve_windows_restricted_token_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &cwd, @@ -737,11 +796,13 @@ fn windows_elevated_supports_split_restricted_read_roots() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), @@ -792,11 +853,13 @@ fn windows_elevated_supports_split_write_read_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), @@ -850,11 +913,13 @@ fn windows_elevated_supports_unreadable_split_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Deny, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), @@ -912,11 +977,13 @@ fn windows_elevated_supports_unreadable_globs() { access: codex_protocol::permissions::FileSystemAccessMode::Deny, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), @@ -977,11 +1044,13 @@ fn windows_elevated_rejects_reopened_writable_descendants() { access: codex_protocol::permissions::FileSystemAccessMode::Write, }, ]); + let permission_profile = + permission_profile_for_runtime_permissions(&policy, &file_system_policy); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &policy, + &permission_profile, &file_system_policy, NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), diff --git a/codex-rs/core/src/sandboxing/mod.rs b/codex-rs/core/src/sandboxing/mod.rs index f853ea3ba6..8a2a2849fd 100644 --- a/codex-rs/core/src/sandboxing/mod.rs +++ b/codex-rs/core/src/sandboxing/mod.rs @@ -22,10 +22,8 @@ use codex_protocol::models::PermissionProfile; pub use codex_protocol::models::SandboxPermissions; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::NetworkSandboxPolicy; -use codex_protocol::protocol::SandboxPolicy; use codex_sandboxing::SandboxExecRequest; use codex_sandboxing::SandboxType; -use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile; use codex_utils_absolute_path::AbsolutePathBuf; use std::collections::HashMap; @@ -102,15 +100,6 @@ impl ExecRequest { } } - pub(crate) fn compatibility_sandbox_policy(&self) -> SandboxPolicy { - compatibility_sandbox_policy_for_permission_profile( - &self.permission_profile, - &self.file_system_sandbox_policy, - self.network_sandbox_policy, - self.windows_sandbox_policy_cwd.as_path(), - ) - } - pub(crate) fn from_sandbox_exec_request( request: SandboxExecRequest, options: ExecOptions, diff --git a/codex-rs/core/src/spawn.rs b/codex-rs/core/src/spawn.rs index a2c4ebe597..a23a1d749e 100644 --- a/codex-rs/core/src/spawn.rs +++ b/codex-rs/core/src/spawn.rs @@ -30,7 +30,7 @@ pub enum StdioPolicy { Inherit, } -/// Spawns the appropriate child process for the ExecParams and SandboxPolicy, +/// Spawns the appropriate child process for the exec params and sandbox settings, /// ensuring the args and environment variables used to create the `Command` /// (and `Child`) honor the configuration. ///