From e18fe7a07f4a6e255494e2b117581fff2cb55e55 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 21 Apr 2026 18:08:25 -0700 Subject: [PATCH] test(core): move prompt debug coverage to integration suite (#18916) ## Why `build_prompt_input` now initializes `ExecServerRuntimePaths`, which requires a configured Codex executable path. The previous inline unit test in `core/src/prompt_debug.rs` built a bare `test_config()` and then failed before it could assert anything useful: ```text Codex executable path is not configured ``` This coverage is also integration-shaped: it drives the public `build_prompt_input` entry point through config, thread, and session setup rather than testing a small internal helper in isolation. Bazel CI did not catch this earlier because the affected test was behind the same wrapped Rust unit-test path fixed by #18913. Before that launcher/sharding fix, the outer `workspace_root_test` changed the working directory for Insta compatibility while the inner `rules_rust` sharding wrapper still expected its runfiles working directory. In practice, Bazel could report success without executing the Rust test cases in that shard. Once #18913 makes the wrapper run the Rust test binary directly and shard with libtest arguments, this stale unit test actually runs and exposes the missing `codex_self_exe` setup. ## What Changed - Moved `build_prompt_input_includes_context_and_user_message` out of `core/src/prompt_debug.rs`. - Added `core/tests/suite/prompt_debug_tests.rs` and registered it from `core/tests/suite/mod.rs`. - Builds the test config with `ConfigBuilder` and provides `codex_self_exe` using the current test executable, matching the runtime-path invariant required by prompt debug setup. - Preserves the existing assertions that the generated prompt input includes both the debug user message and project-specific user instructions. ## Verification - `cargo test -p codex-core --test all prompt_debug_tests::build_prompt_input_includes_context_and_user_message` - `bazel test //codex-rs/core:core-all-test --test_arg=prompt_debug_tests::build_prompt_input_includes_context_and_user_message --test_output=errors` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18916). * #18913 * __->__ #18916 --- codex-rs/core/src/prompt_debug.rs | 59 ------------------ codex-rs/core/tests/suite/mod.rs | 1 + .../core/tests/suite/prompt_debug_tests.rs | 60 +++++++++++++++++++ 3 files changed, 61 insertions(+), 59 deletions(-) create mode 100644 codex-rs/core/tests/suite/prompt_debug_tests.rs diff --git a/codex-rs/core/src/prompt_debug.rs b/codex-rs/core/src/prompt_debug.rs index 1f62c2b08..b6c66410a 100644 --- a/codex-rs/core/src/prompt_debug.rs +++ b/codex-rs/core/src/prompt_debug.rs @@ -98,62 +98,3 @@ pub(crate) async fn build_prompt_input_from_session( Ok(prompt.get_formatted_input()) } - -#[cfg(test)] -mod tests { - use codex_protocol::models::ContentItem; - use codex_protocol::models::ResponseItem; - use codex_protocol::user_input::UserInput; - use codex_utils_absolute_path::AbsolutePathBuf; - use pretty_assertions::assert_eq; - - use crate::config::test_config; - - use super::build_prompt_input; - - #[tokio::test] - async fn build_prompt_input_includes_context_and_user_message() { - let codex_home = tempfile::tempdir().expect("create codex home"); - let cwd = tempfile::tempdir().expect("create cwd"); - let mut config = test_config().await; - config.codex_home = - AbsolutePathBuf::from_absolute_path(codex_home.path()).expect("codex home is absolute"); - config.cwd = AbsolutePathBuf::try_from(cwd.path().to_path_buf()).expect("absolute cwd"); - config.user_instructions = Some("Project-specific test instructions".to_string()); - - let input = build_prompt_input( - config, - vec![UserInput::Text { - text: "hello from debug prompt".to_string(), - text_elements: Vec::new(), - }], - ) - .await - .expect("build prompt input"); - - let expected_user_message = ResponseItem::Message { - id: None, - role: "user".to_string(), - content: vec![ContentItem::InputText { - text: "hello from debug prompt".to_string(), - }], - end_turn: None, - phase: None, - }; - assert_eq!(input.last(), Some(&expected_user_message)); - assert!(input.iter().any(|item| { - let ResponseItem::Message { content, .. } = item else { - return false; - }; - - content.iter().any(|content_item| { - let (ContentItem::InputText { text } | ContentItem::OutputText { text }) = - content_item - else { - return false; - }; - text.contains("Project-specific test instructions") - }) - })); - } -} diff --git a/codex-rs/core/tests/suite/mod.rs b/codex-rs/core/tests/suite/mod.rs index 9f8389c45..6c4cb1981 100644 --- a/codex-rs/core/tests/suite/mod.rs +++ b/codex-rs/core/tests/suite/mod.rs @@ -67,6 +67,7 @@ mod personality; mod personality_migration; mod plugins; mod prompt_caching; +mod prompt_debug_tests; mod quota_exceeded; mod realtime_conversation; mod remote_env; diff --git a/codex-rs/core/tests/suite/prompt_debug_tests.rs b/codex-rs/core/tests/suite/prompt_debug_tests.rs new file mode 100644 index 000000000..1221560bb --- /dev/null +++ b/codex-rs/core/tests/suite/prompt_debug_tests.rs @@ -0,0 +1,60 @@ +use anyhow::Result; +use codex_core::build_prompt_input; +use codex_core::config::ConfigBuilder; +use codex_core::config::ConfigOverrides; +use codex_protocol::models::ContentItem; +use codex_protocol::models::ResponseItem; +use codex_protocol::user_input::UserInput; +use pretty_assertions::assert_eq; +use tempfile::TempDir; + +#[tokio::test] +async fn build_prompt_input_includes_context_and_user_message() -> Result<()> { + let codex_home = TempDir::new()?; + let cwd = TempDir::new()?; + let mut config = ConfigBuilder::default() + .codex_home(codex_home.path().to_path_buf()) + .harness_overrides(ConfigOverrides { + cwd: Some(cwd.path().to_path_buf()), + codex_self_exe: Some(std::env::current_exe()?), + ..ConfigOverrides::default() + }) + .build() + .await?; + config.user_instructions = Some("Project-specific test instructions".to_string()); + + let input = build_prompt_input( + config, + vec![UserInput::Text { + text: "hello from debug prompt".to_string(), + text_elements: Vec::new(), + }], + ) + .await?; + + let expected_user_message = ResponseItem::Message { + id: None, + role: "user".to_string(), + content: vec![ContentItem::InputText { + text: "hello from debug prompt".to_string(), + }], + end_turn: None, + phase: None, + }; + assert_eq!(input.last(), Some(&expected_user_message)); + assert!(input.iter().any(|item| { + let ResponseItem::Message { content, .. } = item else { + return false; + }; + + content.iter().any(|content_item| { + let (ContentItem::InputText { text } | ContentItem::OutputText { text }) = content_item + else { + return false; + }; + text.contains("Project-specific test instructions") + }) + })); + + Ok(()) +}