use std::sync::Arc;
#[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
}
}
#[vox::service]
trait Ping {
async fn ping(&self, value: u32) -> u32;
}
#[derive(Clone)]
struct PingService;
impl Ping for PingService {
async fn ping(&self, value: u32) -> u32 {
value
}
}
#[tokio::test]
async fn serve_and_connect() {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind");
let addr = listener.local_addr().expect("local_addr");
let server = tokio::spawn(async move {
vox::serve_listener(listener, EchoDispatcher::new(EchoService))
.await
.expect("serve");
});
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
let client: EchoClient = vox::connect(format!("tcp://{addr}"))
.await
.expect("connect");
let result = client.echo(42).await.expect("echo");
assert_eq!(result, 42);
server.abort();
}
#[tokio::test]
async fn connect_builder_establish_matches_await() {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind");
let addr = listener.local_addr().expect("local_addr");
let server = tokio::spawn(async move {
vox::serve_listener(listener, EchoDispatcher::new(EchoService))
.await
.expect("serve");
});
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
let client = vox::connect::<EchoClient>(format!("tcp://{addr}"))
.establish()
.await
.expect("connect");
let result = client.echo(77).await.expect("echo");
assert_eq!(result, 77);
server.abort();
}
#[tokio::test]
async fn connect_builder_can_configure_inbound_virtual_connections_before_await() {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind");
let addr = listener.local_addr().expect("local_addr");
let accepted = Arc::new(tokio::sync::Notify::new());
let accepted_server = accepted.clone();
let server = tokio::spawn(async move {
let (socket, _) = listener.accept().await.expect("accept");
let root = vox::acceptor_on(vox::transport::tcp::StreamLink::tcp(socket))
.on_connection(PingDispatcher::new(PingService))
.establish::<vox::NoopClient>()
.await
.expect("server establish");
let session = root.session.clone().expect("server session");
let client: EchoClient = session
.open(vox::ConnectionSettings {
parity: vox::Parity::Odd,
max_concurrent_requests: 64,
})
.await
.expect("open echo client");
let echoed = client.echo(41).await.expect("echo");
assert_eq!(echoed, 41);
accepted_server.notify_one();
root
});
let client: PingClient = vox::connect(format!("tcp://{addr}"))
.on_connection(EchoDispatcher::new(EchoService))
.await
.expect("connect");
let pinged = client.ping(9).await.expect("ping");
assert_eq!(pinged, 9);
tokio::time::timeout(std::time::Duration::from_secs(1), accepted.notified())
.await
.expect("server never used inbound virtual connection");
let _server_root = server.await.expect("server task");
}
#[cfg(unix)]
#[tokio::test]
async fn serve_local_with_lock() {
let dir = tempfile::tempdir().expect("tempdir");
let sock_path = dir.path().join("echo.sock");
let addr = format!("local://{}", sock_path.display());
let server = {
let addr = addr.clone();
tokio::spawn(async move {
vox::serve(&addr, EchoDispatcher::new(EchoService))
.await
.expect("serve");
})
};
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
let client: EchoClient = vox::connect(&addr).await.expect("connect");
let result = client.echo(99).await.expect("echo");
assert_eq!(result, 99);
let err = vox_stream::LocalLinkAcceptor::bind_with_lock(sock_path.to_str().unwrap())
.err()
.expect("second bind should fail");
assert_eq!(err.kind(), std::io::ErrorKind::AddrInUse);
server.abort();
}
#[cfg(feature = "transport-websocket")]
#[tokio::test]
async fn serve_websocket() {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind");
let addr = listener.local_addr().expect("local_addr");
let server = tokio::spawn(async move {
vox::serve_listener(
vox::WsListener::from_tcp(listener),
EchoDispatcher::new(EchoService),
)
.await
.expect("serve");
});
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
let client: EchoClient = vox::connect(format!("ws://{addr}")).await.expect("connect");
let result = client.echo(7).await.expect("echo");
assert_eq!(result, 7);
server.abort();
}
#[cfg(feature = "transport-websocket")]
#[tokio::test]
async fn serve_websocket_string() {
let server = tokio::spawn(async move {
vox::serve("ws://127.0.0.1:0", EchoDispatcher::new(EchoService))
.await
.expect("serve");
});
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
server.abort();
}
#[cfg(unix)]
#[tokio::test]
async fn serve_local_lock_released_after_drop() {
let dir = tempfile::tempdir().expect("tempdir");
let sock_path = dir.path().join("echo.sock");
let path_str = sock_path.to_str().unwrap();
let (acceptor, lock) =
vox_stream::LocalLinkAcceptor::bind_with_lock(path_str).expect("first bind");
drop(acceptor);
drop(lock);
let (_acceptor2, _lock2) = vox_stream::LocalLinkAcceptor::bind_with_lock(path_str)
.expect("second bind after lock release");
}