#![allow(clippy::expect_used, clippy::unwrap_used)] use core_test_support::responses; use core_test_support::test_codex_exec::test_codex_exec; use predicates::prelude::PredicateBooleanExt; use predicates::str::contains; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_includes_workspace_agents_md_in_request() -> anyhow::Result<()> { let test = test_codex_exec(); std::fs::write(test.cwd_path().join("AGENTS.md"), "workspace instructions")?; let server = responses::start_mock_server().await; let body = responses::sse(vec![ responses::ev_response_created("resp1"), responses::ev_assistant_message("m1", "fixture hello"), responses::ev_completed("resp1"), ]); let response_mock = responses::mount_sse_once(&server, body).await; test.cmd_with_server(&server) .arg("--skip-git-repo-check") .arg("tell me something") .assert() .success(); let user_messages = response_mock.single_request().message_input_texts("user"); assert!( user_messages .iter() .any(|text| text.contains("workspace instructions")), "request should include workspace AGENTS.md instructions: {user_messages:?}" ); Ok(()) } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_prefers_workspace_agents_override_md() -> anyhow::Result<()> { let test = test_codex_exec(); std::fs::write(test.cwd_path().join("AGENTS.md"), "base instructions")?; std::fs::write( test.cwd_path().join("AGENTS.override.md"), "override instructions", )?; let server = responses::start_mock_server().await; let body = responses::sse(vec![ responses::ev_response_created("resp1"), responses::ev_assistant_message("m1", "fixture hello"), responses::ev_completed("resp1"), ]); let response_mock = responses::mount_sse_once(&server, body).await; test.cmd_with_server(&server) .arg("--skip-git-repo-check") .arg("tell me something") .assert() .success(); let user_messages = response_mock.single_request().message_input_texts("user"); assert!( user_messages .iter() .any(|text| text.contains("override instructions")), "request should include AGENTS.override.md instructions: {user_messages:?}" ); assert!( user_messages .iter() .all(|text| !text.contains("base instructions")), "request should exclude shadowed AGENTS.md instructions: {user_messages:?}" ); Ok(()) } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_surfaces_project_instruction_loading_warnings() -> anyhow::Result<()> { let test = test_codex_exec(); let project_agents_path = test.cwd_path().join("AGENTS.md"); std::fs::write(&project_agents_path, b"project\xFFinstructions")?; let server = responses::start_mock_server().await; let body = responses::sse(vec![ responses::ev_response_created("resp1"), responses::ev_assistant_message("m1", "fixture hello"), responses::ev_completed("resp1"), ]); responses::mount_sse_once(&server, body).await; test.cmd_with_server(&server) .arg("--skip-git-repo-check") .arg("tell me something") .assert() .success() .stderr(contains("invalid UTF-8").and(contains(project_agents_path.display().to_string()))); Ok(()) } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_json_surfaces_project_instruction_loading_warnings() -> anyhow::Result<()> { let test = test_codex_exec(); let project_agents_path = test.cwd_path().join("AGENTS.md"); std::fs::write(&project_agents_path, b"project\xFFinstructions")?; let server = responses::start_mock_server().await; let body = responses::sse(vec![ responses::ev_response_created("resp1"), responses::ev_assistant_message("m1", "fixture hello"), responses::ev_completed("resp1"), ]); responses::mount_sse_once(&server, body).await; let output = test .cmd_with_server(&server) .arg("--skip-git-repo-check") .arg("--json") .arg("tell me something") .assert() .success() .get_output() .stdout .clone(); let events = String::from_utf8(output)? .lines() .map(serde_json::from_str::) .collect::, _>>()?; assert!( events.iter().any(|event| { event["type"] == "item.completed" && event["item"]["type"] == "error" && event["item"]["message"].as_str().is_some_and(|message| { message.contains("invalid UTF-8") && message.contains(project_agents_path.display().to_string().as_str()) }) }), "expected a JSONL warning event; observed: {events:?}" ); Ok(()) } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_surfaces_global_instruction_loading_warnings() -> anyhow::Result<()> { let test = test_codex_exec(); let global_agents_path = test.home_path().join("AGENTS.md"); let global_agents_source_suffix = format!( "{}{}AGENTS.md", test.home_path() .file_name() .expect("temporary Codex home should have a file name") .to_string_lossy(), std::path::MAIN_SEPARATOR, ); std::fs::write(&global_agents_path, b"global\xFFinstructions")?; let server = responses::start_mock_server().await; let body = responses::sse(vec![ responses::ev_response_created("resp1"), responses::ev_assistant_message("m1", "fixture hello"), responses::ev_completed("resp1"), ]); responses::mount_sse_once(&server, body).await; test.cmd_with_server(&server) .arg("--skip-git-repo-check") .arg("tell me something") .assert() .success() .stderr(contains("invalid UTF-8").and(contains(global_agents_source_suffix))); Ok(()) }