diff --git a/codex-rs/core/tests/common/responses.rs b/codex-rs/core/tests/common/responses.rs index 3934771409..bd4d36f54b 100644 --- a/codex-rs/core/tests/common/responses.rs +++ b/codex-rs/core/tests/common/responses.rs @@ -701,33 +701,6 @@ pub async fn start_mock_server() -> MockServer { server } -// todo(aibrahim): remove this and use our search matching patterns directly -/// Get all POST requests to `/responses` endpoints from the mock server. -/// Filters out GET requests (e.g., `/models`) . -pub async fn get_responses_requests(server: &MockServer) -> Vec { - server - .received_requests() - .await - .expect("mock server should not fail") - .into_iter() - .filter(|req| req.method == "POST" && req.url.path().ends_with("/responses")) - .collect() -} - -// todo(aibrahim): remove this and use our search matching patterns directly -/// Get request bodies as JSON values from POST requests to `/responses` endpoints. -/// Filters out GET requests (e.g., `/models`) . -pub async fn get_responses_request_bodies(server: &MockServer) -> Vec { - get_responses_requests(server) - .await - .into_iter() - .map(|req| { - req.body_json::() - .expect("request body to be valid JSON") - }) - .collect() -} - #[derive(Clone)] pub struct FunctionCallResponseMocks { pub function_call: ResponseMock, diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 24d5dd8bc7..30fb0b4598 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -23,10 +23,12 @@ use tempfile::TempDir; use wiremock::MockServer; use crate::load_default_config_for_test; -use crate::responses::get_responses_request_bodies; use crate::responses::start_mock_server; use crate::streaming_sse::StreamingSseServer; use crate::wait_for_event; +use wiremock::Match; +use wiremock::matchers::method; +use wiremock::matchers::path_regex; type ConfigMutator = dyn FnOnce(&mut Config) + Send; type PreBuildHook = dyn FnOnce(&Path) + Send + 'static; @@ -322,7 +324,19 @@ impl TestCodexHarness { } pub async fn request_bodies(&self) -> Vec { - get_responses_request_bodies(&self.server).await + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + self.server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect() } pub async fn function_call_output_value(&self, call_id: &str) -> Value { diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index 2c08083ba5..4069d1a73d 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -31,7 +31,6 @@ use codex_protocol::user_input::UserInput; use core_test_support::load_default_config_for_test; use core_test_support::load_sse_fixture_with_id; use core_test_support::responses::ev_completed_with_tokens; -use core_test_support::responses::get_responses_requests; use core_test_support::responses::mount_sse_once; use core_test_support::responses::mount_sse_once_match; use core_test_support::responses::sse; @@ -47,6 +46,7 @@ use std::io::Write; use std::sync::Arc; use tempfile::TempDir; use uuid::Uuid; +use wiremock::Match; use wiremock::Mock; use wiremock::MockServer; use wiremock::ResponseTemplate; @@ -54,6 +54,7 @@ use wiremock::matchers::body_string_contains; use wiremock::matchers::header_regex; use wiremock::matchers::method; use wiremock::matchers::path; +use wiremock::matchers::path_regex; use wiremock::matchers::query_param; /// Build minimal SSE stream with completed marker using the JSON fixture. @@ -374,7 +375,15 @@ async fn includes_conversation_id_and_model_headers_in_request() { wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; // get request from the server - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); let request = requests .first() .expect("expected POST request to /responses"); @@ -500,7 +509,15 @@ async fn chatgpt_auth_sends_correct_request() { wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; // get request from the server - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); let request = requests .first() .expect("expected POST request to /responses"); @@ -1233,7 +1250,15 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() { } } - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); assert_eq!(requests.len(), 1, "expected a single POST request"); let body: serde_json::Value = requests[0] .body_json() @@ -1847,7 +1872,15 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() { wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; // Inspect the three captured requests. - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); assert_eq!(requests.len(), 3, "expected 3 requests (one per turn)"); // Replace full-array compare with tail-only raw JSON compare using a single hard-coded value. diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index e0845ef72b..426f53025b 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -31,7 +31,6 @@ 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::get_responses_requests; use core_test_support::responses::mount_compact_json_once; use core_test_support::responses::mount_sse_once; use core_test_support::responses::mount_sse_once_match; @@ -41,7 +40,10 @@ use core_test_support::responses::sse_failed; use core_test_support::responses::start_mock_server; use pretty_assertions::assert_eq; use serde_json::json; +use wiremock::Match; use wiremock::MockServer; +use wiremock::matchers::method; +use wiremock::matchers::path_regex; // --- Test helpers ----------------------------------------------------------- pub(super) const FIRST_REPLY: &str = "FIRST_REPLY"; @@ -358,7 +360,15 @@ async fn manual_compact_uses_custom_prompt() { assert_eq!(message, COMPACT_WARNING_MESSAGE); wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); let body = requests .iter() .find_map(|req| req.body_json::().ok()) @@ -586,7 +596,15 @@ async fn multiple_auto_compact_per_task_runs_after_token_limit_hit() { wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; // collect the requests payloads from the model - let requests_payloads = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests_payloads = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); let body = requests_payloads[0] .body_json::() @@ -1111,7 +1129,15 @@ async fn auto_compact_runs_after_token_limit_hit() { wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); assert_eq!( requests.len(), 4, @@ -1897,7 +1923,15 @@ async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_ "auto compact should not emit task lifecycle events" ); - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); let request_bodies: Vec = requests .into_iter() .map(|request| String::from_utf8(request.body).unwrap_or_default()) diff --git a/codex-rs/core/tests/suite/compact_resume_fork.rs b/codex-rs/core/tests/suite/compact_resume_fork.rs index 4ad829f07a..a651d139ba 100644 --- a/codex-rs/core/tests/suite/compact_resume_fork.rs +++ b/codex-rs/core/tests/suite/compact_resume_fork.rs @@ -26,7 +26,6 @@ use codex_protocol::user_input::UserInput; use core_test_support::load_default_config_for_test; use core_test_support::responses::ev_assistant_message; use core_test_support::responses::ev_completed; -use core_test_support::responses::get_responses_request_bodies; use core_test_support::responses::mount_sse_once_match; use core_test_support::responses::sse; use core_test_support::wait_for_event; @@ -35,7 +34,10 @@ use serde_json::Value; use serde_json::json; use std::sync::Arc; use tempfile::TempDir; +use wiremock::Match; use wiremock::MockServer; +use wiremock::matchers::method; +use wiremock::matchers::path_regex; const AFTER_SECOND_RESUME: &str = "AFTER_SECOND_RESUME"; @@ -772,7 +774,19 @@ fn normalize_line_endings(value: &mut Value) { } async fn gather_request_bodies(server: &MockServer) -> Vec { - let mut bodies = get_responses_request_bodies(server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let mut bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); for body in &mut bodies { normalize_line_endings(body); } diff --git a/codex-rs/core/tests/suite/review.rs b/codex-rs/core/tests/suite/review.rs index 763b6109df..f28bcfb334 100644 --- a/codex-rs/core/tests/suite/review.rs +++ b/codex-rs/core/tests/suite/review.rs @@ -23,7 +23,6 @@ use codex_core::review_format::render_review_output_text; use codex_protocol::user_input::UserInput; use core_test_support::load_default_config_for_test; use core_test_support::load_sse_fixture_with_id_from_str; -use core_test_support::responses::get_responses_requests; use core_test_support::skip_if_no_network; use core_test_support::wait_for_event; use pretty_assertions::assert_eq; @@ -32,11 +31,13 @@ use std::sync::Arc; use tempfile::TempDir; use tokio::io::AsyncWriteExt as _; use uuid::Uuid; +use wiremock::Match; use wiremock::Mock; use wiremock::MockServer; use wiremock::ResponseTemplate; use wiremock::matchers::method; use wiremock::matchers::path; +use wiremock::matchers::path_regex; /// Verify that submitting `Op::Review` spawns a child task and emits /// EnteredReviewMode -> ExitedReviewMode(None) -> TaskComplete @@ -426,7 +427,15 @@ async fn review_uses_custom_review_model_from_config() { let _complete = wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; // Assert the request body model equals the configured review model - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); let request = requests .first() .expect("expected POST request to /responses"); @@ -547,7 +556,15 @@ async fn review_input_isolated_from_parent_history() { let _complete = wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; // Assert the request `input` contains the environment context followed by the user review prompt. - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); let request = requests .first() .expect("expected POST request to /responses"); @@ -674,7 +691,15 @@ async fn review_history_surfaces_in_parent_session() { // Inspect the second request (parent turn) input contents. // Parent turns include session initial messages (user_instructions, environment_context). // Critically, no messages from the review thread should appear. - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); assert_eq!(requests.len(), 2); let body = requests[1].body_json::().unwrap(); let input = body["input"].as_array().expect("input array"); @@ -792,7 +817,15 @@ async fn review_uses_overridden_cwd_for_base_branch_merge_base() { let _entered = wait_for_event(&codex, |ev| matches!(ev, EventMsg::EnteredReviewMode(_))).await; let _complete = wait_for_event(&codex, |ev| matches!(ev, EventMsg::TaskComplete(_))).await; - let requests = get_responses_requests(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let requests = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .collect::>(); assert_eq!(requests.len(), 1); let body = requests[0].body_json::().unwrap(); let input = body["input"].as_array().expect("input array"); diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index 66319e21c2..2096d121a9 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -20,7 +20,6 @@ use core_test_support::responses::ev_assistant_message; use core_test_support::responses::ev_completed; use core_test_support::responses::ev_function_call; use core_test_support::responses::ev_response_created; -use core_test_support::responses::get_responses_request_bodies; use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; @@ -38,6 +37,9 @@ use regex_lite::Regex; use serde_json::Value; use serde_json::json; use tokio::time::Duration; +use wiremock::Match; +use wiremock::matchers::method; +use wiremock::matchers::path_regex; fn extract_output_text(item: &Value) -> Option<&str> { item.get("output").and_then(|value| match value { @@ -1241,7 +1243,19 @@ async fn exec_command_reports_chunk_and_exit_metadata() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; let metadata = outputs @@ -1345,7 +1359,19 @@ async fn unified_exec_respects_early_exit_notifications() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; let output = outputs @@ -1470,7 +1496,19 @@ async fn write_stdin_returns_exit_metadata_and_clears_session() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; @@ -1824,7 +1862,19 @@ async fn unified_exec_reuses_session_via_stdin() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; @@ -1958,7 +2008,19 @@ PY let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; @@ -2067,7 +2129,19 @@ async fn unified_exec_timeout_and_followup_poll() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; @@ -2153,7 +2227,19 @@ PY let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; let large_output = outputs.get(call_id).expect("missing large output summary"); @@ -2230,7 +2316,19 @@ async fn unified_exec_runs_under_sandbox() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; let output = outputs.get(call_id).expect("missing output"); @@ -2328,7 +2426,19 @@ async fn unified_exec_python_prompt_under_seatbelt() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; let startup_output = outputs @@ -2418,7 +2528,19 @@ async fn unified_exec_runs_on_all_platforms() -> Result<()> { let requests = server.received_requests().await.expect("recorded requests"); assert!(!requests.is_empty(), "expected at least one POST request"); - let bodies = get_responses_request_bodies(&server).await; + let method_matcher = method("POST"); + let path_matcher = path_regex(".*/responses$"); + let bodies = server + .received_requests() + .await + .expect("mock server should not fail") + .into_iter() + .filter(|req| method_matcher.matches(req) && path_matcher.matches(req)) + .map(|req| { + req.body_json::() + .expect("request body to be valid JSON") + }) + .collect::>(); let outputs = collect_tool_outputs(&bodies)?; let output = outputs.get(call_id).expect("missing output");