diff --git a/codex-rs/app-server-transport/src/transport/remote_control/pairing_integration_tests.rs b/codex-rs/app-server-transport/src/transport/remote_control/pairing_integration_tests.rs index cc65be5bbf..532cabedb4 100644 --- a/codex-rs/app-server-transport/src/transport/remote_control/pairing_integration_tests.rs +++ b/codex-rs/app-server-transport/src/transport/remote_control/pairing_integration_tests.rs @@ -735,3 +735,77 @@ async fn remote_control_auth_change_cancels_connected_refresh() { shutdown_token.cancel(); let _ = remote_task.await; } + +#[tokio::test] +async fn remote_control_connected_refresh_404_reenrolls() { + let listener = TcpListener::bind("127.0.0.1:0") + .await + .expect("listener should bind"); + let remote_control_url = remote_control_url_for_listener(&listener); + let codex_home = TempDir::new().expect("temp dir should create"); + let (transport_event_tx, _transport_event_rx) = + mpsc::channel::(CHANNEL_CAPACITY); + let shutdown_token = CancellationToken::new(); + let (remote_task, remote_handle) = start_remote_control( + RemoteControlStartConfig { + remote_control_url, + installation_id: TEST_INSTALLATION_ID.to_string(), + }, + Some(remote_control_state_runtime(&codex_home).await), + remote_control_auth_manager(), + transport_event_tx, + shutdown_token.clone(), + /*app_server_client_name_rx*/ None, + /*initial_enabled*/ true, + ) + .await + .expect("remote control should start"); + + let enroll_request = accept_http_request(&listener).await; + respond_with_json( + enroll_request.stream, + remote_control_server_token_response( + "srv_e_initial", + "env_initial", + TEST_REMOTE_CONTROL_SERVER_TOKEN, + ), + ) + .await; + let mut first_websocket = accept_remote_control_connection(&listener).await; + + remote_handle.request_pairing_auth_refresh(); + let refresh_request = accept_http_request(&listener).await; + assert_eq!( + refresh_request.request_line, + "POST /backend-api/wham/remote/control/server/refresh HTTP/1.1" + ); + respond_with_status(refresh_request.stream, "404 Not Found", "").await; + expect_remote_control_connection_closed( + &mut first_websocket, + "stale enrollment refresh should close the websocket", + ) + .await; + + let enroll_request = accept_http_request(&listener).await; + assert_eq!( + enroll_request.request_line, + "POST /backend-api/wham/remote/control/server/enroll HTTP/1.1" + ); + respond_with_json( + enroll_request.stream, + remote_control_server_token_response( + "srv_e_next", + "env_next", + TEST_REFRESHED_REMOTE_CONTROL_SERVER_TOKEN, + ), + ) + .await; + let mut second_websocket = accept_remote_control_connection(&listener).await; + second_websocket + .close(None) + .await + .expect("second websocket should close"); + + shutdown_token.cancel(); + let _ = remote_task.await; +}