mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
## Why Naming a paginated thread before its first turn must make it durable so it can be resumed immediately and after an app-server restart. ## What changed Persist the live rollout before updating name metadata in SQLite. Propagate persistence failures before saving the name. ## Testing Add thread-store tests for empty-thread persistence and failure handling, plus an app-server regression test that resumes a named empty thread before and after a restart and completes its first turn. GitOrigin-RevId: 2f7556c7de65f6a86177130c39e1a17951496931
105 lines
3.8 KiB
Rust
105 lines
3.8 KiB
Rust
//! Naming persists an empty paginated thread for resume before and after a restart.
|
|
|
|
use anyhow::Result;
|
|
use app_test_support::MockResponsesConfig;
|
|
use app_test_support::TestAppServer;
|
|
use app_test_support::create_mock_responses_server_repeating_assistant;
|
|
use codex_app_server_protocol::ThreadHistoryMode;
|
|
use codex_app_server_protocol::ThreadResumeParams;
|
|
use codex_app_server_protocol::ThreadResumeResponse;
|
|
use codex_app_server_protocol::ThreadSetNameParams;
|
|
use codex_app_server_protocol::ThreadSetNameResponse;
|
|
use codex_app_server_protocol::ThreadStartParams;
|
|
use codex_app_server_protocol::ThreadStartResponse;
|
|
use codex_app_server_protocol::TurnStartParams;
|
|
use codex_app_server_protocol::TurnStatus;
|
|
use codex_app_server_protocol::UserInput;
|
|
use pretty_assertions::assert_eq;
|
|
use tempfile::TempDir;
|
|
use tokio::time::Duration;
|
|
use tokio::time::timeout;
|
|
|
|
const READ_TIMEOUT: Duration = Duration::from_secs(30);
|
|
|
|
#[tokio::test]
|
|
async fn named_empty_paginated_thread_resumes_after_restart() -> Result<()> {
|
|
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
|
let codex_home = TempDir::new()?;
|
|
MockResponsesConfig::new(&server.uri()).write(codex_home.path())?;
|
|
let mut app_server = TestAppServer::builder()
|
|
.with_codex_home(codex_home.path())
|
|
.build_initialized()
|
|
.await?;
|
|
let start_id = app_server
|
|
.send_thread_start_request_with_auto_env(ThreadStartParams {
|
|
history_mode: Some(ThreadHistoryMode::Paginated),
|
|
..Default::default()
|
|
})
|
|
.await?;
|
|
let ThreadStartResponse { thread, .. } =
|
|
timeout(READ_TIMEOUT, app_server.read_response(start_id)).await??;
|
|
let thread_id = thread.id;
|
|
let name = "Scheduled task";
|
|
let set_name_id = app_server
|
|
.send_thread_set_name_request(ThreadSetNameParams {
|
|
thread_id: thread_id.clone(),
|
|
name: name.to_string(),
|
|
})
|
|
.await?;
|
|
let _: ThreadSetNameResponse =
|
|
timeout(READ_TIMEOUT, app_server.read_response(set_name_id)).await??;
|
|
|
|
// Run-now resumes immediately after naming, before any user turn exists.
|
|
let resume_id = app_server
|
|
.send_thread_resume_request(ThreadResumeParams {
|
|
thread_id: thread_id.clone(),
|
|
exclude_turns: true,
|
|
..Default::default()
|
|
})
|
|
.await?;
|
|
let ThreadResumeResponse { thread, .. } =
|
|
timeout(READ_TIMEOUT, app_server.read_response(resume_id)).await??;
|
|
assert_eq!(thread.id, thread_id);
|
|
assert_eq!(thread.name.as_deref(), Some(name));
|
|
assert!(
|
|
timeout(READ_TIMEOUT, app_server.shutdown_gracefully())
|
|
.await??
|
|
.success()
|
|
);
|
|
drop(app_server);
|
|
|
|
let mut app_server = TestAppServer::builder()
|
|
.with_codex_home(codex_home.path())
|
|
.build_initialized()
|
|
.await?;
|
|
let resume_id = app_server
|
|
.send_thread_resume_request(ThreadResumeParams {
|
|
thread_id: thread_id.clone(),
|
|
exclude_turns: true,
|
|
..Default::default()
|
|
})
|
|
.await?;
|
|
let ThreadResumeResponse { thread, .. } =
|
|
timeout(READ_TIMEOUT, app_server.read_response(resume_id)).await??;
|
|
assert_eq!(thread.id, thread_id);
|
|
assert_eq!(thread.name.as_deref(), Some(name));
|
|
|
|
let input = vec![UserInput::Text {
|
|
text: "Scheduled run".to_string(),
|
|
text_elements: Vec::new(),
|
|
}];
|
|
let completed = timeout(
|
|
READ_TIMEOUT,
|
|
app_server.start_turn_and_wait_for_completion(TurnStartParams {
|
|
thread_id: thread_id.clone(),
|
|
input,
|
|
..Default::default()
|
|
}),
|
|
)
|
|
.await??;
|
|
assert_eq!(completed.thread_id, thread_id);
|
|
assert_eq!(completed.turn.status, TurnStatus::Completed);
|
|
assert_eq!(completed.turn.error, None);
|
|
Ok(())
|
|
}
|