This commit is contained in:
pakrym-oai
2025-11-14 17:33:05 -08:00
parent 89ecc00b79
commit 622ef4e47a
3 changed files with 52 additions and 84 deletions

View File

@@ -101,7 +101,7 @@ You are producing plain text that will later be styled by the CLI. Follow these
* Use inline code to make file paths clickable.
* Each reference should have a stand alone path. Even if it's the same file.
* Accepted: absolute, workspacerelative, a/ or b/ diff prefixes, or bare filename/suffix.
* Line/column (1based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).
* Optionally include line/column (1based): :line[:column] or #Lline[Ccolumn] (column defaults to 1). Do not run extra command to find line numbers.
* Do not use URIs like file://, vscode://, or https://.
* Do not provide range of lines
* Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\repo\project\main.rs:12:5

View File

@@ -1,35 +1,29 @@
use codex_core::CodexAuth;
use codex_core::ConversationManager;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_core::protocol_config_types::ReasoningEffort;
use core_test_support::load_default_config_for_test;
use core_test_support::responses::start_mock_server;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
const CONFIG_TOML: &str = "config.toml";
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn override_turn_context_does_not_persist_when_config_exists() {
let codex_home = TempDir::new().unwrap();
let config_path = codex_home.path().join(CONFIG_TOML);
let server = start_mock_server().await;
let test = test_codex()
.with_model("gpt-4o")
.build(&server)
.await
.expect("build test codex");
let codex = test.codex.clone();
let config_path = test.home.path().join(CONFIG_TOML);
let initial_contents = "model = \"gpt-4o\"\n";
tokio::fs::write(&config_path, initial_contents)
.await
.expect("seed config.toml");
let mut config = load_default_config_for_test(&codex_home);
config.model = "gpt-4o".to_string();
let conversation_manager =
ConversationManager::with_auth(CodexAuth::from_api_key("Test API Key"));
let codex = conversation_manager
.new_conversation(config)
.await
.expect("create conversation")
.conversation;
codex
.submit(Op::OverrideTurnContext {
cwd: None,
@@ -53,23 +47,21 @@ async fn override_turn_context_does_not_persist_when_config_exists() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn override_turn_context_does_not_create_config_file() {
let codex_home = TempDir::new().unwrap();
let config_path = codex_home.path().join(CONFIG_TOML);
let server = start_mock_server().await;
let test = test_codex().build(&server).await.expect("build test codex");
let codex = test.codex.clone();
let config_path = test.home.path().join(CONFIG_TOML);
if config_path.exists() {
tokio::fs::remove_file(&config_path)
.await
.expect("remove existing config.toml");
}
assert!(
!config_path.exists(),
"test setup should start without config"
);
let config = load_default_config_for_test(&codex_home);
let conversation_manager =
ConversationManager::with_auth(CodexAuth::from_api_key("Test API Key"));
let codex = conversation_manager
.new_conversation(config)
.await
.expect("create conversation")
.conversation;
codex
.submit(Op::OverrideTurnContext {
cwd: None,

View File

@@ -1,6 +1,4 @@
use anyhow::Context;
use codex_core::ConversationManager;
use codex_core::NewConversation;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::EventMsg;
use codex_core::protocol::ExecCommandEndEvent;
@@ -10,7 +8,6 @@ use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_core::protocol::TurnAbortReason;
use core_test_support::assert_regex_match;
use core_test_support::load_default_config_for_test;
use core_test_support::responses;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -24,42 +21,25 @@ use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
use core_test_support::wait_for_event_match;
use regex_lite::escape;
use std::path::PathBuf;
use tempfile::TempDir;
#[tokio::test]
async fn user_shell_cmd_ls_and_cat_in_temp_dir() {
// Create a temporary working directory with a known file.
let cwd = TempDir::new().unwrap();
async fn user_shell_cmd_ls_and_cat_in_temp_dir() -> anyhow::Result<()> {
let server = responses::start_mock_server().await;
let mut builder = test_codex();
let test = builder.build(&server).await?;
// Create a file in the Codex workspace so shell commands can inspect it.
let file_name = "hello.txt";
let file_path: PathBuf = cwd.path().join(file_name);
let file_path = test.cwd.path().join(file_name);
let contents = "hello from bang test\n";
tokio::fs::write(&file_path, contents)
.await
.expect("write temp file");
// Load config and pin cwd to the temp dir so ls/cat operate there.
let codex_home = TempDir::new().unwrap();
let mut config = load_default_config_for_test(&codex_home);
config.cwd = cwd.path().to_path_buf();
let conversation_manager =
ConversationManager::with_auth(codex_core::CodexAuth::from_api_key("dummy"));
let NewConversation {
conversation: codex,
..
} = conversation_manager
.new_conversation(config)
.await
.expect("create new conversation");
tokio::fs::write(&file_path, contents).await?;
// 1) shell command should list the file
let list_cmd = "ls".to_string();
codex
test.codex
.submit(Op::RunUserShellCommand { command: list_cmd })
.await
.unwrap();
let msg = wait_for_event(&codex, |ev| matches!(ev, EventMsg::ExecCommandEnd(_))).await;
.await?;
let msg = wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::ExecCommandEnd(_))).await;
let EventMsg::ExecCommandEnd(ExecCommandEndEvent {
stdout, exit_code, ..
}) = msg
@@ -74,11 +54,10 @@ async fn user_shell_cmd_ls_and_cat_in_temp_dir() {
// 2) shell command should print the file contents verbatim
let cat_cmd = format!("cat {file_name}");
codex
test.codex
.submit(Op::RunUserShellCommand { command: cat_cmd })
.await
.unwrap();
let msg = wait_for_event(&codex, |ev| matches!(ev, EventMsg::ExecCommandEnd(_))).await;
.await?;
let msg = wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::ExecCommandEnd(_))).await;
let EventMsg::ExecCommandEnd(ExecCommandEndEvent {
mut stdout,
exit_code,
@@ -93,40 +72,37 @@ async fn user_shell_cmd_ls_and_cat_in_temp_dir() {
stdout = stdout.replace("\r\n", "\n");
}
assert_eq!(stdout, contents);
Ok(())
}
#[tokio::test]
async fn user_shell_cmd_can_be_interrupted() {
// Set up isolated config and conversation.
let codex_home = TempDir::new().unwrap();
let config = load_default_config_for_test(&codex_home);
let conversation_manager =
ConversationManager::with_auth(codex_core::CodexAuth::from_api_key("dummy"));
let NewConversation {
conversation: codex,
..
} = conversation_manager
.new_conversation(config)
.await
.expect("create new conversation");
async fn user_shell_cmd_can_be_interrupted() -> anyhow::Result<()> {
let server = responses::start_mock_server().await;
let mut builder = test_codex();
let test = builder.build(&server).await?;
// Start a long-running command and then interrupt it.
let sleep_cmd = "sleep 5".to_string();
codex
test.codex
.submit(Op::RunUserShellCommand { command: sleep_cmd })
.await
.unwrap();
.await?;
// Wait until it has started (ExecCommandBegin), then interrupt.
let _ = wait_for_event(&codex, |ev| matches!(ev, EventMsg::ExecCommandBegin(_))).await;
codex.submit(Op::Interrupt).await.unwrap();
let _ = wait_for_event(&test.codex, |ev| {
matches!(ev, EventMsg::ExecCommandBegin(_))
})
.await;
test.codex.submit(Op::Interrupt).await?;
// Expect a TurnAborted(Interrupted) notification.
let msg = wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnAborted(_))).await;
let msg = wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnAborted(_))).await;
let EventMsg::TurnAborted(ev) = msg else {
unreachable!()
};
assert_eq!(ev.reason, TurnAbortReason::Interrupted);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]