Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ If you don’t have the tool:
- `ResponsesRequest` exposes helpers (`body_json`, `input`, `function_call_output`, `custom_tool_call_output`, `call_output`, `header`, `path`, `query_param`) so assertions can target structured payloads instead of manual JSON digging.
- Build SSE payloads with the provided `ev_*` constructors and the `sse(...)`.
- Prefer `wait_for_event` over `wait_for_event_with_timeout`.
- Prefer `mount_sse_once` over `mount_sse_once_match` or `mount_sse_sequence`

- Typical pattern:

Expand Down
5 changes: 2 additions & 3 deletions codex-rs/core/tests/common/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use wiremock::MockBuilder;
use wiremock::MockServer;
use wiremock::Respond;
use wiremock::ResponseTemplate;
use wiremock::matchers::any;
use wiremock::matchers::method;
use wiremock::matchers::path_regex;

Expand Down Expand Up @@ -546,13 +545,13 @@ pub async fn mount_function_call_agent_response(
ev_function_call(call_id, tool_name, arguments),
ev_completed("resp-1"),
]);
let function_call = mount_sse_once_match(server, any(), first_response).await;
let function_call = mount_sse_once(server, first_response).await;

let second_response = sse(vec![
ev_assistant_message("msg-1", "done"),
ev_completed("resp-2"),
]);
let completion = mount_sse_once_match(server, any(), second_response).await;
let completion = mount_sse_once(server, second_response).await;

