diff --git a/codex-rs/app-server/tests/common/lib.rs b/codex-rs/app-server/tests/common/lib.rs index 6bb7372162..2c06248da5 100644 --- a/codex-rs/app-server/tests/common/lib.rs +++ b/codex-rs/app-server/tests/common/lib.rs @@ -37,6 +37,7 @@ pub use responses::create_final_assistant_message_sse_response; pub use responses::create_request_permissions_sse_response; pub use responses::create_request_user_input_sse_response; pub use responses::create_shell_command_sse_response; +pub use responses::create_shell_command_sse_response_from_command; pub use rollout::create_fake_rollout; pub use rollout::create_fake_rollout_with_source; pub use rollout::create_fake_rollout_with_text_elements; diff --git a/codex-rs/app-server/tests/common/responses.rs b/codex-rs/app-server/tests/common/responses.rs index 586d1446c0..8deda2fc27 100644 --- a/codex-rs/app-server/tests/common/responses.rs +++ b/codex-rs/app-server/tests/common/responses.rs @@ -10,11 +10,31 @@ pub fn create_shell_command_sse_response( ) -> anyhow::Result { // The `arguments` for the `shell_command` tool is a serialized JSON object. let command_str = shlex::try_join(command.iter().map(String::as_str))?; - let tool_call_arguments = serde_json::to_string(&json!({ - "command": command_str, + create_shell_command_sse_response_from_command(&command_str, workdir, timeout_ms, call_id, None) +} + +pub fn create_shell_command_sse_response_from_command( + command: &str, + workdir: Option<&Path>, + timeout_ms: Option, + call_id: &str, + login: Option, +) -> anyhow::Result { + // Use this when a test already has a shell command string. It fixes string + // quoting for those callers by avoiding a rebuild from argv with POSIX + // rules, which can change how Windows PowerShell parses the command; for + // sleep-based tests, nested parsing can add another PowerShell startup + // before the requested sleep even begins. + let mut tool_call_arguments = json!({ + "command": command, "workdir": workdir.map(|w| w.to_string_lossy()), "timeout_ms": timeout_ms - }))?; + }); + if let Some(login) = login { + tool_call_arguments["login"] = json!(login); + } + + let tool_call_arguments = serde_json::to_string(&tool_call_arguments)?; Ok(responses::sse(vec![ responses::ev_response_created("resp-1"), responses::ev_function_call(call_id, "shell_command", &tool_call_arguments), 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 6aab3d186f..41ad4ecda6 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_unsubscribe.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_unsubscribe.rs @@ -4,7 +4,7 @@ use app_test_support::McpProcess; 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_unchecked; -use app_test_support::create_shell_command_sse_response; +use app_test_support::create_shell_command_sse_response_from_command; use app_test_support::to_response; use codex_app_server_protocol::ItemStartedNotification; use codex_app_server_protocol::JSONRPCResponse; @@ -129,13 +129,9 @@ async fn thread_unsubscribe_keeps_thread_loaded_until_idle_timeout() -> Result<( #[tokio::test] async fn thread_unsubscribe_during_turn_keeps_turn_running() -> Result<()> { #[cfg(target_os = "windows")] - let shell_command = vec![ - "powershell".to_string(), - "-Command".to_string(), - "Start-Sleep -Seconds 1".to_string(), - ]; + let shell_command = "Start-Sleep -Seconds 1"; #[cfg(not(target_os = "windows"))] - let shell_command = vec!["sleep".to_string(), "1".to_string()]; + let shell_command = "sleep 1"; let tmp = TempDir::new()?; let codex_home = tmp.path().join("codex_home"); @@ -144,11 +140,12 @@ async fn thread_unsubscribe_during_turn_keeps_turn_running() -> Result<()> { std::fs::create_dir(&working_directory)?; let server = create_mock_responses_server_sequence_unchecked(vec![ - create_shell_command_sse_response( - shell_command.clone(), + create_shell_command_sse_response_from_command( + shell_command, Some(&working_directory), Some(10_000), "call_sleep", + Some(false), )?, create_final_assistant_message_sse_response("Done")?, ])