mirror of
https://github.com/openai/codex.git
synced 2026-11-06 20:20:34 +00:00
## What changed - Add `CloudClient` and integrate it with `AppServerClient` and `AppServerRequestHandle` for requests, notifications, server-request replies, events, and shutdown. - Keep retained request and reply handles bound to their cloud backend, with event delivery independent of request completion and a timeout for shutdown. - Return no platform or Codex home metadata for cloud clients, and reject TUI filesystem operations for this backend. ## Testing Add tests for backend ownership of retained handles, independent event delivery, errors after the command receiver closes, and shutdown timeouts when the worker does not respond. GitOrigin-RevId: 4cf308dc0ea6fa6e7b14dd4542d93e8030b0162d
105 lines
3.6 KiB
Rust
105 lines
3.6 KiB
Rust
use super::*;
|
|
use crate::AppServerClient;
|
|
use crate::AppServerEvent;
|
|
use codex_app_server_protocol::ThreadReadParams;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[tokio::test]
|
|
async fn retained_request_and_reply_stay_with_their_cloud_backend() {
|
|
let (first, mut first_commands, first_events) = CloudClient::channel(/*capacity*/ 1);
|
|
let (second, mut second_commands, _second_events) = CloudClient::channel(/*capacity*/ 1);
|
|
let mut first = AppServerClient::Cloud(first);
|
|
let second = AppServerClient::Cloud(second);
|
|
let handle = first.request_handle();
|
|
let reply = handle.server_request_reply_handle(RequestId::Integer(7));
|
|
let worker = tokio::spawn(async move {
|
|
let command = first_commands.recv().await.unwrap();
|
|
let CloudOperation::Request(request) = command.operation else {
|
|
panic!("expected thread read");
|
|
};
|
|
let ClientRequest::ThreadRead { params, .. } = *request else {
|
|
panic!("expected thread read");
|
|
};
|
|
assert_eq!(params.thread_id, "thread-1");
|
|
first_events
|
|
.send(AppServerEvent::Disconnected {
|
|
message: "stream ended".into(),
|
|
})
|
|
.unwrap();
|
|
command
|
|
.response
|
|
.send(Ok(Ok(serde_json::json!({"owner": "first"}))))
|
|
.unwrap();
|
|
let command = first_commands.recv().await.unwrap();
|
|
let CloudOperation::Resolve { request_id, result } = command.operation else {
|
|
panic!("expected retained reply");
|
|
};
|
|
assert_eq!(
|
|
(request_id, result),
|
|
(
|
|
RequestId::Integer(7),
|
|
serde_json::json!({"decision": "accept"})
|
|
)
|
|
);
|
|
command
|
|
.response
|
|
.send(Ok(Ok(serde_json::Value::Null)))
|
|
.unwrap();
|
|
});
|
|
assert_eq!(
|
|
handle
|
|
.request(ClientRequest::ThreadRead {
|
|
request_id: RequestId::Integer(1),
|
|
params: ThreadReadParams {
|
|
thread_id: "thread-1".into(),
|
|
include_turns: false
|
|
},
|
|
})
|
|
.await
|
|
.unwrap()
|
|
.unwrap(),
|
|
serde_json::json!({"owner": "first"})
|
|
);
|
|
reply
|
|
.resolve(serde_json::json!({"decision": "accept"}))
|
|
.await
|
|
.unwrap();
|
|
assert!(second_commands.try_recv().is_err());
|
|
assert!(matches!(
|
|
first.next_event().await,
|
|
Some(AppServerEvent::Disconnected { .. })
|
|
));
|
|
worker.await.unwrap();
|
|
assert!(
|
|
handle
|
|
.request(ClientRequest::ThreadRead {
|
|
request_id: RequestId::Integer(2),
|
|
params: ThreadReadParams {
|
|
thread_id: "thread-1".into(),
|
|
include_turns: false
|
|
},
|
|
})
|
|
.await
|
|
.is_err()
|
|
);
|
|
drop(second);
|
|
}
|
|
|
|
#[tokio::test(start_paused = true)]
|
|
async fn cloud_shutdown_times_out_when_owner_does_not_respond() {
|
|
let (client, mut commands, events) = CloudClient::channel(/*capacity*/ 1);
|
|
let error = tokio::time::timeout(
|
|
crate::SHUTDOWN_TIMEOUT + std::time::Duration::from_secs(1),
|
|
AppServerClient::Cloud(client).shutdown(),
|
|
)
|
|
.await
|
|
.expect("shutdown should be bounded")
|
|
.expect_err("an unresponsive cloud worker should time out");
|
|
assert_eq!(error.kind(), io::ErrorKind::TimedOut);
|
|
let command = commands.try_recv().expect("shutdown was admitted");
|
|
assert!(matches!(command.operation, CloudOperation::Shutdown));
|
|
assert!(command.response.is_closed());
|
|
assert!(events.is_closed());
|
|
assert!(commands.recv().await.is_none());
|
|
}
|