use std::time::{Duration, Instant};
use zccache::ipc::{connect, unique_test_endpoint, IpcError, IpcListener};
use zccache::protocol::{Request, Response};
#[tokio::test]
async fn recv_unbounded_by_default() {
let endpoint = unique_test_endpoint();
let mut listener = IpcListener::bind(&endpoint).unwrap();
let server = tokio::spawn(async move {
let mut conn = listener.accept().await.unwrap();
let msg: Option<Request> = conn.recv().await.unwrap();
assert_eq!(msg, Some(Request::Ping));
tokio::time::sleep(Duration::from_millis(200)).await;
conn.send(&Response::Pong).await.unwrap();
});
let mut client = connect(&endpoint).await.unwrap();
assert!(
client.recv_timeout().is_none(),
"fresh connect must default to unbounded (None)"
);
client.send(&Request::Ping).await.unwrap();
let resp: Option<Response> = client.recv().await.unwrap();
assert_eq!(resp, Some(Response::Pong));
server.await.unwrap();
}
#[tokio::test]
async fn recv_honors_set_recv_timeout() {
let endpoint = unique_test_endpoint();
let mut listener = IpcListener::bind(&endpoint).unwrap();
let server = tokio::spawn(async move {
let _conn = listener.accept().await.unwrap();
tokio::time::sleep(Duration::from_secs(5)).await;
});
let mut client = connect(&endpoint).await.unwrap();
client.set_recv_timeout(Duration::from_millis(200));
assert_eq!(client.recv_timeout(), Some(Duration::from_millis(200)));
client.send(&Request::Ping).await.unwrap();
let start = Instant::now();
let result: Result<Option<Response>, _> = client.recv().await;
let elapsed = start.elapsed();
match result {
Err(IpcError::Timeout(d)) => {
assert_eq!(
d,
Duration::from_millis(200),
"Timeout must carry the configured deadline"
);
}
other => panic!("expected Err(Timeout(200ms)), got {other:?}"),
}
assert!(
elapsed < Duration::from_secs(2),
"recv timeout firing took {elapsed:?}; expected <2s"
);
drop(client);
server.await.unwrap();
}
#[tokio::test]
async fn recv_with_timeout_works_without_default() {
let endpoint = unique_test_endpoint();
let mut listener = IpcListener::bind(&endpoint).unwrap();
let server = tokio::spawn(async move {
let _conn = listener.accept().await.unwrap();
tokio::time::sleep(Duration::from_secs(5)).await;
});
let mut client = connect(&endpoint).await.unwrap();
assert!(
client.recv_timeout().is_none(),
"must start without default"
);
client.send(&Request::Ping).await.unwrap();
let start = Instant::now();
let result: Result<Option<Response>, _> =
client.recv_with_timeout(Duration::from_millis(200)).await;
let elapsed = start.elapsed();
assert!(
matches!(result, Err(IpcError::Timeout(_))),
"expected Err(Timeout(_)), got {result:?}"
);
assert!(
elapsed < Duration::from_secs(2),
"recv_with_timeout firing took {elapsed:?}; expected <2s"
);
drop(client);
server.await.unwrap();
}
#[tokio::test]
async fn recv_does_not_timeout_on_normal_response() {
let endpoint = unique_test_endpoint();
let mut listener = IpcListener::bind(&endpoint).unwrap();
let server = tokio::spawn(async move {
let mut conn = listener.accept().await.unwrap();
let msg: Option<Request> = conn.recv().await.unwrap();
assert_eq!(msg, Some(Request::Ping));
tokio::time::sleep(Duration::from_millis(50)).await;
conn.send(&Response::Pong).await.unwrap();
});
let mut client = connect(&endpoint).await.unwrap();
client.set_recv_timeout(Duration::from_secs(1));
client.send(&Request::Ping).await.unwrap();
let resp: Option<Response> = client.recv().await.unwrap();
assert_eq!(resp, Some(Response::Pong));
server.await.unwrap();
}
#[tokio::test]
async fn recv_reports_io_err_when_peer_dies() {
let endpoint = unique_test_endpoint();
let mut listener = IpcListener::bind(&endpoint).unwrap();
let server = tokio::spawn(async move {
let _conn = listener.accept().await.unwrap();
});
let mut client = connect(&endpoint).await.unwrap();
client.set_recv_timeout(Duration::from_secs(5));
tokio::time::sleep(Duration::from_millis(50)).await;
let result: Result<Option<Response>, _> = client.recv().await;
match result {
Ok(None) => {} Err(IpcError::Io(_)) => {} Err(IpcError::ConnectionClosed) => {} Err(IpcError::Timeout(_)) => panic!(
"peer-death must NOT surface as Timeout — that would mean we treated \
OS-level connection close as a stuck-but-alive failure"
),
other => panic!("unexpected recv result: {other:?}"),
}
server.await.unwrap();
}