Codex Handbook
core/tests/suite/stream_no_completed.rs 102 lines
//! Verifies that the agent retries when the SSE stream terminates before//! delivering a `response.completed` event.use codex_model_provider_info::ModelProviderInfo;use codex_model_provider_info::WireApi;use codex_protocol::protocol::EventMsg;use codex_protocol::protocol::Op;use codex_protocol::user_input::UserInput;use core_test_support::responses;use core_test_support::skip_if_no_network;use core_test_support::streaming_sse::StreamingSseChunk;use core_test_support::streaming_sse::start_streaming_sse_server;use core_test_support::test_codex::TestCodex;use core_test_support::test_codex::test_codex;use core_test_support::wait_for_event;fn sse_incomplete() -> String {    responses::sse(vec![serde_json::json!({        "type": "response.output_item.done",    })])}#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn retries_on_early_close() {    skip_if_no_network!();    let incomplete_sse = sse_incomplete();    let completed_sse = responses::sse_completed("resp_ok");    let (server, _) = start_streaming_sse_server(vec![        vec![StreamingSseChunk {            gate: None,            body: incomplete_sse,        }],        vec![StreamingSseChunk {            gate: None,            body: completed_sse,        }],    ])    .await;    // Configure retry behavior explicitly to avoid mutating process-wide    // environment variables.    let model_provider = ModelProviderInfo {        name: "openai".into(),        base_url: Some(format!("{}/v1", server.uri())),        // Environment variable that should exist in the test environment.        // ModelClient will return an error if the environment variable for the        // provider is not set.        env_key: Some("PATH".into()),        env_key_instructions: None,        experimental_bearer_token: None,        auth: None,        aws: None,        wire_api: WireApi::Responses,        query_params: None,        http_headers: None,        env_http_headers: None,        // exercise retry path: first attempt yields incomplete stream, so allow 1 retry        request_max_retries: Some(0),        stream_max_retries: Some(1),        stream_idle_timeout_ms: Some(2000),        websocket_connect_timeout_ms: None,        requires_openai_auth: false,        supports_websockets: false,    };    let TestCodex { codex, .. } = test_codex()        .with_config(move |config| {            config.model_provider = model_provider;        })        .build_with_streaming_server(&server)        .await        .unwrap();    codex        .submit(Op::UserInput {            items: vec![UserInput::Text {                text: "hello".into(),                text_elements: Vec::new(),            }],            final_output_json_schema: None,            responsesapi_client_metadata: None,            additional_context: Default::default(),            thread_settings: Default::default(),        })        .await        .unwrap();    // Wait until TurnComplete (should succeed after retry).    wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;    let requests = server.requests().await;    assert_eq!(        requests.len(),        2,        "expected retry after incomplete SSE stream"    );    server.shutdown().await;}