FunctionCallResponseMocks {
function_call,
Expand Down
7 changes: 1 addition & 6 deletions codex-rs/core/tests/suite/cli_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,7 @@ async fn exec_cli_applies_experimental_instructions_file() {
"data: {\"type\":\"response.created\",\"response\":{}}\n\n",
"data: {\"type\":\"response.completed\",\"response\":{\"id\":\"r1\"}}\n\n"
);
let resp_mock = core_test_support::responses::mount_sse_once_match(
&server,
path("/v1/responses"),
sse.to_string(),
)
.await;
let resp_mock = core_test_support::responses::mount_sse_once(&server, sse.to_string()).await;

// Create a temporary instructions file with a unique marker we can assert
// appears in the outbound request payload.
Expand Down
40 changes: 10 additions & 30 deletions codex-rs/core/tests/suite/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {

// Mock server that will receive the resumed request
let server = MockServer::start().await;
let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;

// Configure Codex to resume from our file
let model_provider = ModelProviderInfo {
Expand Down Expand Up @@ -381,9 +379,7 @@ async fn includes_base_instructions_override_in_request() {
skip_if_no_network!();
// Mock server
let server = MockServer::start().await;
let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;

let model_provider = ModelProviderInfo {
base_url: Some(format!("{}/v1", server.uri())),
Expand Down Expand Up @@ -571,9 +567,7 @@ async fn includes_user_instructions_message_in_request() {
skip_if_no_network!();
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;

let model_provider = ModelProviderInfo {
base_url: Some(format!("{}/v1", server.uri())),
Expand Down Expand Up @@ -631,9 +625,7 @@ async fn includes_configured_effort_in_request() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.1-codex")
.with_config(|config| {
Expand Down Expand Up @@ -672,9 +664,7 @@ async fn includes_no_effort_in_request() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.1-codex")
.build(&server)
Expand Down Expand Up @@ -711,9 +701,7 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_fami
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?;

codex
Expand Down Expand Up @@ -746,9 +734,7 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?;

codex
Expand Down Expand Up @@ -781,9 +767,7 @@ async fn configured_verbosity_not_sent_for_models_without_support() -> anyhow::R
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5-codex")
.with_config(|config| {
Expand Down Expand Up @@ -821,9 +805,7 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5")
.with_config(|config| {
Expand Down Expand Up @@ -862,9 +844,7 @@ async fn includes_developer_instructions_message_in_request() {
skip_if_no_network!();
let server = MockServer::start().await;

let resp_mock =
responses::mount_sse_once_match(&server, path("/v1/responses"), sse_completed("resp1"))
.await;
let resp_mock = responses::mount_sse_once(&server, sse_completed("resp1")).await;

let model_provider = ModelProviderInfo {
base_url: Some(format!("{}/v1", server.uri())),
Expand Down
17 changes: 8 additions & 9 deletions codex-rs/core/tests/suite/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@ use core_test_support::responses::ev_reasoning_text_delta;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::ev_web_search_call_added;
use core_test_support::responses::ev_web_search_call_done;
use core_test_support::responses::mount_sse_once_match;
use core_test_support::responses::mount_sse_once;
use core_test_support::responses::sse;
use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::TestCodex;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event_match;
use pretty_assertions::assert_eq;
use wiremock::matchers::any;

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn user_message_item_is_emitted() -> anyhow::Result<()> {
Expand All @@ -37,7 +36,7 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> {
let TestCodex { codex, .. } = test_codex().build(&server).await?;

let first_response = sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]);
mount_sse_once_match(&server, any(), first_response).await;
mount_sse_once(&server, first_response).await;

codex
.submit(Op::UserInput {
Expand Down Expand Up @@ -93,7 +92,7 @@ async fn assistant_message_item_is_emitted() -> anyhow::Result<()> {
ev_assistant_message("msg-1", "all done"),
ev_completed("resp-1"),
]);
mount_sse_once_match(&server, any(), first_response).await;
mount_sse_once(&server, first_response).await;

codex
.submit(Op::UserInput {
Expand Down Expand Up @@ -149,7 +148,7 @@ async fn reasoning_item_is_emitted() -> anyhow::Result<()> {
reasoning_item,
ev_completed("resp-1"),
]);
mount_sse_once_match(&server, any(), first_response).await;
mount_sse_once(&server, first_response).await;

codex
.submit(Op::UserInput {
Expand Down Expand Up @@ -207,7 +206,7 @@ async fn web_search_item_is_emitted() -> anyhow::Result<()> {
web_search_done,
ev_completed("resp-1"),
]);
mount_sse_once_match(&server, any(), first_response).await;
mount_sse_once(&server, first_response).await;

codex
.submit(Op::UserInput {
Expand Down Expand Up @@ -259,7 +258,7 @@ async fn agent_message_content_delta_has_item_metadata() -> anyhow::Result<()> {
ev_assistant_message("msg-1", "streamed response"),
ev_completed("resp-1"),
]);
mount_sse_once_match(&server, any(), stream).await;
mount_sse_once(&server, stream).await;

codex
.submit(Op::UserInput {
Expand Down Expand Up @@ -324,7 +323,7 @@ async fn reasoning_content_delta_has_item_metadata() -> anyhow::Result<()> {
ev_reasoning_item("reasoning-1", &["step one"], &[]),
ev_completed("resp-1"),
]);
mount_sse_once_match(&server, any(), stream).await;
mount_sse_once(&server, stream).await;

codex
.submit(Op::UserInput {
Expand Down Expand Up @@ -381,7 +380,7 @@ async fn reasoning_raw_content_delta_respects_flag() -> anyhow::Result<()> {
ev_reasoning_item("reasoning-raw", &["complete"], &["raw detail"]),
ev_completed("resp-1"),
]);
mount_sse_once_match(&server, any(), stream).await;
mount_sse_once(&server, stream).await;

codex
.submit(Op::UserInput {
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/tests/suite/model_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
let server = MockServer::start().await;

let sse = sse_completed(model);
let resp_mock = responses::mount_sse_once_match(&server, wiremock::matchers::any(), sse).await;
let resp_mock = responses::mount_sse_once(&server, sse).await;

let model_provider = ModelProviderInfo {
base_url: Some(format!("{}/v1", server.uri())),
Expand Down
31 changes: 15 additions & 16 deletions codex-rs/core/tests/suite/prompt_caching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ use codex_core::shell::Shell;
use codex_core::shell::default_user_shell;
use codex_protocol::user_input::UserInput;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses::mount_sse_once_match;
use core_test_support::responses::mount_sse_once;
use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::TestCodex;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
use std::collections::HashMap;
use tempfile::TempDir;
use wiremock::matchers::any;

fn text_user_input(text: String) -> serde_json::Value {
serde_json::json!({
Expand Down Expand Up @@ -70,8 +69,8 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> {
use pretty_assertions::assert_eq;

let server = start_mock_server().await;
let req1 = mount_sse_once_match(&server, any(), sse_completed("resp-1")).await;
let req2 = mount_sse_once_match(&server, any(), sse_completed("resp-2")).await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;

let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
Expand Down Expand Up @@ -128,8 +127,8 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
use pretty_assertions::assert_eq;

let server = start_mock_server().await;
let req1 = mount_sse_once_match(&server, any(), sse_completed("resp-1")).await;
let req2 = mount_sse_once_match(&server, any(), sse_completed("resp-2")).await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;

let TestCodex { codex, config, .. } = test_codex()
.with_config(|config| {
Expand Down Expand Up @@ -247,8 +246,8 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
use pretty_assertions::assert_eq;

let server = start_mock_server().await;
let req1 = mount_sse_once_match(&server, any(), sse_completed("resp-1")).await;
let req2 = mount_sse_once_match(&server, any(), sse_completed("resp-2")).await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;

let TestCodex { codex, config, .. } = test_codex()
.with_config(|config| {
Expand Down Expand Up @@ -328,8 +327,8 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
use pretty_assertions::assert_eq;

let server = start_mock_server().await;
let req1 = mount_sse_once_match(&server, any(), sse_completed("resp-1")).await;
let req2 = mount_sse_once_match(&server, any(), sse_completed("resp-2")).await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;

let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
Expand Down Expand Up @@ -427,8 +426,8 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res
use pretty_assertions::assert_eq;

let server = start_mock_server().await;
let req1 = mount_sse_once_match(&server, any(), sse_completed("resp-1")).await;
let req2 = mount_sse_once_match(&server, any(), sse_completed("resp-2")).await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;

let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
Expand Down Expand Up @@ -523,8 +522,8 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
use pretty_assertions::assert_eq;

let server = start_mock_server().await;
let req1 = mount_sse_once_match(&server, any(), sse_completed("resp-1")).await;
let req2 = mount_sse_once_match(&server, any(), sse_completed("resp-2")).await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;

let TestCodex { codex, config, .. } = test_codex()
.with_config(|config| {
Expand Down Expand Up @@ -611,8 +610,8 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu

let server = start_mock_server().await;

let req1 = mount_sse_once_match(&server, any(), sse_completed("resp-1")).await;
let req2 = mount_sse_once_match(&server, any(), sse_completed("resp-2")).await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let TestCodex { codex, config, .. } = test_codex()
.with_config(|config| {
config.user_instructions = Some("be consistent and helpful".to_string());
Expand Down
7 changes: 3 additions & 4 deletions codex-rs/core/tests/suite/resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_reasoning_item;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::mount_sse_once_match;
use core_test_support::responses::mount_sse_once;
use core_test_support::responses::sse;
use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
use std::sync::Arc;
use wiremock::matchers::any;

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn resume_includes_initial_messages_from_rollout_events() -> Result<()> {
Expand All @@ -31,7 +30,7 @@ async fn resume_includes_initial_messages_from_rollout_events() -> Result<()> {
ev_assistant_message("msg-1", "Completed first turn"),
ev_completed("resp-initial"),
]);
mount_sse_once_match(&server, any(), initial_sse).await;
mount_sse_once(&server, initial_sse).await;

codex
.submit(Op::UserInput {
Expand Down Expand Up @@ -83,7 +82,7 @@ async fn resume_includes_initial_messages_from_reasoning_events() -> Result<()>
ev_assistant_message("msg-1", "Completed reasoning turn"),
ev_completed("resp-initial"),
]);
mount_sse_once_match(&server, any(), initial_sse).await;
mount_sse_once(&server, initial_sse).await;

codex
.submit(Op::UserInput {
Expand Down
Loading
Loading