Codex Handbook
core/tests/suite/user_notification.rs 82 lines
#![cfg(not(target_os = "windows"))]use std::os::unix::fs::PermissionsExt;use codex_protocol::protocol::EventMsg;use codex_protocol::protocol::Op;use codex_protocol::user_input::UserInput;use core_test_support::fs_wait;use core_test_support::responses;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 pretty_assertions::assert_eq;use serde_json::Value;use serde_json::json;use tempfile::TempDir;use responses::ev_assistant_message;use responses::ev_completed;use responses::sse;use responses::start_mock_server;use std::time::Duration;#[tokio::test(flavor = "multi_thread", worker_threads = 2)]async fn summarize_context_three_requests_and_instructions() -> anyhow::Result<()> {    skip_if_no_network!(Ok(()));    let server = start_mock_server().await;    let sse1 = sse(vec![ev_assistant_message("m1", "Done"), ev_completed("r1")]);    responses::mount_sse_once(&server, sse1).await;    let notify_dir = TempDir::new()?;    // write a script to the notify that touches a file next to it    let notify_script = notify_dir.path().join("notify.sh");    std::fs::write(        &notify_script,        r#"#!/bin/bashset -epayload_path="$(dirname "${0}")/notify.txt"tmp_path="${payload_path}.tmp"echo -n "${@: -1}" > "${tmp_path}"mv "${tmp_path}" "${payload_path}""#,    )?;    std::fs::set_permissions(&notify_script, std::fs::Permissions::from_mode(0o755))?;    let notify_file = notify_dir.path().join("notify.txt");    let notify_script_str = notify_script.to_str().unwrap().to_string();    let TestCodex { codex, .. } = test_codex()        .with_config(move |cfg| cfg.notify = Some(vec![notify_script_str]))        .build(&server)        .await?;    // 1) Normal user input – should hit server once.    codex        .submit(Op::UserInput {            items: vec![UserInput::Text {                text: "hello world".into(),                text_elements: Vec::new(),            }],            final_output_json_schema: None,            responsesapi_client_metadata: None,            additional_context: Default::default(),            thread_settings: Default::default(),        })        .await?;    wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;    // We fork the notify script, so we need to wait for it to write to the file.    fs_wait::wait_for_path_exists(&notify_file, Duration::from_secs(5)).await?;    let notify_payload_raw = tokio::fs::read_to_string(&notify_file).await?;    let payload: Value = serde_json::from_str(&notify_payload_raw)?;    assert_eq!(payload["type"], json!("agent-turn-complete"));    assert_eq!(payload["input-messages"], json!(["hello world"]));    assert_eq!(payload["last-assistant-message"], json!("Done"));    Ok(())}