mirror of
https://github.com/openai/codex.git
synced 2026-04-26 15:45:02 +00:00
app-server: Emit thread/name/updated event globally (#13674)
This commit is contained in:
committed by
GitHub
parent
3449e00bc9
commit
51fcdc760d
171
codex-rs/app-server/tests/suite/v2/thread_name_websocket.rs
Normal file
171
codex-rs/app-server/tests/suite/v2/thread_name_websocket.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use super::connection_handling_websocket::DEFAULT_READ_TIMEOUT;
|
||||
use super::connection_handling_websocket::WsClient;
|
||||
use super::connection_handling_websocket::assert_no_message;
|
||||
use super::connection_handling_websocket::connect_websocket;
|
||||
use super::connection_handling_websocket::create_config_toml;
|
||||
use super::connection_handling_websocket::read_notification_for_method;
|
||||
use super::connection_handling_websocket::read_response_and_notification_for_method;
|
||||
use super::connection_handling_websocket::read_response_for_id;
|
||||
use super::connection_handling_websocket::reserve_local_addr;
|
||||
use super::connection_handling_websocket::send_initialize_request;
|
||||
use super::connection_handling_websocket::send_request;
|
||||
use super::connection_handling_websocket::spawn_websocket_server;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use app_test_support::create_fake_rollout_with_text_elements;
|
||||
use app_test_support::create_mock_responses_server_repeating_assistant;
|
||||
use app_test_support::to_response;
|
||||
use codex_app_server_protocol::JSONRPCNotification;
|
||||
use codex_app_server_protocol::JSONRPCResponse;
|
||||
use codex_app_server_protocol::ThreadNameUpdatedNotification;
|
||||
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 pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
use tokio::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_name_updated_broadcasts_for_loaded_threads() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml(codex_home.path(), &server.uri(), "never")?;
|
||||
let conversation_id = create_rollout(codex_home.path(), "2025-01-05T12-00-00")?;
|
||||
|
||||
let bind_addr = reserve_local_addr()?;
|
||||
let mut process = spawn_websocket_server(codex_home.path(), bind_addr).await?;
|
||||
|
||||
let result = async {
|
||||
let mut ws1 = connect_websocket(bind_addr).await?;
|
||||
let mut ws2 = connect_websocket(bind_addr).await?;
|
||||
initialize_both_clients(&mut ws1, &mut ws2).await?;
|
||||
|
||||
send_request(
|
||||
&mut ws1,
|
||||
"thread/resume",
|
||||
10,
|
||||
Some(serde_json::to_value(ThreadResumeParams {
|
||||
thread_id: conversation_id.clone(),
|
||||
..Default::default()
|
||||
})?),
|
||||
)
|
||||
.await?;
|
||||
let resume_resp: JSONRPCResponse = read_response_for_id(&mut ws1, 10).await?;
|
||||
let resume: ThreadResumeResponse = to_response::<ThreadResumeResponse>(resume_resp)?;
|
||||
assert_eq!(resume.thread.id, conversation_id);
|
||||
|
||||
let renamed = "Loaded rename";
|
||||
send_request(
|
||||
&mut ws1,
|
||||
"thread/name/set",
|
||||
11,
|
||||
Some(serde_json::to_value(ThreadSetNameParams {
|
||||
thread_id: conversation_id.clone(),
|
||||
name: renamed.to_string(),
|
||||
})?),
|
||||
)
|
||||
.await?;
|
||||
let (rename_resp, ws1_notification) =
|
||||
read_response_and_notification_for_method(&mut ws1, 11, "thread/name/updated").await?;
|
||||
let _: ThreadSetNameResponse = to_response::<ThreadSetNameResponse>(rename_resp)?;
|
||||
assert_thread_name_updated(ws1_notification, &conversation_id, renamed)?;
|
||||
|
||||
let ws2_notification =
|
||||
read_notification_for_method(&mut ws2, "thread/name/updated").await?;
|
||||
assert_thread_name_updated(ws2_notification, &conversation_id, renamed)?;
|
||||
|
||||
assert_no_message(&mut ws1, Duration::from_millis(250)).await?;
|
||||
assert_no_message(&mut ws2, Duration::from_millis(250)).await?;
|
||||
Ok(())
|
||||
}
|
||||
.await;
|
||||
|
||||
process
|
||||
.kill()
|
||||
.await
|
||||
.context("failed to stop websocket app-server process")?;
|
||||
result
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_name_updated_broadcasts_for_not_loaded_threads() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml(codex_home.path(), &server.uri(), "never")?;
|
||||
let conversation_id = create_rollout(codex_home.path(), "2025-01-05T12-05-00")?;
|
||||
|
||||
let bind_addr = reserve_local_addr()?;
|
||||
let mut process = spawn_websocket_server(codex_home.path(), bind_addr).await?;
|
||||
|
||||
let result = async {
|
||||
let mut ws1 = connect_websocket(bind_addr).await?;
|
||||
let mut ws2 = connect_websocket(bind_addr).await?;
|
||||
initialize_both_clients(&mut ws1, &mut ws2).await?;
|
||||
|
||||
let renamed = "Stored rename";
|
||||
send_request(
|
||||
&mut ws1,
|
||||
"thread/name/set",
|
||||
20,
|
||||
Some(serde_json::to_value(ThreadSetNameParams {
|
||||
thread_id: conversation_id.clone(),
|
||||
name: renamed.to_string(),
|
||||
})?),
|
||||
)
|
||||
.await?;
|
||||
let (rename_resp, ws1_notification) =
|
||||
read_response_and_notification_for_method(&mut ws1, 20, "thread/name/updated").await?;
|
||||
let _: ThreadSetNameResponse = to_response::<ThreadSetNameResponse>(rename_resp)?;
|
||||
assert_thread_name_updated(ws1_notification, &conversation_id, renamed)?;
|
||||
|
||||
let ws2_notification =
|
||||
read_notification_for_method(&mut ws2, "thread/name/updated").await?;
|
||||
assert_thread_name_updated(ws2_notification, &conversation_id, renamed)?;
|
||||
|
||||
assert_no_message(&mut ws1, Duration::from_millis(250)).await?;
|
||||
assert_no_message(&mut ws2, Duration::from_millis(250)).await?;
|
||||
Ok(())
|
||||
}
|
||||
.await;
|
||||
|
||||
process
|
||||
.kill()
|
||||
.await
|
||||
.context("failed to stop websocket app-server process")?;
|
||||
result
|
||||
}
|
||||
|
||||
async fn initialize_both_clients(ws1: &mut WsClient, ws2: &mut WsClient) -> Result<()> {
|
||||
send_initialize_request(ws1, 1, "ws_client_one").await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, read_response_for_id(ws1, 1)).await??;
|
||||
|
||||
send_initialize_request(ws2, 2, "ws_client_two").await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, read_response_for_id(ws2, 2)).await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_rollout(codex_home: &std::path::Path, filename_ts: &str) -> Result<String> {
|
||||
create_fake_rollout_with_text_elements(
|
||||
codex_home,
|
||||
filename_ts,
|
||||
"2025-01-05T12:00:00Z",
|
||||
"Saved user message",
|
||||
Vec::new(),
|
||||
Some("mock_provider"),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn assert_thread_name_updated(
|
||||
notification: JSONRPCNotification,
|
||||
thread_id: &str,
|
||||
thread_name: &str,
|
||||
) -> Result<()> {
|
||||
let notification: ThreadNameUpdatedNotification =
|
||||
serde_json::from_value(notification.params.context("thread/name/updated params")?)?;
|
||||
assert_eq!(notification.thread_id, thread_id);
|
||||
assert_eq!(notification.thread_name.as_deref(), Some(thread_name));
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user