@@ -9,6 +9,8 @@ use codex_protocol::protocol::Op;
99use codex_protocol:: protocol:: RealtimeAudioFrame ;
1010use codex_protocol:: protocol:: RealtimeConversationRealtimeEvent ;
1111use codex_protocol:: protocol:: RealtimeEvent ;
12+ use core_test_support:: responses;
13+ use core_test_support:: responses:: start_mock_server;
1214use core_test_support:: responses:: start_websocket_server;
1315use core_test_support:: skip_if_no_network;
1416use core_test_support:: test_codex:: test_codex;
@@ -459,3 +461,82 @@ async fn conversation_uses_experimental_realtime_ws_backend_prompt_override() ->
459461 server. shutdown ( ) . await ;
460462 Ok ( ( ) )
461463}
464+
465+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
466+ async fn conversation_mirrors_assistant_message_text_to_realtime_websocket ( ) -> Result < ( ) > {
467+ skip_if_no_network ! ( Ok ( ( ) ) ) ;
468+
469+ let api_server = start_mock_server ( ) . await ;
470+ let _response_mock = responses:: mount_sse_once (
471+ & api_server,
472+ responses:: sse ( vec ! [
473+ responses:: ev_response_created( "resp_1" ) ,
474+ responses:: ev_assistant_message( "msg_1" , "assistant says hi" ) ,
475+ responses:: ev_completed( "resp_1" ) ,
476+ ] ) ,
477+ )
478+ . await ;
479+
480+ let realtime_server = start_websocket_server ( vec ! [ vec![
481+ vec![ json!( {
482+ "type" : "session.created" ,
483+ "session" : { "id" : "sess_1" }
484+ } ) ] ,
485+ vec![ ] ,
486+ ] ] )
487+ . await ;
488+
489+ let mut builder = test_codex ( ) . with_config ( {
490+ let realtime_base_url = realtime_server. uri ( ) . to_string ( ) ;
491+ move |config| {
492+ config. experimental_realtime_ws_base_url = Some ( realtime_base_url) ;
493+ }
494+ } ) ;
495+ let test = builder. build ( & api_server) . await ?;
496+
497+ test. codex
498+ . submit ( Op :: RealtimeConversationStart ( ConversationStartParams {
499+ prompt : "backend prompt" . to_string ( ) ,
500+ session_id : None ,
501+ } ) )
502+ . await ?;
503+
504+ let session_created = wait_for_event_match ( & test. codex , |msg| match msg {
505+ EventMsg :: RealtimeConversationRealtime ( RealtimeConversationRealtimeEvent {
506+ payload : RealtimeEvent :: SessionCreated { session_id } ,
507+ } ) => Some ( session_id. clone ( ) ) ,
508+ _ => None ,
509+ } )
510+ . await ;
511+ assert_eq ! ( session_created, "sess_1" ) ;
512+
513+ test. submit_turn ( "hello" ) . await ?;
514+
515+ let deadline = tokio:: time:: Instant :: now ( ) + Duration :: from_secs ( 2 ) ;
516+ while tokio:: time:: Instant :: now ( ) < deadline {
517+ let connections = realtime_server. connections ( ) ;
518+ if connections. len ( ) == 1 && connections[ 0 ] . len ( ) >= 2 {
519+ break ;
520+ }
521+ tokio:: time:: sleep ( Duration :: from_millis ( 10 ) ) . await ;
522+ }
523+
524+ let realtime_connections = realtime_server. connections ( ) ;
525+ assert_eq ! ( realtime_connections. len( ) , 1 ) ;
526+ assert_eq ! ( realtime_connections[ 0 ] . len( ) , 2 ) ;
527+ assert_eq ! (
528+ realtime_connections[ 0 ] [ 0 ] . body_json( ) [ "type" ] . as_str( ) ,
529+ Some ( "session.create" )
530+ ) ;
531+ assert_eq ! (
532+ realtime_connections[ 0 ] [ 1 ] . body_json( ) [ "type" ] . as_str( ) ,
533+ Some ( "conversation.item.create" )
534+ ) ;
535+ assert_eq ! (
536+ realtime_connections[ 0 ] [ 1 ] . body_json( ) [ "item" ] [ "content" ] [ 0 ] [ "text" ] . as_str( ) ,
537+ Some ( "assistant says hi" )
538+ ) ;
539+
540+ realtime_server. shutdown ( ) . await ;
541+ Ok ( ( ) )
542+ }
0 commit comments