Update tests to stop using sse_completed fixture (#10638)

Summary:
- replace the `sse_completed` fixture and related JSON template with
direct `responses::ev_completed` payload builders
- cascade the new SSE helpers through all affected core tests for
consistency and clarity
- remove legacy fixtures that were no longer needed once the helpers are
in place

Testing:
- Not run (not requested)
This commit is contained in:
pakrym-oai
2026-02-04 08:38:06 -08:00
committed by GitHub
parent 583e5d4f41
commit 0efd33f7f4
14 changed files with 428 additions and 194 deletions

View File

@@ -1,7 +1,6 @@
#![expect(clippy::expect_used)]
use codex_utils_cargo_bin::CargoBinError;
use codex_utils_cargo_bin::find_resource;
use tempfile::TempDir;
use codex_core::CodexThread;
@@ -147,40 +146,6 @@ pub fn load_sse_fixture_with_id_from_str(raw: &str, id: &str) -> String {
.collect()
}
/// Same as [`load_sse_fixture`], but replaces the placeholder `__ID__` in the
/// fixture template with the supplied identifier before parsing. This lets a
/// single JSON template be reused by multiple tests that each need a unique
/// `response_id`.
pub fn load_sse_fixture_with_id(path: impl AsRef<std::path::Path>, id: &str) -> String {
let p = path.as_ref();
let full_path = match find_resource!(p) {
Ok(p) => p,
Err(err) => panic!(
"failed to find fixture template at {:?}: {err}",
path.as_ref()
),
};
let raw = std::fs::read_to_string(full_path).expect("read fixture template");
let replaced = raw.replace("__ID__", id);
let events: Vec<serde_json::Value> =
serde_json::from_str(&replaced).expect("parse JSON fixture");
events
.into_iter()
.map(|e| {
let kind = e
.get("type")
.and_then(|v| v.as_str())
.expect("fixture event missing type");
if e.as_object().map(|o| o.len() == 1).unwrap_or(false) {
format!("event: {kind}\n\n")
} else {
format!("event: {kind}\ndata: {e}\n\n")
}
})
.collect()
}
pub async fn wait_for_event<F>(codex: &CodexThread, predicate: F) -> codex_core::protocol::EventMsg
where
F: FnMut(&codex_core::protocol::EventMsg) -> bool,

View File

@@ -1,16 +0,0 @@
[
{
"type": "response.completed",
"response": {
"id": "__ID__",
"usage": {
"input_tokens": 0,
"input_tokens_details": null,
"output_tokens": 0,
"output_tokens_details": null,
"total_tokens": 0
},
"output": []
}
}
]

View File

@@ -36,8 +36,9 @@ use codex_protocol::models::WebSearchAction;
use codex_protocol::openai_models::ReasoningEffort;
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;
use core_test_support::responses::ev_completed_with_tokens;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::mount_sse_once;
use core_test_support::responses::mount_sse_once_match;
use core_test_support::responses::mount_sse_sequence;
@@ -64,11 +65,6 @@ use wiremock::matchers::method;
use wiremock::matchers::path;
use wiremock::matchers::query_param;
/// Build minimal SSE stream with completed marker using the JSON fixture.
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[expect(clippy::unwrap_used)]
fn assert_message_role(request_body: &serde_json::Value, role: &str) {
assert_eq!(request_body["role"].as_str().unwrap(), role);
@@ -259,7 +255,11 @@ 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 = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
// Configure Codex to resume from our file
let codex_home = Arc::new(TempDir::new().unwrap());
@@ -377,7 +377,11 @@ async fn includes_conversation_id_and_model_headers_in_request() {
// Mock server
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex().with_auth(CodexAuth::from_api_key("Test API Key"));
let test = builder
@@ -418,7 +422,11 @@ async fn includes_base_instructions_override_in_request() {
skip_if_no_network!();
// Mock server
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex()
.with_auth(CodexAuth::from_api_key("Test API Key"))
@@ -462,7 +470,11 @@ async fn chatgpt_auth_sends_correct_request() {
// Mock server
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut model_provider = built_in_model_providers()["openai"].clone();
model_provider.base_url = Some(format!("{}/api/codex", server.uri()));
@@ -524,7 +536,10 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
let first = ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
.set_body_raw(sse_completed("resp1"), "text/event-stream");
.set_body_raw(
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
"text/event-stream",
);
// Expect API key header, no ChatGPT account header required.
Mock::given(method("POST"))
@@ -590,7 +605,11 @@ async fn includes_user_instructions_message_in_request() {
skip_if_no_network!();
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex()
.with_auth(CodexAuth::from_api_key("Test API Key"))
@@ -652,7 +671,11 @@ async fn skills_append_to_instructions() {
skip_if_no_network!();
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let codex_home = Arc::new(TempDir::new().unwrap());
let skill_dir = codex_home.path().join("skills/demo");
@@ -720,7 +743,11 @@ async fn includes_configured_effort_in_request() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.1-codex")
.with_config(|config| {
@@ -761,7 +788,11 @@ async fn includes_no_effort_in_request() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.1-codex")
.build(&server)
@@ -800,7 +831,11 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_info
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?;
codex
@@ -835,7 +870,11 @@ async fn user_turn_collaboration_mode_overrides_model_and_effort() -> anyhow::Re
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex {
codex,
config,
@@ -893,7 +932,11 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
config.model_reasoning_summary = ReasoningSummary::Concise;
@@ -933,7 +976,11 @@ async fn reasoning_summary_is_omitted_when_disabled() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
config.model_reasoning_summary = ReasoningSummary::None;
@@ -972,7 +1019,11 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex().with_model("gpt-5.1").build(&server).await?;
codex
@@ -1007,7 +1058,11 @@ 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 = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.1-codex")
.with_config(|config| {
@@ -1047,7 +1102,11 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_model("gpt-5.1")
.with_config(|config| {
@@ -1088,7 +1147,11 @@ async fn includes_developer_instructions_message_in_request() {
skip_if_no_network!();
let server = MockServer::start().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex()
.with_auth(CodexAuth::from_api_key("Test API Key"))
.with_config(|config| {
@@ -1570,7 +1633,10 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
mount_sse_once_match(
&server,
body_string_contains("seed turn"),
sse_completed("resp_seed"),
sse(vec![
ev_response_created("resp_seed"),
ev_completed("resp_seed"),
]),
)
.await;
@@ -1656,7 +1722,10 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
// First request must NOT include `previous_response_id`.
let first = ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
.set_body_raw(sse_completed("resp1"), "text/event-stream");
.set_body_raw(
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
"text/event-stream",
);
// Expect POST to /openai/responses with api-version query param
Mock::given(method("POST"))
@@ -1737,7 +1806,10 @@ async fn env_var_overrides_loaded_auth() {
// First request must NOT include `previous_response_id`.
let first = ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
.set_body_raw(sse_completed("resp1"), "text/event-stream");
.set_body_raw(
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
"text/event-stream",
);
// Expect POST to /openai/responses with api-version query param
Mock::given(method("POST"))

View File

@@ -18,10 +18,6 @@ use core_test_support::wait_for_event;
use pretty_assertions::assert_eq;
use serde_json::Value;
fn sse_completed(id: &str) -> String {
sse(vec![ev_response_created(id), ev_completed(id)])
}
fn collab_mode_with_mode_and_instructions(
mode: ModeKind,
instructions: Option<&str>,
@@ -72,7 +68,11 @@ async fn no_collaboration_instructions_by_default() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let test = test_codex().build(&server).await?;
@@ -100,7 +100,11 @@ async fn user_input_includes_collaboration_instructions_after_override() -> Resu
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let test = test_codex().build(&server).await?;
@@ -144,7 +148,11 @@ async fn collaboration_instructions_added_on_user_turn() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let test = test_codex().build(&server).await?;
let collab_text = "turn instructions";
@@ -182,7 +190,11 @@ async fn override_then_next_turn_uses_updated_collaboration_instructions() -> Re
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let test = test_codex().build(&server).await?;
let collab_text = "override instructions";
@@ -226,7 +238,11 @@ async fn user_turn_overrides_collaboration_instructions_after_override() -> Resu
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let test = test_codex().build(&server).await?;
let base_text = "base instructions";
@@ -282,8 +298,16 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()>
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let _req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let test = test_codex().build(&server).await?;
let first_text = "first instructions";
@@ -354,8 +378,16 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let _req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let test = test_codex().build(&server).await?;
let collab_text = "same instructions";
@@ -423,8 +455,16 @@ async fn collaboration_mode_update_emits_new_instruction_message_when_mode_chang
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let _req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let test = test_codex().build(&server).await?;
let default_text = "default mode instructions";
@@ -501,8 +541,16 @@ async fn collaboration_mode_update_noop_does_not_append_when_mode_is_unchanged()
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let _req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let test = test_codex().build(&server).await?;
let collab_text = "mode-stable instructions";
@@ -576,8 +624,16 @@ async fn resume_replays_collaboration_instructions() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let _req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let mut builder = test_codex();
let initial = builder.build(&server).await?;
@@ -642,7 +698,11 @@ async fn empty_collaboration_instructions_are_ignored() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let test = test_codex().build(&server).await?;

View File

@@ -6,6 +6,9 @@ use codex_core::protocol::RolloutItem;
use codex_core::protocol::RolloutLine;
use codex_protocol::items::TurnItem;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::sse;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
@@ -15,18 +18,13 @@ use wiremock::ResponseTemplate;
use wiremock::matchers::method;
use wiremock::matchers::path;
/// Build minimal SSE stream with completed marker using the JSON fixture.
fn sse_completed(id: &str) -> String {
core_test_support::load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn fork_thread_twice_drops_to_first_message() {
skip_if_no_network!();
// Start a mock server that completes three turns.
let server = MockServer::start().await;
let sse = sse_completed("resp");
let sse = sse(vec![ev_response_created("resp"), ev_completed("resp")]);
let first = ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
.set_body_raw(sse.clone(), "text/event-stream");

View File

@@ -1,20 +1,22 @@
use codex_core::features::Feature;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
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::test_codex::test_codex;
const HIERARCHICAL_AGENTS_SNIPPET: &str =
"Files called AGENTS.md commonly appear in many places inside a container";
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn hierarchical_agents_appends_to_project_doc_in_user_instructions() {
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex().with_config(|config| {
config.features.enable(Feature::ChildAgentsMd);
@@ -49,7 +51,11 @@ async fn hierarchical_agents_appends_to_project_doc_in_user_instructions() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn hierarchical_agents_emits_when_no_project_doc() {
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
)
.await;
let mut builder = test_codex().with_config(|config| {
config.features.enable(Feature::ChildAgentsMd);

View File

@@ -2,16 +2,11 @@
use codex_core::features::Feature;
use codex_protocol::config_types::WebSearchMode;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses;
use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::test_codex;
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[allow(clippy::expect_used)]
fn tool_identifiers(body: &serde_json::Value) -> Vec<String> {
body["tools"]
@@ -31,7 +26,10 @@ fn tool_identifiers(body: &serde_json::Value) -> Vec<String> {
#[allow(clippy::expect_used)]
async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
let server = start_mock_server().await;
let sse = sse_completed(model);
let sse = responses::sse(vec![
responses::ev_response_created(model),
responses::ev_completed(model),
]);
let resp_mock = responses::mount_sse_once(&server, sse).await;
let mut builder = test_codex()

View File

@@ -43,16 +43,16 @@ fn permissions_texts(input: &[serde_json::Value]) -> Vec<String> {
.collect()
}
fn sse_completed(id: &str) -> String {
sse(vec![ev_response_created(id), ev_completed(id)])
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn permissions_message_sent_once_on_start() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let mut builder = test_codex().with_config(move |config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
@@ -84,8 +84,16 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let mut builder = test_codex().with_config(move |config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
@@ -148,8 +156,16 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let mut builder = test_codex().with_config(move |config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
@@ -197,9 +213,21 @@ async fn resume_replays_permissions_messages() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let _req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req3 = mount_sse_once(&server, sse_completed("resp-3")).await;
let _req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let _req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let req3 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-3"), ev_completed("resp-3")]),
)
.await;
let mut builder = test_codex().with_config(|config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
@@ -279,10 +307,26 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let _req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req3 = mount_sse_once(&server, sse_completed("resp-3")).await;
let req4 = mount_sse_once(&server, sse_completed("resp-4")).await;
let _req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let req3 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-3"), ev_completed("resp-3")]),
)
.await;
let req4 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-4"), ev_completed("resp-4")]),
)
.await;
let mut builder = test_codex().with_config(|config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
@@ -404,7 +448,11 @@ async fn permissions_message_includes_writable_roots() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let writable = TempDir::new()?;
let writable_root = AbsolutePathBuf::try_from(writable.path())?;
let sandbox_policy = SandboxPolicy::WorkspaceWrite {

View File

@@ -42,10 +42,6 @@ const LOCAL_FRIENDLY_TEMPLATE: &str =
"You optimize for team morale and being a supportive teammate as much as code quality.";
const LOCAL_PRAGMATIC_TEMPLATE: &str = "You are a deeply pragmatic, effective software engineer.";
fn sse_completed(id: &str) -> String {
sse(vec![ev_response_created(id), ev_completed(id)])
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn personality_does_not_mutate_base_instructions_without_template() {
let codex_home = TempDir::new().expect("create temp dir");
@@ -82,7 +78,11 @@ async fn user_turn_personality_none_does_not_add_update_message() -> anyhow::Res
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let mut builder = test_codex()
.with_model("gpt-5.2-codex")
.with_config(|config| {
@@ -128,7 +128,11 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result<
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let mut builder = test_codex()
.with_model("gpt-5.2-codex")
.with_config(|config| {
@@ -184,7 +188,10 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()>
let server = start_mock_server().await;
let resp_mock = mount_sse_sequence(
&server,
vec![sse_completed("resp-1"), sse_completed("resp-2")],
vec![
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
],
)
.await;
let mut builder = test_codex()
@@ -280,7 +287,10 @@ async fn user_turn_personality_same_value_does_not_add_update_message() -> anyho
let server = start_mock_server().await;
let resp_mock = mount_sse_sequence(
&server,
vec![sse_completed("resp-1"), sse_completed("resp-2")],
vec![
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
],
)
.await;
let mut builder = test_codex()
@@ -387,7 +397,10 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()>
let server = start_mock_server().await;
let resp_mock = mount_sse_sequence(
&server,
vec![sse_completed("resp-1"), sse_completed("resp-2")],
vec![
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
],
)
.await;
let mut builder = test_codex()
@@ -524,7 +537,11 @@ async fn ignores_remote_personality_if_remote_models_disabled() -> anyhow::Resul
)
.await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let mut builder = test_codex()
.with_auth(codex_core::CodexAuth::create_dummy_chatgpt_auth_for_testing())
@@ -640,7 +657,11 @@ async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow
)
.await;
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
let resp_mock = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let mut builder = test_codex()
.with_auth(codex_core::CodexAuth::create_dummy_chatgpt_auth_for_testing())
@@ -753,7 +774,10 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
let resp_mock = mount_sse_sequence(
&server,
vec![sse_completed("resp-1"), sse_completed("resp-2")],
vec![
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
],
)
.await;

View File

@@ -18,8 +18,10 @@ use codex_protocol::config_types::WebSearchMode;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::user_input::UserInput;
use codex_utils_absolute_path::AbsolutePathBuf;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
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;
@@ -46,11 +48,6 @@ fn default_env_context_str(cwd: &str, shell: &Shell) -> String {
)
}
/// Build minimal SSE stream with completed marker using the JSON fixture.
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
fn assert_tool_names(body: &serde_json::Value, expected_names: &[&str]) {
assert_eq!(
body["tools"]
@@ -79,8 +76,16 @@ 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(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let TestCodex {
codex,
@@ -172,8 +177,16 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> {
use pretty_assertions::assert_eq;
let server = start_mock_server().await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
@@ -238,8 +251,16 @@ 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(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let TestCodex { codex, config, .. } = test_codex()
.with_config(|config| {
@@ -315,8 +336,16 @@ 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(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
@@ -407,7 +436,11 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let req = mount_sse_once(&server, sse_completed("resp-1")).await;
let req = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let TestCodex { codex, .. } = test_codex().build(&server).await?;
@@ -542,8 +575,16 @@ 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(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
@@ -646,8 +687,16 @@ 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(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let TestCodex {
codex,
@@ -747,8 +796,16 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
let server = start_mock_server().await;
let req1 = mount_sse_once(&server, sse_completed("resp-1")).await;
let req2 = mount_sse_once(&server, sse_completed("resp-2")).await;
let req1 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
)
.await;
let req2 = mount_sse_once(
&server,
sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
)
.await;
let TestCodex {
codex,
config,

View File

@@ -9,7 +9,6 @@ use codex_protocol::protocol::SessionMeta;
use codex_protocol::protocol::SessionMetaLine;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::UserMessageEvent;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_function_call;
@@ -24,10 +23,6 @@ use tokio::time::Duration;
use tracing_subscriber::prelude::*;
use uuid::Uuid;
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn new_thread_is_recorded_in_state_db() -> Result<()> {
let server = start_mock_server().await;
@@ -195,7 +190,10 @@ async fn user_messages_persist_in_state_db() -> Result<()> {
let server = start_mock_server().await;
mount_sse_sequence(
&server,
vec![sse_completed("resp-1"), sse_completed("resp-2")],
vec![
responses::sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
responses::sse(vec![ev_response_created("resp-2"), ev_completed("resp-2")]),
],
)
.await;

View File

@@ -3,7 +3,9 @@ use codex_core::WireApi;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::sse;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::TestCodex;
use core_test_support::test_codex::test_codex;
@@ -15,10 +17,6 @@ use wiremock::matchers::body_string_contains;
use wiremock::matchers::method;
use wiremock::matchers::path;
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn continue_after_stream_error() {
skip_if_no_network!();
@@ -46,7 +44,13 @@ async fn continue_after_stream_error() {
let ok = ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
.set_body_raw(sse_completed("resp_ok2"), "text/event-stream");
.set_body_raw(
sse(vec![
ev_response_created("resp_ok2"),
ev_completed("resp_ok2"),
]),
"text/event-stream",
);
Mock::given(method("POST"))
.and(path("/v1/responses"))

View File

@@ -7,7 +7,9 @@ use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::load_sse_fixture;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::sse;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::TestCodex;
use core_test_support::test_codex::test_codex;
@@ -24,10 +26,6 @@ fn sse_incomplete() -> String {
load_sse_fixture("tests/fixtures/incomplete_sse.json")
}
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn retries_on_early_close() {
skip_if_no_network!();
@@ -48,7 +46,13 @@ async fn retries_on_early_close() {
} else {
ResponseTemplate::new(200)
.insert_header("content-type", "text/event-stream")
.set_body_raw(sse_completed("resp_ok"), "text/event-stream")
.set_body_raw(
sse(vec![
ev_response_created("resp_ok"),
ev_completed("resp_ok"),
]),
"text/event-stream",
)
}
}
}

View File

@@ -5,7 +5,6 @@ use codex_core::built_in_model_providers;
use codex_core::features::Feature;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::WebSearchMode;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses;
use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_no_network;
@@ -13,10 +12,6 @@ use core_test_support::test_codex::test_codex;
use pretty_assertions::assert_eq;
use serde_json::Value;
fn sse_completed(id: &str) -> String {
load_sse_fixture_with_id("../fixtures/completed_template.json", id)
}
#[allow(clippy::expect_used)]
fn find_web_search_tool(body: &Value) -> &Value {
body["tools"]
@@ -41,7 +36,10 @@ async fn web_search_mode_cached_sets_external_web_access_false() {
skip_if_no_network!();
let server = start_mock_server().await;
let sse = sse_completed("resp-1");
let sse = responses::sse(vec![
responses::ev_response_created("resp-1"),
responses::ev_completed("resp-1"),
]);
let resp_mock = responses::mount_sse_once(&server, sse).await;
let mut builder = test_codex()
@@ -72,7 +70,10 @@ async fn web_search_mode_takes_precedence_over_legacy_flags() {
skip_if_no_network!();
let server = start_mock_server().await;
let sse = sse_completed("resp-1");
let sse = responses::sse(vec![
responses::ev_response_created("resp-1"),
responses::ev_completed("resp-1"),
]);
let resp_mock = responses::mount_sse_once(&server, sse).await;
let mut builder = test_codex()
@@ -104,7 +105,10 @@ async fn web_search_mode_defaults_to_cached_when_unset() {
skip_if_no_network!();
let server = start_mock_server().await;
let sse = sse_completed("resp-1");
let sse = responses::sse(vec![
responses::ev_response_created("resp-1"),
responses::ev_completed("resp-1"),
]);
let resp_mock = responses::mount_sse_once(&server, sse).await;
let mut builder = test_codex()
@@ -139,7 +143,16 @@ async fn web_search_mode_updates_between_turns_with_sandbox_policy() {
let server = start_mock_server().await;
let resp_mock = responses::mount_sse_sequence(
&server,
vec![sse_completed("resp-1"), sse_completed("resp-2")],
vec![
responses::sse(vec![
responses::ev_response_created("resp-1"),
responses::ev_completed("resp-1"),
]),
responses::sse(vec![
responses::ev_response_created("resp-2"),
responses::ev_completed("resp-2"),
]),
],
)
.await;
@@ -191,7 +204,10 @@ async fn web_search_mode_defaults_to_disabled_for_azure_responses() {
skip_if_no_network!();
let server = start_mock_server().await;
let sse = sse_completed("resp-1");
let sse = responses::sse(vec![
responses::ev_response_created("resp-1"),
responses::ev_completed("resp-1"),
]);
let resp_mock = responses::mount_sse_once(&server, sse).await;
let mut builder = test_codex()