diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 73c1117a..653e205b 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -797,6 +797,7 @@ dependencies = [ "chrono", "codex-core", "codex-protocol", + "core_test_support", "rand 0.8.5", "reqwest", "serde", @@ -835,6 +836,7 @@ dependencies = [ "codex-core", "codex-login", "codex-protocol", + "core_test_support", "mcp-types", "mcp_test_support", "os_info", diff --git a/codex-rs/core/tests/common/lib.rs b/codex-rs/core/tests/common/lib.rs index 95af79b2..9b86694e 100644 --- a/codex-rs/core/tests/common/lib.rs +++ b/codex-rs/core/tests/common/lib.rs @@ -126,3 +126,21 @@ where } } } + +#[macro_export] +macro_rules! non_sandbox_test { + // For tests that return () + () => {{ + if ::std::env::var("CODEX_SANDBOX_NETWORK_DISABLED").is_ok() { + println!("Skipping test because it cannot execute when network is disabled in a Codex sandbox."); + return; + } + }}; + // For tests that return Result<(), _> + (result $(,)?) => {{ + if ::std::env::var("CODEX_SANDBOX_NETWORK_DISABLED").is_ok() { + println!("Skipping test because it cannot execute when network is disabled in a Codex sandbox."); + return ::core::result::Result::Ok(()); + } + }}; +} diff --git a/codex-rs/core/tests/suite/cli_stream.rs b/codex-rs/core/tests/suite/cli_stream.rs index 1a0c0162..368c47dc 100644 --- a/codex-rs/core/tests/suite/cli_stream.rs +++ b/codex-rs/core/tests/suite/cli_stream.rs @@ -1,7 +1,7 @@ use assert_cmd::Command as AssertCommand; use codex_core::RolloutRecorder; use codex_core::protocol::GitInfo; -use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; +use core_test_support::non_sandbox_test; use std::time::Duration; use std::time::Instant; use tempfile::TempDir; @@ -21,12 +21,7 @@ use wiremock::matchers::path; /// 4. Ensures the response is received exactly once and contains "hi" #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn chat_mode_stream_cli() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = MockServer::start().await; let sse = concat!( @@ -102,12 +97,7 @@ async fn chat_mode_stream_cli() { /// received by a mock OpenAI Responses endpoint. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_cli_applies_experimental_instructions_file() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Start mock server which will capture the request and return a minimal // SSE stream for a single turn. @@ -195,12 +185,7 @@ async fn exec_cli_applies_experimental_instructions_file() { /// 4. Ensures the fixture content is correctly streamed through the CLI #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn responses_api_stream_cli() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let fixture = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/cli_responses_fixture.sse"); @@ -232,12 +217,7 @@ async fn responses_api_stream_cli() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn integration_creates_and_checks_session_file() { // Honor sandbox network restrictions for CI parity with the other tests. - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // 1. Temp home so we read/write isolated session files. let home = TempDir::new().unwrap(); diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index d0ae608c..002a4b1f 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -16,12 +16,12 @@ use codex_core::built_in_model_providers; use codex_core::protocol::EventMsg; use codex_core::protocol::InputItem; use codex_core::protocol::Op; -use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use codex_protocol::mcp_protocol::ConversationId; use codex_protocol::models::ReasoningItemReasoningSummary; use codex_protocol::models::WebSearchAction; use core_test_support::load_default_config_for_test; use core_test_support::load_sse_fixture_with_id; +use core_test_support::non_sandbox_test; use core_test_support::responses; use core_test_support::wait_for_event; use futures::StreamExt; @@ -126,12 +126,7 @@ fn write_auth_json( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn resume_includes_initial_messages_and_sends_prior_items() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Create a fake rollout session file with prior user + system + assistant messages. let tmpdir = TempDir::new().unwrap(); @@ -297,12 +292,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn includes_conversation_id_and_model_headers_in_request() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Mock server let server = MockServer::start().await; @@ -427,12 +417,7 @@ async fn includes_base_instructions_override_in_request() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn chatgpt_auth_sends_correct_request() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Mock server let server = MockServer::start().await; @@ -506,12 +491,7 @@ async fn chatgpt_auth_sends_correct_request() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Mock server let server = MockServer::start().await; @@ -638,12 +618,7 @@ async fn includes_user_instructions_message_in_request() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn azure_responses_request_includes_store_and_reasoning_ids() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = MockServer::start().await; @@ -1036,12 +1011,7 @@ fn create_dummy_codex_auth() -> CodexAuth { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn history_dedupes_streamed_and_final_messages_across_turns() { // Skip under Codex sandbox network restrictions (mirrors other tests). - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Mock server that will receive three sequential requests and return the same SSE stream // each time: a few deltas, then a final assistant message, then completed. diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index a58de304..3cae9841 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -9,9 +9,7 @@ use codex_core::protocol::InputItem; use codex_core::protocol::Op; use codex_core::protocol::RolloutItem; use codex_core::protocol::RolloutLine; -use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use core_test_support::load_default_config_for_test; -use core_test_support::responses; use core_test_support::wait_for_event; use tempfile::TempDir; use wiremock::Mock; @@ -21,11 +19,16 @@ use wiremock::ResponseTemplate; use wiremock::matchers::method; use wiremock::matchers::path; +use core_test_support::non_sandbox_test; +use core_test_support::responses::ev_assistant_message; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_completed_with_tokens; +use core_test_support::responses::ev_function_call; +use core_test_support::responses::mount_sse_once; +use core_test_support::responses::sse; +use core_test_support::responses::sse_response; +use core_test_support::responses::start_mock_server; use pretty_assertions::assert_eq; -use responses::ev_assistant_message; -use responses::ev_completed; -use responses::sse; -use responses::start_mock_server; use std::sync::Arc; use std::sync::Mutex; use std::sync::atomic::AtomicUsize; @@ -50,12 +53,7 @@ const DUMMY_CALL_ID: &str = "call-multi-auto"; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn summarize_context_three_requests_and_instructions() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Set up a mock server that we can inspect after the run. let server = start_mock_server().await; @@ -81,19 +79,19 @@ async fn summarize_context_three_requests_and_instructions() { body.contains("\"text\":\"hello world\"") && !body.contains(&format!("\"text\":\"{SUMMARIZE_TRIGGER}\"")) }; - responses::mount_sse_once(&server, first_matcher, sse1).await; + mount_sse_once(&server, first_matcher, sse1).await; let second_matcher = |req: &wiremock::Request| { let body = std::str::from_utf8(&req.body).unwrap_or(""); body.contains(&format!("\"text\":\"{SUMMARIZE_TRIGGER}\"")) }; - responses::mount_sse_once(&server, second_matcher, sse2).await; + mount_sse_once(&server, second_matcher, sse2).await; let third_matcher = |req: &wiremock::Request| { let body = std::str::from_utf8(&req.body).unwrap_or(""); body.contains(&format!("\"text\":\"{THIRD_USER_MSG}\"")) }; - responses::mount_sse_once(&server, third_matcher, sse3).await; + mount_sse_once(&server, third_matcher, sse3).await; // Build config pointing to the mock server and spawn Codex. let model_provider = ModelProviderInfo { @@ -276,28 +274,23 @@ async fn summarize_context_three_requests_and_instructions() { #[cfg_attr(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] async fn auto_compact_runs_after_token_limit_hit() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = start_mock_server().await; let sse1 = sse(vec![ ev_assistant_message("m1", FIRST_REPLY), - responses::ev_completed_with_tokens("r1", 70_000), + ev_completed_with_tokens("r1", 70_000), ]); let sse2 = sse(vec![ ev_assistant_message("m2", "SECOND_REPLY"), - responses::ev_completed_with_tokens("r2", 330_000), + ev_completed_with_tokens("r2", 330_000), ]); let sse3 = sse(vec![ ev_assistant_message("m3", AUTO_SUMMARY_TEXT), - responses::ev_completed_with_tokens("r3", 200), + ev_completed_with_tokens("r3", 200), ]); let first_matcher = |req: &wiremock::Request| { @@ -309,7 +302,7 @@ async fn auto_compact_runs_after_token_limit_hit() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(first_matcher) - .respond_with(responses::sse_response(sse1)) + .respond_with(sse_response(sse1)) .mount(&server) .await; @@ -322,7 +315,7 @@ async fn auto_compact_runs_after_token_limit_hit() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(second_matcher) - .respond_with(responses::sse_response(sse2)) + .respond_with(sse_response(sse2)) .mount(&server) .await; @@ -333,7 +326,7 @@ async fn auto_compact_runs_after_token_limit_hit() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(third_matcher) - .respond_with(responses::sse_response(sse3)) + .respond_with(sse_response(sse3)) .mount(&server) .await; @@ -417,28 +410,23 @@ async fn auto_compact_runs_after_token_limit_hit() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn auto_compact_persists_rollout_entries() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = start_mock_server().await; let sse1 = sse(vec![ ev_assistant_message("m1", FIRST_REPLY), - responses::ev_completed_with_tokens("r1", 70_000), + ev_completed_with_tokens("r1", 70_000), ]); let sse2 = sse(vec![ ev_assistant_message("m2", "SECOND_REPLY"), - responses::ev_completed_with_tokens("r2", 330_000), + ev_completed_with_tokens("r2", 330_000), ]); let sse3 = sse(vec![ ev_assistant_message("m3", AUTO_SUMMARY_TEXT), - responses::ev_completed_with_tokens("r3", 200), + ev_completed_with_tokens("r3", 200), ]); let first_matcher = |req: &wiremock::Request| { @@ -450,7 +438,7 @@ async fn auto_compact_persists_rollout_entries() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(first_matcher) - .respond_with(responses::sse_response(sse1)) + .respond_with(sse_response(sse1)) .mount(&server) .await; @@ -463,7 +451,7 @@ async fn auto_compact_persists_rollout_entries() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(second_matcher) - .respond_with(responses::sse_response(sse2)) + .respond_with(sse_response(sse2)) .mount(&server) .await; @@ -474,7 +462,7 @@ async fn auto_compact_persists_rollout_entries() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(third_matcher) - .respond_with(responses::sse_response(sse3)) + .respond_with(sse_response(sse3)) .mount(&server) .await; @@ -550,28 +538,23 @@ async fn auto_compact_persists_rollout_entries() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn auto_compact_stops_after_failed_attempt() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = start_mock_server().await; let sse1 = sse(vec![ ev_assistant_message("m1", FIRST_REPLY), - responses::ev_completed_with_tokens("r1", 500), + ev_completed_with_tokens("r1", 500), ]); let sse2 = sse(vec![ ev_assistant_message("m2", SUMMARY_TEXT), - responses::ev_completed_with_tokens("r2", 50), + ev_completed_with_tokens("r2", 50), ]); let sse3 = sse(vec![ ev_assistant_message("m3", STILL_TOO_BIG_REPLY), - responses::ev_completed_with_tokens("r3", 500), + ev_completed_with_tokens("r3", 500), ]); let first_matcher = |req: &wiremock::Request| { @@ -582,7 +565,7 @@ async fn auto_compact_stops_after_failed_attempt() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(first_matcher) - .respond_with(responses::sse_response(sse1.clone())) + .respond_with(sse_response(sse1.clone())) .mount(&server) .await; @@ -593,7 +576,7 @@ async fn auto_compact_stops_after_failed_attempt() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(second_matcher) - .respond_with(responses::sse_response(sse2.clone())) + .respond_with(sse_response(sse2.clone())) .mount(&server) .await; @@ -605,7 +588,7 @@ async fn auto_compact_stops_after_failed_attempt() { Mock::given(method("POST")) .and(path("/v1/responses")) .and(third_matcher) - .respond_with(responses::sse_response(sse3.clone())) + .respond_with(sse_response(sse3.clone())) .mount(&server) .await; @@ -664,38 +647,33 @@ async fn auto_compact_stops_after_failed_attempt() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_events() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = start_mock_server().await; let sse1 = sse(vec![ ev_assistant_message("m1", FIRST_REPLY), - responses::ev_completed_with_tokens("r1", 500), + ev_completed_with_tokens("r1", 500), ]); let sse2 = sse(vec![ ev_assistant_message("m2", FIRST_AUTO_SUMMARY), - responses::ev_completed_with_tokens("r2", 50), + ev_completed_with_tokens("r2", 50), ]); let sse3 = sse(vec![ - responses::ev_function_call(DUMMY_CALL_ID, DUMMY_FUNCTION_NAME, "{}"), - responses::ev_completed_with_tokens("r3", 150), + ev_function_call(DUMMY_CALL_ID, DUMMY_FUNCTION_NAME, "{}"), + ev_completed_with_tokens("r3", 150), ]); let sse4 = sse(vec![ ev_assistant_message("m4", SECOND_LARGE_REPLY), - responses::ev_completed_with_tokens("r4", 450), + ev_completed_with_tokens("r4", 450), ]); let sse5 = sse(vec![ ev_assistant_message("m5", SECOND_AUTO_SUMMARY), - responses::ev_completed_with_tokens("r5", 60), + ev_completed_with_tokens("r5", 60), ]); let sse6 = sse(vec![ ev_assistant_message("m6", FINAL_REPLY), - responses::ev_completed_with_tokens("r6", 120), + ev_completed_with_tokens("r6", 120), ]); #[derive(Clone)] diff --git a/codex-rs/core/tests/suite/review.rs b/codex-rs/core/tests/suite/review.rs index a20807e4..d511946a 100644 --- a/codex-rs/core/tests/suite/review.rs +++ b/codex-rs/core/tests/suite/review.rs @@ -20,9 +20,9 @@ use codex_core::protocol::ReviewOutputEvent; use codex_core::protocol::ReviewRequest; use codex_core::protocol::RolloutItem; use codex_core::protocol::RolloutLine; -use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use core_test_support::load_default_config_for_test; use core_test_support::load_sse_fixture_with_id_from_str; +use core_test_support::non_sandbox_test; use core_test_support::wait_for_event; use pretty_assertions::assert_eq; use std::path::PathBuf; @@ -42,12 +42,7 @@ use wiremock::matchers::path; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn review_op_emits_lifecycle_and_review_output() { // Skip under Codex sandbox network restrictions. - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Start mock Responses API server. Return a single assistant message whose // text is a JSON-encoded ReviewOutputEvent. @@ -172,12 +167,7 @@ async fn review_op_emits_lifecycle_and_review_output() { #[cfg_attr(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] async fn review_op_with_plain_text_emits_review_fallback() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let sse_raw = r#"[ {"type":"response.output_item.done", "item":{ @@ -226,12 +216,7 @@ async fn review_op_with_plain_text_emits_review_fallback() { #[cfg_attr(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] async fn review_does_not_emit_agent_message_on_structured_output() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let review_json = serde_json::json!({ "findings": [ @@ -303,12 +288,7 @@ async fn review_does_not_emit_agent_message_on_structured_output() { /// request uses that model (and not the main chat model). #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn review_uses_custom_review_model_from_config() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Minimal stream: just a completed event let sse_raw = r#"[ @@ -361,12 +341,7 @@ async fn review_uses_custom_review_model_from_config() { #[cfg_attr(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] async fn review_input_isolated_from_parent_history() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Mock server for the single review request let sse_raw = r#"[ @@ -542,12 +517,7 @@ async fn review_input_isolated_from_parent_history() { /// messages in its request `input`. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn review_history_does_not_leak_into_parent_session() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Respond to both the review request and the subsequent parent request. let sse_raw = r#"[ diff --git a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs index 3395eef9..f41534ab 100644 --- a/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs +++ b/codex-rs/core/tests/suite/stream_error_allows_next_turn.rs @@ -7,9 +7,9 @@ use codex_core::WireApi; use codex_core::protocol::EventMsg; use codex_core::protocol::InputItem; use codex_core::protocol::Op; -use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use core_test_support::load_default_config_for_test; use core_test_support::load_sse_fixture_with_id; +use core_test_support::non_sandbox_test; use core_test_support::wait_for_event_with_timeout; use tempfile::TempDir; use wiremock::Mock; @@ -25,12 +25,7 @@ fn sse_completed(id: &str) -> String { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn continue_after_stream_error() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = MockServer::start().await; diff --git a/codex-rs/core/tests/suite/stream_no_completed.rs b/codex-rs/core/tests/suite/stream_no_completed.rs index 6e6938b9..4801376d 100644 --- a/codex-rs/core/tests/suite/stream_no_completed.rs +++ b/codex-rs/core/tests/suite/stream_no_completed.rs @@ -9,10 +9,10 @@ use codex_core::ModelProviderInfo; use codex_core::protocol::EventMsg; use codex_core::protocol::InputItem; use codex_core::protocol::Op; -use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use core_test_support::load_default_config_for_test; use core_test_support::load_sse_fixture; use core_test_support::load_sse_fixture_with_id; +use core_test_support::non_sandbox_test; use tempfile::TempDir; use tokio::time::timeout; use wiremock::Mock; @@ -33,12 +33,7 @@ fn sse_completed(id: &str) -> String { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn retries_on_early_close() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let server = MockServer::start().await; diff --git a/codex-rs/exec/tests/suite/apply_patch.rs b/codex-rs/exec/tests/suite/apply_patch.rs index 489f34f9..9db1f1cf 100644 --- a/codex-rs/exec/tests/suite/apply_patch.rs +++ b/codex-rs/exec/tests/suite/apply_patch.rs @@ -48,14 +48,9 @@ fn test_standalone_exec_cli_can_use_apply_patch() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn test_apply_patch_tool() -> anyhow::Result<()> { use crate::suite::common::run_e2e_exec_test; - use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; + use core_test_support::non_sandbox_test; - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return Ok(()); - } + non_sandbox_test!(result); let tmp_cwd = tempdir().expect("failed to create temp dir"); let tmp_path = tmp_cwd.path().to_path_buf(); @@ -93,14 +88,9 @@ async fn test_apply_patch_tool() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn test_apply_patch_freeform_tool() -> anyhow::Result<()> { use crate::suite::common::run_e2e_exec_test; - use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; + use core_test_support::non_sandbox_test; - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return Ok(()); - } + non_sandbox_test!(result); let tmp_cwd = tempdir().expect("failed to create temp dir"); let freeform_add_patch = r#"*** Begin Patch diff --git a/codex-rs/login/Cargo.toml b/codex-rs/login/Cargo.toml index 1e360149..ea8095ff 100644 --- a/codex-rs/login/Cargo.toml +++ b/codex-rs/login/Cargo.toml @@ -31,3 +31,4 @@ webbrowser = "1.0" [dev-dependencies] tempfile = "3" +core_test_support = { path = "../core/tests/common" } diff --git a/codex-rs/login/tests/suite/login_server_e2e.rs b/codex-rs/login/tests/suite/login_server_e2e.rs index dd2cb904..1acba009 100644 --- a/codex-rs/login/tests/suite/login_server_e2e.rs +++ b/codex-rs/login/tests/suite/login_server_e2e.rs @@ -8,10 +8,10 @@ use std::time::Duration; use base64::Engine; use codex_login::ServerOptions; use codex_login::run_login_server; +use core_test_support::non_sandbox_test; use tempfile::tempdir; // See spawn.rs for details -pub const CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR: &str = "CODEX_SANDBOX_NETWORK_DISABLED"; fn start_mock_issuer() -> (SocketAddr, thread::JoinHandle<()>) { // Bind to a random available port @@ -77,12 +77,7 @@ fn start_mock_issuer() -> (SocketAddr, thread::JoinHandle<()>) { #[tokio::test] async fn end_to_end_login_flow_persists_auth_json() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let (issuer_addr, issuer_handle) = start_mock_issuer(); let issuer = format!("http://{}:{}", issuer_addr.ip(), issuer_addr.port()); @@ -152,12 +147,7 @@ async fn end_to_end_login_flow_persists_auth_json() { #[tokio::test] async fn creates_missing_codex_home_dir() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let (issuer_addr, _issuer_handle) = start_mock_issuer(); let issuer = format!("http://{}:{}", issuer_addr.ip(), issuer_addr.port()); @@ -196,12 +186,7 @@ async fn creates_missing_codex_home_dir() { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn cancels_previous_login_server_when_port_is_in_use() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); let (issuer_addr, _issuer_handle) = start_mock_issuer(); let issuer = format!("http://{}:{}", issuer_addr.ip(), issuer_addr.port()); diff --git a/codex-rs/mcp-server/Cargo.toml b/codex-rs/mcp-server/Cargo.toml index 9eb5f6eb..0a0e938b 100644 --- a/codex-rs/mcp-server/Cargo.toml +++ b/codex-rs/mcp-server/Cargo.toml @@ -46,3 +46,4 @@ os_info = "3.12.0" pretty_assertions = "1.4.1" tempfile = "3" wiremock = "0.6" +core_test_support = { path = "../core/tests/common" } diff --git a/codex-rs/mcp-server/tests/suite/codex_tool.rs b/codex-rs/mcp-server/tests/suite/codex_tool.rs index e7097b6b..78f758d6 100644 --- a/codex-rs/mcp-server/tests/suite/codex_tool.rs +++ b/codex-rs/mcp-server/tests/suite/codex_tool.rs @@ -24,6 +24,7 @@ use tempfile::TempDir; use tokio::time::timeout; use wiremock::MockServer; +use core_test_support::non_sandbox_test; use mcp_test_support::McpProcess; use mcp_test_support::create_apply_patch_sse_response; use mcp_test_support::create_final_assistant_message_sse_response; @@ -307,12 +308,7 @@ async fn patch_approval_triggers_elicitation() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_codex_tool_passes_base_instructions() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); // Apparently `#[tokio::test]` must return `()`, so we create a helper // function that returns `Result` so we can use `?` in favor of `unwrap`. diff --git a/codex-rs/mcp-server/tests/suite/interrupt.rs b/codex-rs/mcp-server/tests/suite/interrupt.rs index 113a8dd2..e4daeae0 100644 --- a/codex-rs/mcp-server/tests/suite/interrupt.rs +++ b/codex-rs/mcp-server/tests/suite/interrupt.rs @@ -4,7 +4,6 @@ use std::path::Path; use codex_core::protocol::TurnAbortReason; -use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; use codex_protocol::mcp_protocol::AddConversationListenerParams; use codex_protocol::mcp_protocol::InterruptConversationParams; use codex_protocol::mcp_protocol::InterruptConversationResponse; @@ -12,6 +11,7 @@ use codex_protocol::mcp_protocol::NewConversationParams; use codex_protocol::mcp_protocol::NewConversationResponse; use codex_protocol::mcp_protocol::SendUserMessageParams; use codex_protocol::mcp_protocol::SendUserMessageResponse; +use core_test_support::non_sandbox_test; use mcp_types::JSONRPCResponse; use mcp_types::RequestId; use tempfile::TempDir; @@ -26,12 +26,7 @@ const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_shell_command_interruption() { - if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { - println!( - "Skipping test because it cannot execute when network is disabled in a Codex sandbox." - ); - return; - } + non_sandbox_test!(); if let Err(err) = shell_command_interruption().await { panic!("failure: {err}");