Split timer and queued message flags

This commit is contained in:
Eric Traut
2026-04-10 14:43:56 -07:00
parent 2a6526c9fd
commit cbe466866c
11 changed files with 350 additions and 98 deletions

View File

@@ -12,12 +12,15 @@ use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::mount_sse_once;
use core_test_support::responses::mount_sse_sequence;
use core_test_support::responses::sse;
use core_test_support::responses::start_mock_server;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
use core_test_support::wait_for_event_match;
use core_test_support::wait_for_event_with_timeout;
use pretty_assertions::assert_eq;
use std::time::Duration;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn create_timer_emits_fired_background_event_when_timer_starts() -> Result<()> {
@@ -44,7 +47,7 @@ async fn assert_after_turn_timer_starts_and_emits_fired_event() -> Result<()> {
let mut builder = test_codex().with_config(|config| {
config
.features
.enable(Feature::TimerScheduler)
.enable(Feature::Timers)
.unwrap_or_else(|err| panic!("test config should allow feature update: {err}"));
config
.features
@@ -107,7 +110,7 @@ async fn create_timer_persists_source_and_client_metadata() -> Result<()> {
let mut builder = test_codex().with_config(|config| {
config
.features
.enable(Feature::TimerScheduler)
.enable(Feature::Timers)
.unwrap_or_else(|err| panic!("test config should allow feature update: {err}"));
config
.features
@@ -153,7 +156,7 @@ async fn create_timer_lazily_opens_sqlite_for_ephemeral_thread() -> Result<()> {
config.ephemeral = true;
config
.features
.enable(Feature::TimerScheduler)
.enable(Feature::Timers)
.unwrap_or_else(|err| panic!("test config should allow feature update: {err}"));
});
let test = builder.build(&server).await?;
@@ -190,7 +193,7 @@ async fn list_timers_discovers_externally_inserted_timer() -> Result<()> {
let mut builder = test_codex().with_config(|config| {
config
.features
.enable(Feature::TimerScheduler)
.enable(Feature::Timers)
.unwrap_or_else(|err| panic!("test config should allow feature update: {err}"));
config
.features
@@ -236,3 +239,119 @@ async fn list_timers_discovers_externally_inserted_timer() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn queued_messages_feature_consumes_messages_without_timers() -> Result<()> {
let server = start_mock_server().await;
let mock = mount_sse_sequence(
&server,
vec![
sse(vec![
ev_response_created("resp-1"),
ev_assistant_message("msg-1", "first turn"),
ev_completed("resp-1"),
]),
sse(vec![
ev_response_created("resp-2"),
ev_assistant_message("msg-2", "queued turn"),
ev_completed("resp-2"),
]),
],
)
.await;
let mut builder = test_codex().with_config(|config| {
config
.features
.enable(Feature::QueuedMessages)
.unwrap_or_else(|err| panic!("test config should allow feature update: {err}"));
config
.features
.enable(Feature::Sqlite)
.unwrap_or_else(|err| panic!("test config should allow feature update: {err}"));
});
let test = builder.build(&server).await?;
let db = test.codex.state_db().expect("state db enabled");
let thread_id = test.session_configured.session_id.to_string();
db.create_thread_message(&codex_state::ThreadMessageCreateParams::new(
thread_id,
"external".to_string(),
"queued hello".to_string(),
None,
"{}".to_string(),
TimerDelivery::AfterTurn.as_str().to_string(),
Utc::now().timestamp(),
))
.await?;
test.submit_turn("start").await?;
wait_for_event_with_timeout(
&test.codex,
|event| matches!(event, EventMsg::InjectedMessage(_)),
Duration::from_secs(20),
)
.await;
wait_for_event_with_timeout(
&test.codex,
|event| matches!(event, EventMsg::TurnComplete(_)),
Duration::from_secs(20),
)
.await;
let requests = mock.requests();
assert_eq!(requests.len(), 2);
assert!(
requests[1]
.message_input_texts("user")
.iter()
.any(|message| message.contains("<content>\nqueued hello\n</content>"))
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn queued_messages_feature_disabled_leaves_messages_queued() -> Result<()> {
let server = start_mock_server().await;
let mock = mount_sse_once(
&server,
sse(vec![
ev_response_created("resp-1"),
ev_assistant_message("msg-1", "first turn"),
ev_completed("resp-1"),
]),
)
.await;
let mut builder = test_codex().with_config(|config| {
config
.features
.enable(Feature::Sqlite)
.unwrap_or_else(|err| panic!("test config should allow feature update: {err}"));
});
let test = builder.build(&server).await?;
let db = test.codex.state_db().expect("state db enabled");
let thread_id = test.session_configured.session_id.to_string();
db.create_thread_message(&codex_state::ThreadMessageCreateParams::new(
thread_id.clone(),
"external".to_string(),
"queued hello".to_string(),
None,
"{}".to_string(),
TimerDelivery::AfterTurn.as_str().to_string(),
Utc::now().timestamp(),
))
.await?;
test.submit_turn("start").await?;
tokio::time::sleep(Duration::from_millis(100)).await;
assert_eq!(mock.requests().len(), 1);
assert!(
db.claim_next_thread_message(&thread_id, true, true)
.await?
.is_some()
);
Ok(())
}