test: migrate app-server v2 starts to auto env (#31614)

## Why

We should be running as many integrations tests as possible against the
split cross-OS configuration.

## What

- migrate eligible thread starts and builders to auto env
- keep explicit custom/local-executor cases local with rationale
comments
- keep auto-env coverage where possible and add narrow `TODO(anp)` skips
for fixtures that are not target-native yet
This commit is contained in:
Adam Perry @ OpenAI
2026-07-08 14:13:59 -07:00
committed by GitHub
parent 4b64bf0751
commit 5c7624a69e
29 changed files with 327 additions and 237 deletions
@@ -511,7 +511,6 @@ async fn external_auth_refreshes_on_unauthorized() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -538,7 +537,7 @@ async fn external_auth_refreshes_on_unauthorized() -> Result<()> {
.await??;
let thread_req = mcp
.send_thread_start_request(codex_app_server_protocol::ThreadStartParams {
.send_thread_start_request_with_auto_env(codex_app_server_protocol::ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -623,7 +622,6 @@ async fn external_auth_refresh_error_fails_turn() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -650,7 +648,7 @@ async fn external_auth_refresh_error_fails_turn() -> Result<()> {
.await??;
let thread_req = mcp
.send_thread_start_request(codex_app_server_protocol::ThreadStartParams {
.send_thread_start_request_with_auto_env(codex_app_server_protocol::ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -751,7 +749,6 @@ async fn external_auth_refresh_mismatched_workspace_fails_turn() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -778,7 +775,7 @@ async fn external_auth_refresh_mismatched_workspace_fails_turn() -> Result<()> {
.await??;
let thread_req = mcp
.send_thread_start_request(codex_app_server_protocol::ThreadStartParams {
.send_thread_start_request_with_auto_env(codex_app_server_protocol::ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -872,7 +869,6 @@ async fn external_auth_refresh_invalid_access_token_fails_turn() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -899,7 +895,7 @@ async fn external_auth_refresh_invalid_access_token_fails_turn() -> Result<()> {
.await??;
let thread_req = mcp
.send_thread_start_request(codex_app_server_protocol::ThreadStartParams {
.send_thread_start_request_with_auto_env(codex_app_server_protocol::ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -69,7 +69,6 @@ async fn attestation_generate_round_trip_adds_header_to_responses_websocket_hand
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -95,7 +94,7 @@ async fn attestation_generate_round_trip_adds_header_to_responses_websocket_hand
};
let thread_request_id = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let thread_response: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
@@ -83,7 +83,6 @@ async fn auto_compaction_local_emits_started_and_completed_items() -> Result<()>
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -170,7 +169,6 @@ async fn auto_compaction_remote_emits_started_and_completed_items() -> Result<()
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -281,7 +279,6 @@ async fn thread_compact_start_triggers_compaction_and_returns_empty_response() -
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -335,7 +332,6 @@ async fn thread_compact_start_rejects_invalid_thread_id() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -375,7 +371,6 @@ async fn thread_compact_start_rejects_unknown_thread_id() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -399,7 +394,7 @@ async fn thread_compact_start_rejects_unknown_thread_id() -> Result<()> {
async fn start_thread(mcp: &mut TestAppServer) -> Result<String> {
let thread_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -178,6 +178,7 @@ HTTP_PROXY = {http_proxy}
let mut app_server = TestAppServer::builder()
.with_codex_home(codex_home.path())
// This suite owns environments.toml to exercise explicit executor selection.
.without_auto_env()
.build()
.await?;
@@ -13,6 +13,7 @@ use codex_app_server_protocol::TurnStartParams;
use codex_app_server_protocol::UserInput;
use codex_utils_path_uri::PathUri;
use core_test_support::responses;
use core_test_support::skip_if_remote;
use tempfile::TempDir;
use tokio::time::timeout;
@@ -23,6 +24,12 @@ const LOCAL_SKILL_MARKER: &str = "LOCAL_SKILL_BODY_MARKER";
#[tokio::test]
async fn selected_executor_root_exposes_plugin_skill() -> Result<()> {
// TODO(anp): Remove after selected capability-root fixtures can be materialized in remote exec.
skip_if_remote!(
Ok(()),
"selected capability root fixture is only materialized on the host"
);
let server = responses::start_mock_server().await;
let response_mock = responses::mount_sse_once(
&server,
@@ -83,13 +90,12 @@ stream_max_retries = 0
let mut app_server = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(READ_TIMEOUT, app_server.initialize()).await??;
let request_id = app_server
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
selected_capability_roots: Some(vec![SelectedCapabilityRoot {
id: "demo-plugin@1".to_string(),
@@ -253,7 +253,6 @@ async fn thread_start_mock_field_requires_experimental_api_capability() -> Resul
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
let init = mcp
@@ -296,7 +295,6 @@ async fn thread_start_without_dynamic_tools_allows_without_experimental_api_capa
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
let init = mcp
@@ -338,7 +336,6 @@ async fn thread_start_granular_approval_policy_requires_experimental_api_capabil
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
let init = mcp
@@ -206,14 +206,13 @@ memories = true
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().display().to_string()),
..Default::default()
})
@@ -26,6 +26,7 @@ use codex_core::config::set_project_trust_level;
use codex_protocol::config_types::TrustLevel;
use codex_utils_absolute_path::AbsolutePathBuf;
use core_test_support::skip_if_host_windows;
use core_test_support::skip_if_remote;
use pretty_assertions::assert_eq;
use serde::Serialize;
use tempfile::TempDir;
@@ -137,7 +138,6 @@ async fn hooks_list_shows_discovered_hook() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -309,7 +309,6 @@ async fn hooks_list_warms_plugin_capabilities_for_thread_start() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -328,7 +327,7 @@ async fn hooks_list_warms_plugin_capabilities_for_thread_start() -> Result<()> {
std::fs::remove_file(plugin_mcp_path)?;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let _: ThreadStartResponse = to_response(
timeout(
@@ -680,6 +679,8 @@ async fn config_batch_write_toggles_user_hook() -> Result<()> {
#[tokio::test]
async fn config_batch_write_updates_hook_trust_for_loaded_session() -> Result<()> {
skip_if_host_windows!(Ok(()));
// TODO(anp): Teach command-hook fixtures to run in selected remote environments.
skip_if_remote!(Ok(()), "command hooks use host-local script and log paths");
let responses = vec![
create_final_assistant_message_sse_response("Warmup")?,
@@ -737,7 +738,6 @@ command = "python3 {hook_script_path}"
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -757,7 +757,7 @@ command = "python3 {hook_script_path}"
assert_eq!(hook.trust_status, HookTrustStatus::Untrusted);
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -935,6 +935,8 @@ command = "python3 {hook_script_path}"
#[tokio::test]
async fn config_batch_write_disables_hook_for_loaded_session() -> Result<()> {
skip_if_host_windows!(Ok(()));
// TODO(anp): Teach command-hook fixtures to run in selected remote environments.
skip_if_remote!(Ok(()), "command hooks use host-local script and log paths");
let responses = vec![
create_final_assistant_message_sse_response("Warmup")?,
@@ -991,7 +993,6 @@ command = "python3 {hook_script_path}"
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -1034,7 +1035,7 @@ command = "python3 {hook_script_path}"
let _: codex_app_server_protocol::ConfigWriteResponse = to_response(response)?;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -19,6 +19,7 @@ use codex_app_server_protocol::TurnStartResponse;
use codex_app_server_protocol::UserInput as V2UserInput;
use codex_config::types::AuthCredentialsStoreMode;
use core_test_support::responses;
use core_test_support::skip_if_remote;
use pretty_assertions::assert_eq;
use serde_json::json;
use tempfile::TempDir;
@@ -90,7 +91,6 @@ async fn standalone_image_generation_returns_saved_path_hint_to_model() -> Resul
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -192,7 +192,6 @@ async fn standalone_image_generation_failure_emits_terminal_item() -> Result<()>
)?;
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -236,6 +235,11 @@ async fn standalone_image_generation_failure_emits_terminal_item() -> Result<()>
#[tokio::test]
async fn standalone_image_edit_uses_attached_model_visible_image() -> Result<()> {
skip_if_remote!(
Ok(()),
"remote executors use different imagegen storage approaches, so host-local image paths are unavailable"
);
let edit_request = run_image_edit_test(|codex_home| {
let image_path = codex_home.join("attached.png");
std::fs::write(&image_path, TINY_PNG_BYTES)?;
@@ -317,7 +321,6 @@ async fn standalone_image_generation_is_exposed_in_code_mode_only() -> Result<()
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -384,7 +387,6 @@ generatedImage(result);
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -468,7 +470,6 @@ async fn run_image_edit_test(
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
@@ -499,7 +500,7 @@ async fn run_image_edit_test(
async fn start_turn(mcp: &mut TestAppServer, input: Vec<V2UserInput>) -> Result<()> {
let thread_req = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
@@ -89,11 +89,15 @@ async fn mcp_resource_read_returns_resource_contents() -> Result<()> {
let (apps_server_url, _apps_server_calls, apps_server_handle) =
start_resource_apps_mcp_server().await?;
let responses_server_uri = responses_server.uri();
let (_codex_home, mut mcp) =
start_resource_test_app_server(&apps_server_url, &responses_server_uri).await?;
let (_codex_home, mut mcp) = start_resource_test_app_server(
&apps_server_url,
&responses_server_uri,
ResourceTestEnvironment::Auto,
)
.await?;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -133,8 +137,12 @@ async fn orchestrator_skill_can_read_referenced_resource_without_an_executor() -
let (apps_server_url, apps_server_calls, apps_server_handle) =
start_resource_apps_mcp_server().await?;
let responses_server_uri = responses_server.uri();
let (_codex_home, mut mcp) =
start_resource_test_app_server(&apps_server_url, &responses_server_uri).await?;
let (_codex_home, mut mcp) = start_resource_test_app_server(
&apps_server_url,
&responses_server_uri,
ResourceTestEnvironment::Auto,
)
.await?;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
@@ -369,8 +377,13 @@ async fn local_executor_does_not_expose_orchestrator_skills() -> Result<()> {
let (apps_server_url, _apps_server_calls, apps_server_handle) =
start_resource_apps_mcp_server().await?;
let responses_server_uri = responses_server.uri();
let (_codex_home, mut mcp) =
start_resource_test_app_server(&apps_server_url, &responses_server_uri).await?;
let (_codex_home, mut mcp) = start_resource_test_app_server(
&apps_server_url,
&responses_server_uri,
// This test exercises the implicit local executor.
ResourceTestEnvironment::Local,
)
.await?;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
@@ -449,13 +462,13 @@ async fn disabled_orchestrator_skills_do_not_expose_skills_namespace() -> Result
[orchestrator.skills]
enabled = false
"#,
ResourceTestEnvironment::Auto,
)
.await?;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
environments: Some(Vec::new()),
..Default::default()
})
.await?;
@@ -648,15 +661,22 @@ async fn mcp_resource_read_returns_error_for_unknown_thread() -> Result<()> {
async fn start_resource_test_app_server(
apps_server_url: &str,
responses_server_uri: &str,
environment: ResourceTestEnvironment,
) -> Result<(TempDir, TestAppServer)> {
start_resource_test_app_server_with_extra_config(apps_server_url, responses_server_uri, "")
.await
start_resource_test_app_server_with_extra_config(
apps_server_url,
responses_server_uri,
"",
environment,
)
.await
}
async fn start_resource_test_app_server_with_extra_config(
apps_server_url: &str,
responses_server_uri: &str,
extra_config: &str,
environment: ResourceTestEnvironment,
) -> Result<(TempDir, TestAppServer)> {
let codex_home = TempDir::new()?;
std::fs::write(
@@ -696,15 +716,22 @@ stream_max_retries = 0
AuthCredentialsStoreMode::File,
)?;
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
let builder = TestAppServer::builder().with_codex_home(codex_home.path());
let builder = match environment {
ResourceTestEnvironment::Auto => builder,
// The Local caller explicitly exercises the implicit local executor.
ResourceTestEnvironment::Local => builder.without_auto_env(),
};
let mut mcp = builder.build().await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
Ok((codex_home, mcp))
}
enum ResourceTestEnvironment {
Auto,
Local,
}
async fn start_resource_apps_mcp_server()
-> Result<(String, Arc<ResourceAppsMcpCalls>, JoinHandle<()>)> {
let listener = TcpListener::bind("127.0.0.1:0").await?;
@@ -138,13 +138,12 @@ async fn mcp_server_status_list_uses_thread_project_local_config() -> Result<()>
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().to_string_lossy().into_owned()),
..Default::default()
})
@@ -348,6 +348,7 @@ url = "{mcp_server_url}/mcp"
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
// This test adds and refreshes an explicitly selected runtime environment.
.without_auto_env()
.build()
.await?;
@@ -56,6 +56,7 @@ use core_test_support::responses::WebSocketTestServer;
use core_test_support::responses::start_websocket_server;
use core_test_support::responses::start_websocket_server_with_headers;
use core_test_support::skip_if_no_network;
use core_test_support::skip_if_remote;
use pretty_assertions::assert_eq;
use serde::de::DeserializeOwned;
use serde_json::Value;
@@ -293,14 +294,13 @@ impl RealtimeE2eHarness {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
login_with_api_key(&mut mcp, "sk-test-key").await?;
let thread_start_request_id = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let thread_start_response: JSONRPCResponse = timeout(
DEFAULT_TIMEOUT,
@@ -1173,7 +1173,6 @@ async fn realtime_list_voices_returns_supported_names() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -2520,6 +2519,11 @@ async fn websocket_v2_background_agent_progress_is_sent_before_function_output()
#[tokio::test]
async fn websocket_v2_tool_call_delegated_turn_can_execute_shell_tool() -> Result<()> {
// TODO(anp): Remove after delegated shell commands resolve target-native cwd in remote environments.
skip_if_remote!(
Ok(()),
"delegated shell command cwd is only materialized on the host"
);
skip_if_no_network!(Ok(()));
// Phase 1: keep the two mocked OpenAI conversations explicit. The realtime sideband only
@@ -74,7 +74,6 @@ async fn first_turn_after_external_login_waits_for_recommended_plugins() -> Resu
let sqlite_home = codex_home.path().to_string_lossy();
let mut app_server = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.with_env_overrides(&[("CODEX_SQLITE_HOME", Some(sqlite_home.as_ref()))])
.build()
@@ -105,7 +104,7 @@ async fn first_turn_after_external_login_waits_for_recommended_plugins() -> Resu
);
let thread_id = app_server
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -142,6 +142,7 @@ async fn selected_capability_stack_tracks_environment_availability_and_resume()
let mut app_server = TestAppServer::builder()
.with_codex_home(fixture.codex_home.path())
// This fixture owns environments.toml and selects its environments explicitly.
.without_auto_env()
.build()
.await?;
@@ -192,6 +193,7 @@ async fn selected_capability_stack_tracks_environment_availability_and_resume()
let mut app_server = TestAppServer::builder()
.with_codex_home(fixture.codex_home.path())
// This fixture owns environments.toml and selects its environments explicitly.
.without_auto_env()
.build()
.await?;
@@ -343,6 +345,7 @@ async fn selected_capabilities_become_available_between_samples_in_one_turn() ->
let mut app_server = TestAppServer::builder()
.with_codex_home(fixture.codex_home.path())
// This fixture owns environments.toml and selects its environments explicitly.
.without_auto_env()
.build()
.await?;
@@ -24,6 +24,7 @@ use codex_app_server_protocol::ThreadStartParams;
use codex_config::types::AuthCredentialsStoreMode;
use codex_exec_server::CODEX_EXEC_SERVER_URL_ENV_VAR;
use codex_utils_absolute_path::AbsolutePathBuf;
use core_test_support::skip_if_remote;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
use tokio::time::timeout;
@@ -874,6 +875,12 @@ async fn skills_extra_roots_set_updates_process_runtime_roots() -> Result<()> {
#[tokio::test]
async fn skills_changed_notification_is_emitted_after_skill_change() -> Result<()> {
// TODO(anp): Remove after skill watching can bridge host-local storage into remote exec.
skip_if_remote!(
Ok(()),
"host-local skill changes are not visible to remote executors"
);
let server = create_mock_responses_server_repeating_assistant("Done").await;
let codex_home = TempDir::new()?;
write_mock_responses_config_toml_with_chatgpt_base_url(
@@ -885,8 +892,6 @@ async fn skills_changed_notification_is_emitted_after_skill_change() -> Result<(
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[(CODEX_EXEC_SERVER_URL_ENV_VAR, None)])
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -911,7 +916,7 @@ async fn skills_changed_notification_is_emitted_after_skill_change() -> Result<(
);
let thread_start_request_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: None,
model_provider: None,
allow_provider_model_fallback: false,
@@ -120,7 +120,6 @@ async fn thread_fork_creates_new_thread_and_emits_started() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -263,13 +262,12 @@ async fn thread_fork_at_last_turn_id_keeps_only_terminal_prefix() -> Result<()>
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let start_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
@@ -52,7 +52,6 @@ const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs
async fn init_mcp(codex_home: &Path) -> Result<TestAppServer> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -261,7 +260,7 @@ async fn thread_list_reports_system_error_idle_flag_after_failed_turn() -> Resul
let mut mcp = init_mcp(codex_home.path()).await?;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -85,6 +85,8 @@ use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::LegacyAppPathString;
use core_test_support::responses;
use core_test_support::skip_if_no_network;
use core_test_support::skip_if_remote;
use core_test_support::skip_if_wine_exec;
use pretty_assertions::assert_eq;
use serde_json::json;
use std::fs::FileTimes;
@@ -269,6 +271,11 @@ async fn thread_resume_with_empty_path_uses_running_thread_id() -> Result<()> {
#[tokio::test]
async fn thread_resume_running_thread_uses_cached_instruction_sources() -> Result<()> {
skip_if_remote!(
Ok(()),
"cached instruction-source fixture is outside the selected remote cwd"
);
let server = create_mock_responses_server_repeating_assistant("Done").await;
let codex_home = TempDir::new()?;
create_config_toml(codex_home.path(), &server.uri())?;
@@ -278,6 +285,7 @@ async fn thread_resume_running_thread_uses_cached_instruction_sources() -> Resul
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
// TODO(anp): Move the cached instruction-source fixture into the auto environment cwd.
.without_auto_env()
.build()
.await?;
@@ -360,13 +368,12 @@ async fn turn_start_updates_runtime_workspace_roots_for_loaded_thread() -> Resul
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -443,14 +450,13 @@ async fn thread_goal_get_rejects_unmaterialized_thread() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.2-codex".to_string()),
ephemeral: Some(true),
..Default::default()
@@ -507,7 +513,6 @@ async fn goal_first_live_thread_appears_in_state_db_thread_list() -> Result<()>
.expect("test codex home should be utf-8");
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home_path)
.without_auto_env()
.without_managed_config()
.with_env_overrides(&[("CODEX_SQLITE_HOME", Some(sqlite_home))])
.build()
@@ -515,7 +520,7 @@ async fn goal_first_live_thread_appears_in_state_db_thread_list() -> Result<()>
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.2-codex".to_string()),
..Default::default()
})
@@ -605,7 +610,6 @@ async fn thread_resume_tracks_thread_initialized_analytics() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
@@ -654,14 +658,13 @@ async fn thread_resume_running_thread_tracks_thread_originator_in_analytics() ->
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
thread_source: Some(ThreadSource::User),
service_name: Some("codex_work_desktop".to_string()),
@@ -778,7 +781,6 @@ async fn thread_resume_returns_rollout_history() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -977,7 +979,6 @@ async fn resume_redaction_fixture(client_name: Option<&str>) -> Result<ThreadRes
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
if let Some(client_name) = client_name {
@@ -1091,7 +1092,6 @@ async fn thread_resume_can_skip_turns_for_metadata_only_resume() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -1142,7 +1142,6 @@ async fn thread_resume_rejects_archived_session_by_id() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -1185,14 +1184,13 @@ async fn thread_resume_keeps_paused_goal_paused() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.2-codex".to_string()),
..Default::default()
})
@@ -1295,14 +1293,13 @@ async fn thread_goal_set_preserves_budget_limited_same_objective() -> Result<()>
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.2-codex".to_string()),
..Default::default()
})
@@ -1399,14 +1396,13 @@ async fn thread_goal_set_persists_resumable_stopped_statuses() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.2-codex".to_string()),
..Default::default()
})
@@ -1499,7 +1495,6 @@ async fn thread_goal_set_edits_objective_without_resetting_usage() -> Result<()>
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
@@ -1616,14 +1611,13 @@ async fn thread_goal_lifecycle_emits_analytics_and_clear_deletes_goal() -> Resul
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT.saturating_mul(2), mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.2-codex".to_string()),
..Default::default()
})
@@ -1806,7 +1800,6 @@ async fn thread_resume_emits_restored_token_usage_before_next_turn() -> Result<(
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -1863,7 +1856,6 @@ async fn thread_resume_skips_restored_token_usage_when_turns_are_excluded() -> R
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -1975,7 +1967,6 @@ async fn thread_resume_token_usage_replay_ignores_stale_interrupted_tail_turn()
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -2103,7 +2094,6 @@ async fn thread_resume_token_usage_replay_can_belong_to_interrupted_turn() -> Re
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -2301,7 +2291,6 @@ stream_max_retries = 0
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -2399,7 +2388,6 @@ async fn thread_resume_and_read_interrupt_incomplete_rollout_turn_when_thread_is
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -2477,7 +2465,6 @@ async fn thread_resume_defers_updated_at_until_turn_start() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -2603,13 +2590,12 @@ async fn thread_resume_keeps_in_flight_turn_streaming() -> Result<()> {
let mut primary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -2646,7 +2632,6 @@ async fn thread_resume_keeps_in_flight_turn_streaming() -> Result<()> {
let mut secondary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, secondary.initialize()).await??;
@@ -2720,13 +2705,12 @@ async fn thread_resume_rejects_history_when_thread_is_running() -> Result<()> {
let mut primary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -2843,13 +2827,12 @@ async fn thread_resume_rejects_mismatched_path_for_running_thread_id() -> Result
let mut primary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -3016,13 +2999,12 @@ async fn thread_resume_rejoins_running_thread_even_with_override_mismatch() -> R
let mut primary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -3151,13 +3133,12 @@ async fn thread_resume_can_skip_turns_when_thread_is_running() -> Result<()> {
let mut primary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -3193,7 +3174,6 @@ async fn thread_resume_can_skip_turns_when_thread_is_running() -> Result<()> {
let mut secondary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, secondary.initialize()).await??;
@@ -3223,6 +3203,12 @@ async fn thread_resume_can_skip_turns_when_thread_is_running() -> Result<()> {
#[tokio::test]
async fn thread_resume_replays_pending_command_execution_request_approval() -> Result<()> {
// TODO(anp): Remove after shell approval replay can route target-native cwd across host OSes.
skip_if_wine_exec!(
Ok(()),
"shell approval replay rejects the Windows cwd on the Linux host"
);
let responses = vec![
create_final_assistant_message_sse_response("seeded")?,
create_shell_command_sse_response(
@@ -3243,13 +3229,12 @@ async fn thread_resume_replays_pending_command_execution_request_approval() -> R
let mut primary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -3365,6 +3350,12 @@ async fn thread_resume_replays_pending_command_execution_request_approval() -> R
#[tokio::test]
async fn thread_resume_replays_pending_file_change_request_approval() -> Result<()> {
// TODO(anp): Remove after apply-patch approval fixtures use a target-native workspace.
skip_if_remote!(
Ok(()),
"apply-patch approval fixture is only materialized on the host"
);
let tmp = TempDir::new()?;
let codex_home = tmp.path().join("codex_home");
std::fs::create_dir(&codex_home)?;
@@ -3386,13 +3377,12 @@ async fn thread_resume_replays_pending_file_change_request_approval() -> Result<
let mut primary = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -3611,7 +3601,6 @@ async fn thread_resume_fails_when_required_mcp_server_fails_to_initialize() -> R
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -3694,7 +3683,6 @@ async fn thread_resume_surfaces_cloud_config_bundle_load_errors() -> Result<()>
let refresh_token_url = format!("{}/oauth/token", server.uri());
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[
("OPENAI_API_KEY", None),
(
@@ -3789,7 +3777,6 @@ async fn thread_resume_can_load_source_by_external_path() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -3883,13 +3870,12 @@ async fn start_materialized_thread_and_restart(
) -> Result<RestartedThreadFixture> {
let mut first_mcp = TestAppServer::builder()
.with_codex_home(codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, first_mcp.initialize()).await??;
let start_id = first_mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.4".to_string()),
..Default::default()
})
@@ -3946,7 +3932,6 @@ async fn start_materialized_thread_and_restart(
let mut second_mcp = TestAppServer::builder()
.with_codex_home(codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, second_mcp.initialize()).await??;
@@ -3981,13 +3966,12 @@ async fn thread_resume_accepts_personality_override() -> Result<()> {
let mut primary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
let start_id = primary
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("gpt-5.3-codex".to_string()),
..Default::default()
})
@@ -4023,7 +4007,6 @@ async fn thread_resume_accepts_personality_override() -> Result<()> {
let mut secondary = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, secondary.initialize()).await??;
@@ -46,7 +46,6 @@ async fn thread_settings_update_emits_notification_and_updates_future_turns() ->
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -113,7 +112,6 @@ async fn thread_settings_update_cwd_retargets_default_environment() -> Result<()
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -167,7 +165,6 @@ async fn thread_settings_update_while_turn_is_active_emits_notification() -> Res
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -214,7 +211,6 @@ async fn thread_settings_update_null_service_tier_uses_default() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -284,7 +280,6 @@ async fn thread_settings_update_rejects_sandbox_policy_with_permissions() -> Res
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -322,7 +317,6 @@ async fn turn_start_settings_override_emits_thread_settings_updated() -> Result<
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_TIMEOUT, mcp.initialize()).await??;
@@ -402,7 +396,7 @@ async fn start_text_turn(mcp: &mut TestAppServer, thread_id: String) -> Result<(
async fn start_thread(mcp: &mut TestAppServer) -> Result<ThreadStartResponse> {
let request_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -62,6 +62,7 @@ async fn thread_shell_command_history_responses_exclude_persisted_command_execut
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.as_path())
// thread/shellCommand intentionally executes on the app-server host.
.without_auto_env()
.build()
.await?;
@@ -199,6 +200,7 @@ async fn thread_shell_command_returns_error_when_local_environment_is_disabled()
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.as_path())
// This test intentionally exercises thread/shellCommand without a local host environment.
.without_auto_env()
.with_env_overrides(&[(CODEX_EXEC_SERVER_URL_ENV_VAR, Some("none"))])
.build()
@@ -259,6 +261,7 @@ async fn thread_shell_command_uses_existing_active_turn() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.as_path())
// thread/shellCommand intentionally joins the app-server's host-local active turn.
.without_auto_env()
.build()
.await?;
@@ -525,13 +525,12 @@ async fn thread_start_accepts_absolute_runtime_workspace_roots() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let req_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(cwd.to_string_lossy().to_string()),
runtime_workspace_roots: Some(vec![extra_root.abs()]),
..Default::default()
@@ -570,13 +569,12 @@ async fn thread_start_excludes_profile_workspace_roots_from_runtime_workspace_ro
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let req_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(cwd.path().to_string_lossy().to_string()),
..Default::default()
})
@@ -609,7 +607,6 @@ async fn thread_start_rejects_unknown_environment_as_invalid_request() -> Result
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -648,15 +645,15 @@ async fn thread_start_rejects_relative_environment_cwd_as_invalid_request() -> R
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let environment_id = mcp.auto_env_params()?.environment_id;
let request_id = mcp
.send_thread_start_request(ThreadStartParams {
environments: Some(vec![TurnEnvironmentParams {
environment_id: "local".to_string(),
environment_id: environment_id.clone(),
cwd: serde_json::from_value(json!("relative"))?,
}]),
..Default::default()
@@ -672,7 +669,9 @@ async fn thread_start_rejects_relative_environment_cwd_as_invalid_request() -> R
assert_eq!(error.error.code, INVALID_REQUEST_ERROR_CODE);
assert_eq!(
error.error.message,
"invalid cwd for environment `local`: path `relative` does not use absolute POSIX or Windows path syntax"
format!(
"invalid cwd for environment `{environment_id}`: path `relative` does not use absolute POSIX or Windows path syntax"
)
);
Ok(())
@@ -691,6 +690,7 @@ async fn thread_start_response_includes_loaded_instruction_sources() -> Result<(
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
// TODO(anp): Move the instruction-source fixture into the auto environment cwd.
.without_auto_env()
.build()
.await?;
@@ -742,6 +742,7 @@ async fn thread_start_response_excludes_empty_project_instruction_source() -> Re
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
// TODO(anp): Move the instruction-source fixture into the auto environment cwd.
.without_auto_env()
.build()
.await?;
@@ -789,7 +790,6 @@ async fn thread_start_without_selected_environment_includes_only_global_instruct
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -883,14 +883,13 @@ async fn thread_start_tracks_thread_initialized_analytics() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let req_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
thread_source: Some(ThreadSource::User),
service_name: Some("codex_work_desktop".to_string()),
..Default::default()
@@ -938,13 +937,12 @@ model_reasoning_effort = "high"
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let req_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().to_string_lossy().into_owned()),
..Default::default()
})
@@ -1294,7 +1292,6 @@ async fn thread_start_surfaces_cloud_config_bundle_load_errors() -> Result<()> {
let refresh_token_url = format!("{}/oauth/token", server.uri());
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[
("OPENAI_API_KEY", None),
(
@@ -1307,7 +1304,7 @@ async fn thread_start_surfaces_cloud_config_bundle_load_errors() -> Result<()> {
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let req_id = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let err: JSONRPCError = timeout(
@@ -1355,13 +1352,12 @@ model_reasoning_effort = "high"
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let first_request = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().display().to_string()),
sandbox: Some(SandboxMode::WorkspaceWrite),
..Default::default()
@@ -1374,7 +1370,7 @@ model_reasoning_effort = "high"
.await??;
let second_request = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().display().to_string()),
..Default::default()
})
@@ -1419,13 +1415,12 @@ async fn thread_start_with_nested_git_cwd_trusts_repo_root() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let request_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(nested.display().to_string()),
sandbox: Some(SandboxMode::WorkspaceWrite),
..Default::default()
@@ -1461,13 +1456,12 @@ async fn thread_start_with_read_only_sandbox_does_not_persist_project_trust() ->
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let request_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().display().to_string()),
..Default::default()
})
@@ -1503,13 +1497,12 @@ async fn thread_start_preserves_untrusted_project_trust() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let request_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().display().to_string()),
sandbox: Some(SandboxMode::WorkspaceWrite),
..Default::default()
@@ -1548,13 +1541,12 @@ model_reasoning_effort = "high"
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let request_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().display().to_string()),
sandbox: Some(SandboxMode::WorkspaceWrite),
..Default::default()
@@ -30,14 +30,13 @@ async fn thread_status_changed_emits_runtime_updates() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("RUST_LOG", Some("info"))])
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -44,12 +44,23 @@ async fn thread_unsubscribe_keeps_thread_loaded_until_idle_timeout() -> Result<(
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_id = start_thread(&mut mcp).await?;
let thread_req = mcp
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(thread_req)),
)
.await??;
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(thread_resp)?;
let thread_id = thread.id;
let unsubscribe_id = mcp
.send_thread_unsubscribe_request(ThreadUnsubscribeParams {
@@ -99,8 +110,6 @@ async fn thread_unsubscribe_during_turn_keeps_turn_running() -> Result<()> {
let tmp = TempDir::new()?;
let codex_home = tmp.path().join("codex_home");
std::fs::create_dir(&codex_home)?;
let working_directory = tmp.path().join("workdir");
std::fs::create_dir(&working_directory)?;
let (server, mut completions) = start_streaming_sse_server(vec![
vec![StreamingSseChunk {
@@ -127,13 +136,12 @@ async fn thread_unsubscribe_during_turn_keeps_turn_running() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
dynamic_tools: Some(vec![DynamicToolSpec::Function(DynamicToolFunctionSpec {
name: tool_name.to_string(),
@@ -164,7 +172,6 @@ async fn thread_unsubscribe_during_turn_keeps_turn_running() -> Result<()> {
text: "run deterministic tool".to_string(),
text_elements: Vec::new(),
}],
cwd: Some(working_directory),
..Default::default()
})
.await?;
@@ -263,12 +270,23 @@ async fn thread_unsubscribe_preserves_cached_status_before_idle_unload() -> Resu
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_id = start_thread(&mut mcp).await?;
let thread_req = mcp
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(thread_req)),
)
.await??;
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(thread_resp)?;
let thread_id = thread.id;
let turn_req = mcp
.send_turn_start_request(TurnStartParams {
@@ -354,12 +372,23 @@ async fn thread_unsubscribe_reports_not_subscribed_before_idle_unload() -> Resul
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_id = start_thread(&mut mcp).await?;
let thread_req = mcp
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(thread_req)),
)
.await??;
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(thread_resp)?;
let thread_id = thread.id;
let first_unsubscribe_id = mcp
.send_thread_unsubscribe_request(ThreadUnsubscribeParams {
@@ -434,19 +463,3 @@ stream_max_retries = 0
),
)
}
async fn start_thread(mcp: &mut TestAppServer) -> Result<String> {
let req_id = mcp
.send_thread_start_request(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
.await?;
let resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(req_id)),
)
.await??;
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(resp)?;
Ok(thread.id)
}
@@ -22,6 +22,7 @@ use codex_app_server_protocol::TurnStartParams;
use codex_app_server_protocol::TurnStartResponse;
use codex_app_server_protocol::TurnStatus;
use codex_app_server_protocol::UserInput as V2UserInput;
use core_test_support::skip_if_remote;
use tempfile::TempDir;
use tokio::time::timeout;
@@ -30,6 +31,12 @@ const INVALID_REQUEST_ERROR_CODE: i64 = -32600;
#[tokio::test]
async fn turn_interrupt_aborts_running_turn() -> Result<()> {
// TODO(anp): Remove after the long-running command fixture can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"uses a host-local command and cwd fixture unavailable to remote executors"
);
// Use a portable sleep command to keep the turn running.
#[cfg(target_os = "windows")]
let shell_command = vec![
@@ -59,14 +66,13 @@ async fn turn_interrupt_aborts_running_turn() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
// Start a v2 thread and capture its id.
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -215,6 +221,12 @@ async fn turn_interrupt_rejects_completed_turn() -> Result<()> {
#[tokio::test]
async fn turn_interrupt_resolves_pending_command_approval_request() -> Result<()> {
// TODO(anp): Remove after the approval command fixture can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"uses a host-local command and cwd fixture unavailable to remote executors"
);
#[cfg(target_os = "windows")]
let shell_command = vec![
"powershell".to_string(),
@@ -245,13 +257,12 @@ async fn turn_interrupt_resolves_pending_command_approval_request() -> Result<()
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -80,6 +80,8 @@ use codex_protocol::user_input::MAX_USER_INPUT_TEXT_CHARS;
use codex_utils_absolute_path::test_support::PathExt;
use core_test_support::responses;
use core_test_support::skip_if_no_network;
use core_test_support::skip_if_remote;
use core_test_support::skip_if_wine_exec;
use pretty_assertions::assert_eq;
use serde_json::Value;
use serde_json::json;
@@ -618,7 +620,6 @@ async fn turn_start_emits_thread_scoped_warning_notification_for_trimmed_skills(
let isolated_home = codex_home.path().to_string_lossy();
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[
("HOME", Some(isolated_home.as_ref())),
("USERPROFILE", Some(isolated_home.as_ref())),
@@ -628,7 +629,7 @@ async fn turn_start_emits_thread_scoped_warning_notification_for_trimmed_skills(
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
@@ -891,14 +892,13 @@ async fn turn_start_tracks_thread_originator_in_analytics() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
thread_source: Some(ThreadSource::User),
service_name: Some("codex_work_desktop".to_string()),
@@ -1021,14 +1021,13 @@ async fn turn_profile_tracks_blocking_tool_and_follow_up_sampling() -> Result<()
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -1348,13 +1347,12 @@ async fn turn_start_rejects_unknown_environment_before_starting_turn() -> Result
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -2214,6 +2212,11 @@ async fn turn_start_forwards_custom_local_image_detail() -> Result<()> {
#[tokio::test]
async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
// TODO(anp): Remove after shell-command approval routing supports target-native Windows cwd.
skip_if_wine_exec!(
Ok(()),
"shell-command approval routing requires a host-native cwd under Wine-exec"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -2256,14 +2259,14 @@ async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.as_path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let expected_environment_id = mcp.auto_env_params()?.environment_id;
// thread/start
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -2304,7 +2307,10 @@ async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
panic!("expected CommandExecutionRequestApproval request");
};
assert_eq!(params.item_id, "call1");
assert_eq!(params.environment_id.as_deref(), Some("local"));
assert_eq!(
params.environment_id.as_deref(),
Some(expected_environment_id.as_str())
);
let resolved_request_id = request_id.clone();
// Approve and wait for task completion
@@ -2376,12 +2382,15 @@ async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
#[tokio::test]
async fn turn_start_exec_approval_decline_v2() -> Result<()> {
// TODO(anp): Remove after command approval routing accepts target-native Windows cwd.
skip_if_wine_exec!(
Ok(()),
"command approval routing rejects the selected Windows cwd on the Linux host"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
let codex_home = tmp.path().to_path_buf();
let workspace = tmp.path().join("workspace");
std::fs::create_dir(&workspace)?;
let responses = vec![
create_shell_command_sse_response(
@@ -2406,13 +2415,12 @@ async fn turn_start_exec_approval_decline_v2() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.as_path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -2432,7 +2440,6 @@ async fn turn_start_exec_approval_decline_v2() -> Result<()> {
text: "run python".to_string(),
text_elements: Vec::new(),
}],
cwd: Some(workspace.clone()),
..Default::default()
})
.await?;
@@ -2525,6 +2532,8 @@ async fn turn_start_exec_approval_decline_v2() -> Result<()> {
#[tokio::test]
async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
// TODO(anp): Materialize cwd and shell-display fixtures in the selected remote environment.
skip_if_remote!(Ok(()), "cwd fixtures are only materialized on the host");
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -2563,14 +2572,13 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
// thread/start
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -2761,13 +2769,12 @@ stream_max_retries = 0
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -2876,6 +2883,8 @@ url = "ws://127.0.0.1:1"
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
// This test owns environments.toml and explicitly compares local selections
// with a configured remote environment, so auto env would change its subject.
.without_auto_env()
.build()
.await?;
@@ -3008,6 +3017,11 @@ fn environment_params(ids: Option<&[&str]>, cwd: &Path) -> Option<Vec<TurnEnviro
#[tokio::test]
async fn turn_start_file_change_approval_v2() -> Result<()> {
// TODO(anp): Materialize apply-patch workspaces in the selected remote environment.
skip_if_remote!(
Ok(()),
"apply-patch workspace fixture is only materialized on the host"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -3035,13 +3049,12 @@ async fn turn_start_file_change_approval_v2() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -3181,6 +3194,11 @@ async fn turn_start_file_change_approval_v2() -> Result<()> {
#[tokio::test]
async fn turn_start_does_not_stream_apply_patch_change_updates_without_feature_v2() -> Result<()> {
// TODO(anp): Materialize apply-patch workspaces in the selected remote environment.
skip_if_remote!(
Ok(()),
"apply-patch workspace fixture is only materialized on the host"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -3230,13 +3248,12 @@ async fn turn_start_does_not_stream_apply_patch_change_updates_without_feature_v
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -3283,6 +3300,11 @@ async fn turn_start_does_not_stream_apply_patch_change_updates_without_feature_v
#[tokio::test]
async fn turn_start_streams_apply_patch_change_updates_v2() -> Result<()> {
// TODO(anp): Materialize apply-patch workspaces in the selected remote environment.
skip_if_remote!(
Ok(()),
"apply-patch workspace fixture is only materialized on the host"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -3372,13 +3394,12 @@ async fn turn_start_streams_apply_patch_change_updates_v2() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -4020,6 +4041,11 @@ config_file = "./custom-role.toml"
#[tokio::test]
async fn turn_start_file_change_approval_accept_for_session_persists_v2() -> Result<()> {
// TODO(anp): Materialize apply-patch workspaces in the selected remote environment.
skip_if_remote!(
Ok(()),
"apply-patch workspace fixture is only materialized on the host"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -4057,13 +4083,12 @@ async fn turn_start_file_change_approval_accept_for_session_persists_v2() -> Res
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -4207,6 +4232,11 @@ async fn turn_start_file_change_approval_accept_for_session_persists_v2() -> Res
#[tokio::test]
async fn turn_start_file_change_approval_decline_v2() -> Result<()> {
// TODO(anp): Materialize apply-patch workspaces in the selected remote environment.
skip_if_remote!(
Ok(()),
"apply-patch workspace fixture is only materialized on the host"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -4234,13 +4264,12 @@ async fn turn_start_file_change_approval_decline_v2() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -4367,6 +4396,11 @@ async fn turn_start_file_change_approval_decline_v2() -> Result<()> {
#[tokio::test]
#[cfg_attr(windows, ignore = "process id reporting differs on Windows")]
async fn command_execution_notifications_include_process_id() -> Result<()> {
// TODO(anp): Add target-Windows process-id expectations for remote executors.
skip_if_wine_exec!(
Ok(()),
"process id reporting differs for a Windows executor"
);
skip_if_no_network!(Ok(()));
let responses = vec![
@@ -4385,13 +4419,12 @@ async fn command_execution_notifications_include_process_id() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -4523,13 +4556,12 @@ async fn turn_start_with_elevated_override_does_not_persist_project_trust() -> R
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_request = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
cwd: Some(workspace.path().display().to_string()),
..Default::default()
})
@@ -34,6 +34,7 @@ use codex_features::FEATURES;
use codex_features::Feature;
use core_test_support::responses;
use core_test_support::skip_if_no_network;
use core_test_support::skip_if_remote;
use pretty_assertions::assert_eq;
use std::collections::BTreeMap;
use std::path::Path;
@@ -48,6 +49,11 @@ const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs
#[tokio::test]
async fn turn_start_shell_zsh_fork_executes_command_v2() -> Result<()> {
// TODO(anp): Remove after zsh-fork fixtures can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"zsh-fork fixtures use host-local zsh and workspace paths"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -101,7 +107,7 @@ async fn turn_start_shell_zsh_fork_executes_command_v2() -> Result<()> {
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -177,6 +183,11 @@ async fn turn_start_shell_zsh_fork_executes_command_v2() -> Result<()> {
#[tokio::test]
async fn turn_start_shell_zsh_fork_exec_approval_decline_v2() -> Result<()> {
// TODO(anp): Remove after zsh-fork fixtures can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"zsh-fork fixtures use host-local zsh and workspace paths"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -220,7 +231,7 @@ async fn turn_start_shell_zsh_fork_exec_approval_decline_v2() -> Result<()> {
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -313,6 +324,11 @@ async fn turn_start_shell_zsh_fork_exec_approval_decline_v2() -> Result<()> {
#[tokio::test]
async fn turn_start_shell_zsh_fork_exec_approval_cancel_v2() -> Result<()> {
// TODO(anp): Remove after zsh-fork fixtures can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"zsh-fork fixtures use host-local zsh and workspace paths"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -353,7 +369,7 @@ async fn turn_start_shell_zsh_fork_exec_approval_cancel_v2() -> Result<()> {
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -444,6 +460,11 @@ async fn turn_start_shell_zsh_fork_exec_approval_cancel_v2() -> Result<()> {
#[tokio::test]
async fn turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2() -> Result<()> {
// TODO(anp): Remove after zsh-fork fixtures can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"zsh-fork fixtures use host-local zsh and workspace paths"
);
skip_if_no_network!(Ok(()));
let tmp = TempDir::new()?;
@@ -512,7 +533,7 @@ async fn turn_start_shell_zsh_fork_subcommand_decline_marks_parent_declined_v2()
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let start_id = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
cwd: Some(workspace.to_string_lossy().into_owned()),
..Default::default()
@@ -748,7 +769,6 @@ async fn create_zsh_test_mcp_process(
let zdotdir = zdotdir.to_string_lossy().into_owned();
TestAppServer::builder()
.with_codex_home(codex_home)
.without_auto_env()
.with_program(&app_server)
.with_env_overrides(&[("ZDOTDIR", Some(zdotdir.as_str()))])
.build()
@@ -26,6 +26,7 @@ use codex_app_server_protocol::TurnSteerParams;
use codex_app_server_protocol::TurnSteerResponse;
use codex_app_server_protocol::UserInput as V2UserInput;
use codex_protocol::user_input::MAX_USER_INPUT_TEXT_CHARS;
use core_test_support::skip_if_remote;
use serde_json::Value;
use std::collections::HashMap;
use tempfile::TempDir;
@@ -52,14 +53,13 @@ async fn turn_steer_requires_active_turn() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -111,6 +111,12 @@ async fn turn_steer_requires_active_turn() -> Result<()> {
#[tokio::test]
async fn turn_steer_rejects_oversized_text_input() -> Result<()> {
// TODO(anp): Remove after the active-turn fixture can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"uses a host-local command and cwd fixture unavailable to remote executors"
);
#[cfg(target_os = "windows")]
let shell_command = vec![
"powershell".to_string(),
@@ -143,14 +149,13 @@ async fn turn_steer_rejects_oversized_text_input() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -228,6 +233,12 @@ async fn turn_steer_rejects_oversized_text_input() -> Result<()> {
#[tokio::test]
async fn turn_steer_returns_active_turn_id() -> Result<()> {
// TODO(anp): Remove after the active-turn fixture can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"uses a host-local command and cwd fixture unavailable to remote executors"
);
#[cfg(target_os = "windows")]
let shell_command = vec![
"powershell".to_string(),
@@ -262,14 +273,13 @@ async fn turn_steer_returns_active_turn_id() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -379,6 +389,12 @@ async fn turn_steer_returns_active_turn_id() -> Result<()> {
#[tokio::test]
async fn turn_steer_rejects_context_only_input_without_merging_context() -> Result<()> {
// TODO(anp): Remove after the active-turn fixture can run in the selected remote environment.
skip_if_remote!(
Ok(()),
"uses a host-local command and cwd fixture unavailable to remote executors"
);
let tmp = TempDir::new()?;
let codex_home = tmp.path().join("codex_home");
std::fs::create_dir(&codex_home)?;
@@ -404,14 +420,13 @@ async fn turn_steer_rejects_context_only_input_without_merging_context() -> Resu
let mut mcp = TestAppServer::builder()
.with_codex_home(&codex_home)
.without_auto_env()
.without_managed_config()
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams {
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
@@ -81,14 +81,13 @@ async fn standalone_web_search_round_trips_output() -> Result<()> {
let mut mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let thread_req = mcp
.send_thread_start_request(ThreadStartParams::default())
.send_thread_start_request_with_auto_env(ThreadStartParams::default())
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
@@ -203,7 +202,6 @@ async fn standalone_web_search_round_trips_output() -> Result<()> {
drop(mcp);
let mut reloaded_mcp = TestAppServer::builder()
.with_codex_home(codex_home.path())
.without_auto_env()
.with_env_overrides(&[("OPENAI_API_KEY", None)])
.build()
.await?;