Allow --output-last-message to print to stdout

This commit is contained in:
pakrym-oai
2025-10-01 18:02:18 -07:00
parent c07fb71186
commit f8f66adeac
9 changed files with 198 additions and 77 deletions

View File

@@ -51,7 +51,7 @@ fn event(id: &str, msg: EventMsg) -> Event {
#[test]
fn session_configured_produces_thread_started_event() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let session_id =
codex_protocol::ConversationId::from_string("67e55044-10b1-426f-9247-bb680e5fe0c8")
.unwrap();
@@ -79,7 +79,7 @@ fn session_configured_produces_thread_started_event() {
#[test]
fn task_started_produces_turn_started_event() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let out = ep.collect_thread_events(&event(
"t1",
EventMsg::TaskStarted(codex_core::protocol::TaskStartedEvent {
@@ -92,7 +92,7 @@ fn task_started_produces_turn_started_event() {
#[test]
fn web_search_end_emits_item_completed() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let query = "rust async await".to_string();
let out = ep.collect_thread_events(&event(
"w1",
@@ -119,7 +119,7 @@ fn plan_update_emits_todo_list_started_updated_and_completed() {
use codex_core::plan_tool::StepStatus;
use codex_core::plan_tool::UpdatePlanArgs;
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
// First plan update => item.started (todo_list)
let first = event(
@@ -236,7 +236,7 @@ fn plan_update_emits_todo_list_started_updated_and_completed() {
#[test]
fn mcp_tool_call_begin_and_end_emit_item_events() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let invocation = McpInvocation {
server: "server_a".to_string(),
tool: "tool_x".to_string(),
@@ -296,7 +296,7 @@ fn mcp_tool_call_begin_and_end_emit_item_events() {
#[test]
fn mcp_tool_call_failure_sets_failed_status() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let invocation = McpInvocation {
server: "server_b".to_string(),
tool: "tool_y".to_string(),
@@ -343,7 +343,7 @@ fn plan_update_after_complete_starts_new_todo_list_with_new_id() {
use codex_core::plan_tool::StepStatus;
use codex_core::plan_tool::UpdatePlanArgs;
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
// First turn: start + complete
let start = event(
@@ -388,7 +388,7 @@ fn plan_update_after_complete_starts_new_todo_list_with_new_id() {
#[test]
fn agent_reasoning_produces_item_completed_reasoning() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let ev = event(
"e1",
EventMsg::AgentReasoning(AgentReasoningEvent {
@@ -411,7 +411,7 @@ fn agent_reasoning_produces_item_completed_reasoning() {
#[test]
fn agent_message_produces_item_completed_assistant_message() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let ev = event(
"e1",
EventMsg::AgentMessage(AgentMessageEvent {
@@ -434,7 +434,7 @@ fn agent_message_produces_item_completed_assistant_message() {
#[test]
fn error_event_produces_error() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let out = ep.collect_thread_events(&event(
"e1",
EventMsg::Error(codex_core::protocol::ErrorEvent {
@@ -451,7 +451,7 @@ fn error_event_produces_error() {
#[test]
fn stream_error_event_produces_error() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let out = ep.collect_thread_events(&event(
"e1",
EventMsg::StreamError(codex_core::protocol::StreamErrorEvent {
@@ -468,7 +468,7 @@ fn stream_error_event_produces_error() {
#[test]
fn error_followed_by_task_complete_produces_turn_failed() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let error_event = event(
"e1",
@@ -501,7 +501,7 @@ fn error_followed_by_task_complete_produces_turn_failed() {
#[test]
fn exec_command_end_success_produces_completed_command_item() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
// Begin -> no output
let begin = event(
@@ -561,7 +561,7 @@ fn exec_command_end_success_produces_completed_command_item() {
#[test]
fn exec_command_end_failure_produces_failed_command_item() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
// Begin -> no output
let begin = event(
@@ -620,7 +620,7 @@ fn exec_command_end_failure_produces_failed_command_item() {
#[test]
fn exec_command_end_without_begin_is_ignored() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
// End event arrives without a prior Begin; should produce no thread events.
let end_only = event(
@@ -641,7 +641,7 @@ fn exec_command_end_without_begin_is_ignored() {
#[test]
fn patch_apply_success_produces_item_completed_patchapply() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
// Prepare a patch with multiple kinds of changes
let mut changes = std::collections::HashMap::new();
@@ -723,7 +723,7 @@ fn patch_apply_success_produces_item_completed_patchapply() {
#[test]
fn patch_apply_failure_produces_item_completed_patchapply_failed() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
let mut changes = std::collections::HashMap::new();
changes.insert(
@@ -777,7 +777,7 @@ fn patch_apply_failure_produces_item_completed_patchapply_failed() {
#[test]
fn task_complete_produces_turn_completed_with_usage() {
let mut ep = EventProcessorWithJsonOutput::new(None);
let mut ep = EventProcessorWithJsonOutput::default();
// First, feed a TokenCount event with known totals.
let usage = codex_core::protocol::TokenUsage {

View File

@@ -1,5 +1,6 @@
// Aggregates all former standalone integration tests as modules.
mod apply_patch;
mod output_last_message;
mod output_schema;
mod resume;
mod sandbox;

View File

@@ -0,0 +1,106 @@
#![cfg(not(target_os = "windows"))]
#![allow(clippy::expect_used, clippy::unwrap_used)]
use core_test_support::responses;
use core_test_support::test_codex_exec::test_codex_exec;
use wiremock::matchers::any;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn exec_includes_output_last_message_in_request() -> anyhow::Result<()> {
let test = test_codex_exec();
let last_message_path = test.cwd_path().join("last_message.txt");
let server = responses::start_mock_server().await;
let body = responses::sse(vec![
serde_json::json!({
"type": "response.created",
"response": {"id": "resp1"}
}),
responses::ev_assistant_message("m1", "fixture hello"),
responses::ev_completed("resp1"),
]);
responses::mount_sse_once_match(&server, any(), body).await;
test.cmd_with_server(&server)
.arg("--skip-git-repo-check")
// keep using -C in the test to exercise the flag as well
.arg("-C")
.arg(test.cwd_path())
.arg("--output-last-message")
.arg(&last_message_path)
.arg("tell me a joke")
.assert()
.success();
assert_eq!(
std::fs::read_to_string(&last_message_path)?,
"fixture hello"
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn exec_includes_output_last_message_in_request_json() -> anyhow::Result<()> {
let test = test_codex_exec();
let last_message_path = test.cwd_path().join("last_message.txt");
let server = responses::start_mock_server().await;
let body = responses::sse(vec![
serde_json::json!({
"type": "response.created",
"response": {"id": "resp1"}
}),
responses::ev_assistant_message("m1", "fixture hello"),
responses::ev_completed("resp1"),
]);
responses::mount_sse_once_match(&server, any(), body).await;
test.cmd_with_server(&server)
.arg("--skip-git-repo-check")
// keep using -C in the test to exercise the flag as well
.arg("-C")
.arg(test.cwd_path())
.arg("--output-last-message")
.arg(&last_message_path)
.arg("--json")
.arg("tell me a joke")
.assert()
.success();
assert_eq!(
std::fs::read_to_string(&last_message_path)?,
"fixture hello"
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn exec_includes_output_last_message_in_request_stdout() -> anyhow::Result<()> {
let test = test_codex_exec();
let server = responses::start_mock_server().await;
let body = responses::sse(vec![
serde_json::json!({
"type": "response.created",
"response": {"id": "resp1"}
}),
responses::ev_assistant_message("m1", "fixture hello"),
responses::ev_completed("resp1"),
]);
responses::mount_sse_once_match(&server, any(), body).await;
test.cmd_with_server(&server)
.arg("--skip-git-repo-check")
// keep using -C in the test to exercise the flag as well
.arg("-C")
.arg(test.cwd_path())
.arg("tell me a joke")
.arg("--output-last-message")
.assert()
.success()
.stdout("fixture hello\n");
Ok(())
}