use std::time::Duration;
use vox::memory_link_pair;
#[vox::service]
trait Echo {
async fn echo(&self, value: u32) -> u32;
}
#[derive(Clone)]
struct EchoService;
impl Echo for EchoService {
async fn echo(&self, value: u32) -> u32 {
value
}
}
async fn echo_pair() -> (EchoClient, vox::NoopClient) {
let (client_link, server_link) = memory_link_pair(16);
let server = tokio::spawn(async move {
vox::acceptor_on(server_link)
.on_connection(EchoDispatcher::new(EchoService))
.establish::<vox::NoopClient>()
.await
.expect("server establish")
});
let client = vox::initiator_on(client_link, vox::TransportMode::Bare)
.establish::<EchoClient>()
.await
.expect("client establish");
let server_guard = server.await.expect("server task");
(client, server_guard)
}
#[tokio::test]
async fn basic_echo_call() {
let (client, _server) = echo_pair().await;
let result = client.echo(42).await.expect("echo call");
assert_eq!(result, 42);
}
#[tokio::test]
async fn multiple_sequential_calls() {
let (client, _server) = echo_pair().await;
for i in 0..10 {
let result = client.echo(i).await.expect("echo call");
assert_eq!(result, i);
}
}
#[tokio::test]
async fn dropping_one_client_clone_keeps_session_alive() {
let (client_link, server_link) = memory_link_pair(16);
let (server_session_tx, server_session_rx) =
tokio::sync::oneshot::channel::<tokio::task::JoinHandle<()>>();
let (client_session_tx, client_session_rx) =
tokio::sync::oneshot::channel::<tokio::task::JoinHandle<()>>();
let server_task = tokio::spawn(async move {
vox::acceptor_on(server_link)
.spawn_fn(move |fut| {
let handle = tokio::spawn(fut);
let _ = server_session_tx.send(handle);
})
.on_connection(EchoDispatcher::new(EchoService))
.establish::<vox::NoopClient>()
.await
.expect("server establish")
});
let client = vox::initiator_on(client_link, vox::TransportMode::Bare)
.spawn_fn(move |fut| {
let handle = tokio::spawn(fut);
let _ = client_session_tx.send(handle);
})
.establish::<EchoClient>()
.await
.expect("client establish");
let server_guard = server_task.await.expect("server task");
let client_session = client_session_rx.await.expect("client session");
let server_session = server_session_rx.await.expect("server session");
let client_clone = client.clone();
drop(client_clone);
let result = client.echo(42).await.expect("call after clone drop");
assert_eq!(result, 42);
drop(client);
drop(server_guard);
tokio::time::timeout(Duration::from_millis(500), client_session)
.await
.expect("client session exit timeout")
.expect("client session failed");
tokio::time::timeout(Duration::from_millis(500), server_session)
.await
.expect("server session exit timeout")
.expect("server session failed");
}
#[tokio::test]
async fn dropping_root_caller_shuts_down_session() {
let (client_link, server_link) = memory_link_pair(16);
let (server_session_tx, server_session_rx) =
tokio::sync::oneshot::channel::<tokio::task::JoinHandle<()>>();
let (client_session_tx, client_session_rx) =
tokio::sync::oneshot::channel::<tokio::task::JoinHandle<()>>();
let server_task = tokio::spawn(async move {
vox::acceptor_on(server_link)
.spawn_fn(move |fut| {
let handle = tokio::spawn(fut);
let _ = server_session_tx.send(handle);
})
.on_connection(EchoDispatcher::new(EchoService))
.establish::<vox::NoopClient>()
.await
.expect("server establish")
});
let client = vox::initiator_on(client_link, vox::TransportMode::Bare)
.spawn_fn(move |fut| {
let handle = tokio::spawn(fut);
let _ = client_session_tx.send(handle);
})
.establish::<EchoClient>()
.await
.expect("client establish");
let server_guard = server_task.await.expect("server task");
let client_session = client_session_rx.await.expect("client session");
let server_session = server_session_rx.await.expect("server session");
drop(client);
drop(server_guard);
tokio::time::timeout(Duration::from_millis(500), client_session)
.await
.expect("client session exit timeout")
.expect("client session failed");
tokio::time::timeout(Duration::from_millis(500), server_session)
.await
.expect("server session exit timeout")
.expect("server session failed");
}
#[tokio::test]
async fn echo_call_across_memory_link() {
let (client, _server) = echo_pair().await;
let result = client.echo(42).await.expect("echo across memory link");
assert_eq!(result, 42);
}
#[tokio::test]
async fn in_flight_call_returns_error_when_peer_closes() {
let (client_link, server_link) = memory_link_pair(16);
let server = tokio::spawn(async move {
let server = vox::acceptor_on(server_link)
.on_connection(EchoDispatcher::new(EchoService))
.establish::<vox::NoopClient>()
.await
.expect("server establish");
tokio::time::sleep(Duration::from_millis(50)).await;
drop(server);
});
let client = vox::initiator_on(client_link, vox::TransportMode::Bare)
.establish::<EchoClient>()
.await
.expect("client establish");
server.await.expect("server task");
tokio::time::sleep(Duration::from_millis(50)).await;
let result = client.echo(123).await;
assert!(result.is_err(), "call should fail after peer closes");
}