scroll.server/crates/service-rpc/tests/error_channel.rs
WiseDev 5c35bef330 send service errors back over rpc instead of dropping the connection
the call outcome now rides the frame as Result<Response, WireError>, so
a rejection reaches the caller with its message and the pooled
connection stays up. only a real transport failure closes it now; a
malformed request comes back as a rejection too.
2026-08-28 23:43:47 +03:00

43 lines
1.3 KiB
Rust

use std::sync::Arc;
use service_rpc::{serve, RpcClient, RpcError, RpcResult, RpcService};
use tokio::net::TcpListener;
struct Doubler;
#[async_trait::async_trait]
impl RpcService for Doubler {
type Request = i32;
type Response = i32;
fn service_name(&self) -> &'static str {
"doubler"
}
async fn call(&self, request: i32) -> RpcResult<i32> {
if request == 0 {
return Err(RpcError::Rejected("zero is not allowed".to_owned()));
}
Ok(request * 2)
}
}
#[tokio::test]
async fn a_service_error_reaches_the_caller_and_keeps_the_connection() {
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let endpoint = listener.local_addr().expect("addr").to_string();
tokio::spawn(async move {
let _ = serve(listener, Arc::new(Doubler)).await;
});
let client: RpcClient<i32, i32> = RpcClient::with_pool_size(endpoint, 1);
assert_eq!(client.call(&5).await.expect("ok call"), 10);
match client.call(&0).await {
Err(RpcError::Rejected(message)) => assert_eq!(message, "zero is not allowed"),
other => panic!("expected a rejection, got {other:?}"),
}
assert_eq!(
client.call(&7).await.expect("connection survived the error"),
14
);
}