Add non_sandbox_test helper (#3880)

Makes tests shorter
This commit is contained in:
pakrym-oai
2025-09-22 07:50:41 -07:00
committed by GitHub
parent 5996ee0e5f
commit 14a115d488
14 changed files with 99 additions and 223 deletions

2
codex-rs/Cargo.lock generated
View File

@@ -797,6 +797,7 @@ dependencies = [
"chrono", "chrono",
"codex-core", "codex-core",
"codex-protocol", "codex-protocol",
"core_test_support",
"rand 0.8.5", "rand 0.8.5",
"reqwest", "reqwest",
"serde", "serde",
@@ -835,6 +836,7 @@ dependencies = [
"codex-core", "codex-core",
"codex-login", "codex-login",
"codex-protocol", "codex-protocol",
"core_test_support",
"mcp-types", "mcp-types",
"mcp_test_support", "mcp_test_support",
"os_info", "os_info",

View File

@@ -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(());
}
}};
}

View File

@@ -1,7 +1,7 @@
use assert_cmd::Command as AssertCommand; use assert_cmd::Command as AssertCommand;
use codex_core::RolloutRecorder; use codex_core::RolloutRecorder;
use codex_core::protocol::GitInfo; 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::Duration;
use std::time::Instant; use std::time::Instant;
use tempfile::TempDir; use tempfile::TempDir;
@@ -21,12 +21,7 @@ use wiremock::matchers::path;
/// 4. Ensures the response is received exactly once and contains "hi" /// 4. Ensures the response is received exactly once and contains "hi"
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn chat_mode_stream_cli() { async fn chat_mode_stream_cli() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = MockServer::start().await; let server = MockServer::start().await;
let sse = concat!( let sse = concat!(
@@ -102,12 +97,7 @@ async fn chat_mode_stream_cli() {
/// received by a mock OpenAI Responses endpoint. /// received by a mock OpenAI Responses endpoint.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn exec_cli_applies_experimental_instructions_file() { async fn exec_cli_applies_experimental_instructions_file() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Start mock server which will capture the request and return a minimal // Start mock server which will capture the request and return a minimal
// SSE stream for a single turn. // 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 /// 4. Ensures the fixture content is correctly streamed through the CLI
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn responses_api_stream_cli() { async fn responses_api_stream_cli() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let fixture = let fixture =
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/cli_responses_fixture.sse"); 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn integration_creates_and_checks_session_file() { async fn integration_creates_and_checks_session_file() {
// Honor sandbox network restrictions for CI parity with the other tests. // Honor sandbox network restrictions for CI parity with the other tests.
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// 1. Temp home so we read/write isolated session files. // 1. Temp home so we read/write isolated session files.
let home = TempDir::new().unwrap(); let home = TempDir::new().unwrap();

View File

@@ -16,12 +16,12 @@ use codex_core::built_in_model_providers;
use codex_core::protocol::EventMsg; use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem; use codex_core::protocol::InputItem;
use codex_core::protocol::Op; use codex_core::protocol::Op;
use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use codex_protocol::mcp_protocol::ConversationId; use codex_protocol::mcp_protocol::ConversationId;
use codex_protocol::models::ReasoningItemReasoningSummary; use codex_protocol::models::ReasoningItemReasoningSummary;
use codex_protocol::models::WebSearchAction; use codex_protocol::models::WebSearchAction;
use core_test_support::load_default_config_for_test; use core_test_support::load_default_config_for_test;
use core_test_support::load_sse_fixture_with_id; 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::responses;
use core_test_support::wait_for_event; use core_test_support::wait_for_event;
use futures::StreamExt; use futures::StreamExt;
@@ -126,12 +126,7 @@ fn write_auth_json(
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn resume_includes_initial_messages_and_sends_prior_items() { async fn resume_includes_initial_messages_and_sends_prior_items() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Create a fake rollout session file with prior user + system + assistant messages. // Create a fake rollout session file with prior user + system + assistant messages.
let tmpdir = TempDir::new().unwrap(); 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn includes_conversation_id_and_model_headers_in_request() { async fn includes_conversation_id_and_model_headers_in_request() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Mock server // Mock server
let server = MockServer::start().await; 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn chatgpt_auth_sends_correct_request() { async fn chatgpt_auth_sends_correct_request() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Mock server // Mock server
let server = MockServer::start().await; let server = MockServer::start().await;
@@ -506,12 +491,7 @@ async fn chatgpt_auth_sends_correct_request() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() { async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Mock server // Mock server
let server = MockServer::start().await; 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn azure_responses_request_includes_store_and_reasoning_ids() { async fn azure_responses_request_includes_store_and_reasoning_ids() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = MockServer::start().await; let server = MockServer::start().await;
@@ -1036,12 +1011,7 @@ fn create_dummy_codex_auth() -> CodexAuth {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn history_dedupes_streamed_and_final_messages_across_turns() { async fn history_dedupes_streamed_and_final_messages_across_turns() {
// Skip under Codex sandbox network restrictions (mirrors other tests). // Skip under Codex sandbox network restrictions (mirrors other tests).
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Mock server that will receive three sequential requests and return the same SSE stream // 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. // each time: a few deltas, then a final assistant message, then completed.

View File

@@ -9,9 +9,7 @@ use codex_core::protocol::InputItem;
use codex_core::protocol::Op; use codex_core::protocol::Op;
use codex_core::protocol::RolloutItem; use codex_core::protocol::RolloutItem;
use codex_core::protocol::RolloutLine; 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_default_config_for_test;
use core_test_support::responses;
use core_test_support::wait_for_event; use core_test_support::wait_for_event;
use tempfile::TempDir; use tempfile::TempDir;
use wiremock::Mock; use wiremock::Mock;
@@ -21,11 +19,16 @@ use wiremock::ResponseTemplate;
use wiremock::matchers::method; use wiremock::matchers::method;
use wiremock::matchers::path; 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 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::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::atomic::AtomicUsize; 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn summarize_context_three_requests_and_instructions() { async fn summarize_context_three_requests_and_instructions() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Set up a mock server that we can inspect after the run. // Set up a mock server that we can inspect after the run.
let server = start_mock_server().await; 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("\"text\":\"hello world\"")
&& !body.contains(&format!("\"text\":\"{SUMMARIZE_TRIGGER}\"")) && !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 second_matcher = |req: &wiremock::Request| {
let body = std::str::from_utf8(&req.body).unwrap_or(""); let body = std::str::from_utf8(&req.body).unwrap_or("");
body.contains(&format!("\"text\":\"{SUMMARIZE_TRIGGER}\"")) 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 third_matcher = |req: &wiremock::Request| {
let body = std::str::from_utf8(&req.body).unwrap_or(""); let body = std::str::from_utf8(&req.body).unwrap_or("");
body.contains(&format!("\"text\":\"{THIRD_USER_MSG}\"")) 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. // Build config pointing to the mock server and spawn Codex.
let model_provider = ModelProviderInfo { 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(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))]
#[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))]
async fn auto_compact_runs_after_token_limit_hit() { async fn auto_compact_runs_after_token_limit_hit() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = start_mock_server().await; let server = start_mock_server().await;
let sse1 = sse(vec![ let sse1 = sse(vec![
ev_assistant_message("m1", FIRST_REPLY), 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![ let sse2 = sse(vec![
ev_assistant_message("m2", "SECOND_REPLY"), 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![ let sse3 = sse(vec![
ev_assistant_message("m3", AUTO_SUMMARY_TEXT), 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| { let first_matcher = |req: &wiremock::Request| {
@@ -309,7 +302,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(first_matcher) .and(first_matcher)
.respond_with(responses::sse_response(sse1)) .respond_with(sse_response(sse1))
.mount(&server) .mount(&server)
.await; .await;
@@ -322,7 +315,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(second_matcher) .and(second_matcher)
.respond_with(responses::sse_response(sse2)) .respond_with(sse_response(sse2))
.mount(&server) .mount(&server)
.await; .await;
@@ -333,7 +326,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(third_matcher) .and(third_matcher)
.respond_with(responses::sse_response(sse3)) .respond_with(sse_response(sse3))
.mount(&server) .mount(&server)
.await; .await;
@@ -417,28 +410,23 @@ async fn auto_compact_runs_after_token_limit_hit() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn auto_compact_persists_rollout_entries() { async fn auto_compact_persists_rollout_entries() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = start_mock_server().await; let server = start_mock_server().await;
let sse1 = sse(vec![ let sse1 = sse(vec![
ev_assistant_message("m1", FIRST_REPLY), 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![ let sse2 = sse(vec![
ev_assistant_message("m2", "SECOND_REPLY"), 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![ let sse3 = sse(vec![
ev_assistant_message("m3", AUTO_SUMMARY_TEXT), 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| { let first_matcher = |req: &wiremock::Request| {
@@ -450,7 +438,7 @@ async fn auto_compact_persists_rollout_entries() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(first_matcher) .and(first_matcher)
.respond_with(responses::sse_response(sse1)) .respond_with(sse_response(sse1))
.mount(&server) .mount(&server)
.await; .await;
@@ -463,7 +451,7 @@ async fn auto_compact_persists_rollout_entries() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(second_matcher) .and(second_matcher)
.respond_with(responses::sse_response(sse2)) .respond_with(sse_response(sse2))
.mount(&server) .mount(&server)
.await; .await;
@@ -474,7 +462,7 @@ async fn auto_compact_persists_rollout_entries() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(third_matcher) .and(third_matcher)
.respond_with(responses::sse_response(sse3)) .respond_with(sse_response(sse3))
.mount(&server) .mount(&server)
.await; .await;
@@ -550,28 +538,23 @@ async fn auto_compact_persists_rollout_entries() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn auto_compact_stops_after_failed_attempt() { async fn auto_compact_stops_after_failed_attempt() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = start_mock_server().await; let server = start_mock_server().await;
let sse1 = sse(vec![ let sse1 = sse(vec![
ev_assistant_message("m1", FIRST_REPLY), ev_assistant_message("m1", FIRST_REPLY),
responses::ev_completed_with_tokens("r1", 500), ev_completed_with_tokens("r1", 500),
]); ]);
let sse2 = sse(vec![ let sse2 = sse(vec![
ev_assistant_message("m2", SUMMARY_TEXT), ev_assistant_message("m2", SUMMARY_TEXT),
responses::ev_completed_with_tokens("r2", 50), ev_completed_with_tokens("r2", 50),
]); ]);
let sse3 = sse(vec![ let sse3 = sse(vec![
ev_assistant_message("m3", STILL_TOO_BIG_REPLY), 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| { let first_matcher = |req: &wiremock::Request| {
@@ -582,7 +565,7 @@ async fn auto_compact_stops_after_failed_attempt() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(first_matcher) .and(first_matcher)
.respond_with(responses::sse_response(sse1.clone())) .respond_with(sse_response(sse1.clone()))
.mount(&server) .mount(&server)
.await; .await;
@@ -593,7 +576,7 @@ async fn auto_compact_stops_after_failed_attempt() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(second_matcher) .and(second_matcher)
.respond_with(responses::sse_response(sse2.clone())) .respond_with(sse_response(sse2.clone()))
.mount(&server) .mount(&server)
.await; .await;
@@ -605,7 +588,7 @@ async fn auto_compact_stops_after_failed_attempt() {
Mock::given(method("POST")) Mock::given(method("POST"))
.and(path("/v1/responses")) .and(path("/v1/responses"))
.and(third_matcher) .and(third_matcher)
.respond_with(responses::sse_response(sse3.clone())) .respond_with(sse_response(sse3.clone()))
.mount(&server) .mount(&server)
.await; .await;
@@ -664,38 +647,33 @@ async fn auto_compact_stops_after_failed_attempt() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_events() { 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() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = start_mock_server().await; let server = start_mock_server().await;
let sse1 = sse(vec![ let sse1 = sse(vec![
ev_assistant_message("m1", FIRST_REPLY), ev_assistant_message("m1", FIRST_REPLY),
responses::ev_completed_with_tokens("r1", 500), ev_completed_with_tokens("r1", 500),
]); ]);
let sse2 = sse(vec![ let sse2 = sse(vec![
ev_assistant_message("m2", FIRST_AUTO_SUMMARY), ev_assistant_message("m2", FIRST_AUTO_SUMMARY),
responses::ev_completed_with_tokens("r2", 50), ev_completed_with_tokens("r2", 50),
]); ]);
let sse3 = sse(vec![ let sse3 = sse(vec![
responses::ev_function_call(DUMMY_CALL_ID, DUMMY_FUNCTION_NAME, "{}"), ev_function_call(DUMMY_CALL_ID, DUMMY_FUNCTION_NAME, "{}"),
responses::ev_completed_with_tokens("r3", 150), ev_completed_with_tokens("r3", 150),
]); ]);
let sse4 = sse(vec![ let sse4 = sse(vec![
ev_assistant_message("m4", SECOND_LARGE_REPLY), ev_assistant_message("m4", SECOND_LARGE_REPLY),
responses::ev_completed_with_tokens("r4", 450), ev_completed_with_tokens("r4", 450),
]); ]);
let sse5 = sse(vec![ let sse5 = sse(vec![
ev_assistant_message("m5", SECOND_AUTO_SUMMARY), ev_assistant_message("m5", SECOND_AUTO_SUMMARY),
responses::ev_completed_with_tokens("r5", 60), ev_completed_with_tokens("r5", 60),
]); ]);
let sse6 = sse(vec![ let sse6 = sse(vec![
ev_assistant_message("m6", FINAL_REPLY), ev_assistant_message("m6", FINAL_REPLY),
responses::ev_completed_with_tokens("r6", 120), ev_completed_with_tokens("r6", 120),
]); ]);
#[derive(Clone)] #[derive(Clone)]

View File

@@ -20,9 +20,9 @@ use codex_core::protocol::ReviewOutputEvent;
use codex_core::protocol::ReviewRequest; use codex_core::protocol::ReviewRequest;
use codex_core::protocol::RolloutItem; use codex_core::protocol::RolloutItem;
use codex_core::protocol::RolloutLine; 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_default_config_for_test;
use core_test_support::load_sse_fixture_with_id_from_str; 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 core_test_support::wait_for_event;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::path::PathBuf; use std::path::PathBuf;
@@ -42,12 +42,7 @@ use wiremock::matchers::path;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn review_op_emits_lifecycle_and_review_output() { async fn review_op_emits_lifecycle_and_review_output() {
// Skip under Codex sandbox network restrictions. // Skip under Codex sandbox network restrictions.
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Start mock Responses API server. Return a single assistant message whose // Start mock Responses API server. Return a single assistant message whose
// text is a JSON-encoded ReviewOutputEvent. // 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(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))]
#[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))]
async fn review_op_with_plain_text_emits_review_fallback() { async fn review_op_with_plain_text_emits_review_fallback() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let sse_raw = r#"[ let sse_raw = r#"[
{"type":"response.output_item.done", "item":{ {"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(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))]
#[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))]
async fn review_does_not_emit_agent_message_on_structured_output() { async fn review_does_not_emit_agent_message_on_structured_output() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let review_json = serde_json::json!({ let review_json = serde_json::json!({
"findings": [ "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). /// request uses that model (and not the main chat model).
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn review_uses_custom_review_model_from_config() { async fn review_uses_custom_review_model_from_config() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Minimal stream: just a completed event // Minimal stream: just a completed event
let sse_raw = r#"[ 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(windows, tokio::test(flavor = "multi_thread", worker_threads = 4))]
#[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))] #[cfg_attr(not(windows), tokio::test(flavor = "multi_thread", worker_threads = 2))]
async fn review_input_isolated_from_parent_history() { async fn review_input_isolated_from_parent_history() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Mock server for the single review request // Mock server for the single review request
let sse_raw = r#"[ let sse_raw = r#"[
@@ -542,12 +517,7 @@ async fn review_input_isolated_from_parent_history() {
/// messages in its request `input`. /// messages in its request `input`.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn review_history_does_not_leak_into_parent_session() { async fn review_history_does_not_leak_into_parent_session() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Respond to both the review request and the subsequent parent request. // Respond to both the review request and the subsequent parent request.
let sse_raw = r#"[ let sse_raw = r#"[

View File

@@ -7,9 +7,9 @@ use codex_core::WireApi;
use codex_core::protocol::EventMsg; use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem; use codex_core::protocol::InputItem;
use codex_core::protocol::Op; 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_default_config_for_test;
use core_test_support::load_sse_fixture_with_id; 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 core_test_support::wait_for_event_with_timeout;
use tempfile::TempDir; use tempfile::TempDir;
use wiremock::Mock; use wiremock::Mock;
@@ -25,12 +25,7 @@ fn sse_completed(id: &str) -> String {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn continue_after_stream_error() { async fn continue_after_stream_error() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = MockServer::start().await; let server = MockServer::start().await;

View File

@@ -9,10 +9,10 @@ use codex_core::ModelProviderInfo;
use codex_core::protocol::EventMsg; use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem; use codex_core::protocol::InputItem;
use codex_core::protocol::Op; 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_default_config_for_test;
use core_test_support::load_sse_fixture; use core_test_support::load_sse_fixture;
use core_test_support::load_sse_fixture_with_id; use core_test_support::load_sse_fixture_with_id;
use core_test_support::non_sandbox_test;
use tempfile::TempDir; use tempfile::TempDir;
use tokio::time::timeout; use tokio::time::timeout;
use wiremock::Mock; use wiremock::Mock;
@@ -33,12 +33,7 @@ fn sse_completed(id: &str) -> String {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn retries_on_early_close() { async fn retries_on_early_close() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let server = MockServer::start().await; let server = MockServer::start().await;

View File

@@ -48,14 +48,9 @@ fn test_standalone_exec_cli_can_use_apply_patch() -> anyhow::Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_apply_patch_tool() -> anyhow::Result<()> { async fn test_apply_patch_tool() -> anyhow::Result<()> {
use crate::suite::common::run_e2e_exec_test; 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() { non_sandbox_test!(result);
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return Ok(());
}
let tmp_cwd = tempdir().expect("failed to create temp dir"); let tmp_cwd = tempdir().expect("failed to create temp dir");
let tmp_path = tmp_cwd.path().to_path_buf(); 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_apply_patch_freeform_tool() -> anyhow::Result<()> { async fn test_apply_patch_freeform_tool() -> anyhow::Result<()> {
use crate::suite::common::run_e2e_exec_test; 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() { non_sandbox_test!(result);
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return Ok(());
}
let tmp_cwd = tempdir().expect("failed to create temp dir"); let tmp_cwd = tempdir().expect("failed to create temp dir");
let freeform_add_patch = r#"*** Begin Patch let freeform_add_patch = r#"*** Begin Patch

View File

@@ -31,3 +31,4 @@ webbrowser = "1.0"
[dev-dependencies] [dev-dependencies]
tempfile = "3" tempfile = "3"
core_test_support = { path = "../core/tests/common" }

View File

@@ -8,10 +8,10 @@ use std::time::Duration;
use base64::Engine; use base64::Engine;
use codex_login::ServerOptions; use codex_login::ServerOptions;
use codex_login::run_login_server; use codex_login::run_login_server;
use core_test_support::non_sandbox_test;
use tempfile::tempdir; use tempfile::tempdir;
// See spawn.rs for details // 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<()>) { fn start_mock_issuer() -> (SocketAddr, thread::JoinHandle<()>) {
// Bind to a random available port // Bind to a random available port
@@ -77,12 +77,7 @@ fn start_mock_issuer() -> (SocketAddr, thread::JoinHandle<()>) {
#[tokio::test] #[tokio::test]
async fn end_to_end_login_flow_persists_auth_json() { async fn end_to_end_login_flow_persists_auth_json() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let (issuer_addr, issuer_handle) = start_mock_issuer(); let (issuer_addr, issuer_handle) = start_mock_issuer();
let issuer = format!("http://{}:{}", issuer_addr.ip(), issuer_addr.port()); 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] #[tokio::test]
async fn creates_missing_codex_home_dir() { async fn creates_missing_codex_home_dir() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let (issuer_addr, _issuer_handle) = start_mock_issuer(); let (issuer_addr, _issuer_handle) = start_mock_issuer();
let issuer = format!("http://{}:{}", issuer_addr.ip(), issuer_addr.port()); 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn cancels_previous_login_server_when_port_is_in_use() { async fn cancels_previous_login_server_when_port_is_in_use() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
let (issuer_addr, _issuer_handle) = start_mock_issuer(); let (issuer_addr, _issuer_handle) = start_mock_issuer();
let issuer = format!("http://{}:{}", issuer_addr.ip(), issuer_addr.port()); let issuer = format!("http://{}:{}", issuer_addr.ip(), issuer_addr.port());

View File

@@ -46,3 +46,4 @@ os_info = "3.12.0"
pretty_assertions = "1.4.1" pretty_assertions = "1.4.1"
tempfile = "3" tempfile = "3"
wiremock = "0.6" wiremock = "0.6"
core_test_support = { path = "../core/tests/common" }

View File

@@ -24,6 +24,7 @@ use tempfile::TempDir;
use tokio::time::timeout; use tokio::time::timeout;
use wiremock::MockServer; use wiremock::MockServer;
use core_test_support::non_sandbox_test;
use mcp_test_support::McpProcess; use mcp_test_support::McpProcess;
use mcp_test_support::create_apply_patch_sse_response; use mcp_test_support::create_apply_patch_sse_response;
use mcp_test_support::create_final_assistant_message_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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_codex_tool_passes_base_instructions() { async fn test_codex_tool_passes_base_instructions() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
// Apparently `#[tokio::test]` must return `()`, so we create a helper // Apparently `#[tokio::test]` must return `()`, so we create a helper
// function that returns `Result` so we can use `?` in favor of `unwrap`. // function that returns `Result` so we can use `?` in favor of `unwrap`.

View File

@@ -4,7 +4,6 @@
use std::path::Path; use std::path::Path;
use codex_core::protocol::TurnAbortReason; 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::AddConversationListenerParams;
use codex_protocol::mcp_protocol::InterruptConversationParams; use codex_protocol::mcp_protocol::InterruptConversationParams;
use codex_protocol::mcp_protocol::InterruptConversationResponse; 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::NewConversationResponse;
use codex_protocol::mcp_protocol::SendUserMessageParams; use codex_protocol::mcp_protocol::SendUserMessageParams;
use codex_protocol::mcp_protocol::SendUserMessageResponse; use codex_protocol::mcp_protocol::SendUserMessageResponse;
use core_test_support::non_sandbox_test;
use mcp_types::JSONRPCResponse; use mcp_types::JSONRPCResponse;
use mcp_types::RequestId; use mcp_types::RequestId;
use tempfile::TempDir; 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_shell_command_interruption() { async fn test_shell_command_interruption() {
if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() { non_sandbox_test!();
println!(
"Skipping test because it cannot execute when network is disabled in a Codex sandbox."
);
return;
}
if let Err(err) = shell_command_interruption().await { if let Err(err) = shell_command_interruption().await {
panic!("failure: {err}"); panic!("failure: {err}");