#![cfg(any(unix, windows))]
#[allow(unused_imports)]
use mq_bridge::endpoints::memory::transport::{TransportChannel, TransportUrl};
#[allow(unused_imports)]
use mq_bridge::CanonicalMessage;
#[cfg(unix)]
#[allow(unused_imports)]
use mq_bridge::endpoints::memory::ipc_unix::UnixIpcTransport;
#[cfg(windows)]
#[allow(unused_imports)]
use mq_bridge::endpoints::memory::ipc_windows::WindowsIpcTransport;
#[cfg(any(unix, windows))]
#[allow(dead_code)]
async fn connect_with_retry<T, F, Fut>(mut make_client: F) -> T
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = anyhow::Result<T>>,
{
let deadline = tokio::time::Instant::now() + tokio::time::Duration::from_secs(5);
let mut last_error = None;
loop {
match make_client().await {
Ok(client) => return client,
Err(error) if tokio::time::Instant::now() < deadline => {
last_error = Some(error);
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
}
Err(error) => {
panic!(
"timed out connecting IPC client: {}",
last_error.unwrap_or(error)
);
}
}
}
}
#[cfg(unix)]
#[tokio::test]
async fn test_unix_ipc_basic_roundtrip() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test-basic.sock");
let server = UnixIpcTransport::new_server(&socket_path, 10)
.await
.unwrap();
let socket_path_clone = socket_path.clone();
let client_handle = tokio::spawn(async move {
connect_with_retry(|| UnixIpcTransport::new_client(&socket_path_clone, 10)).await
});
let client = client_handle.await.unwrap();
let mut msg = CanonicalMessage::from_vec(b"Hello IPC!");
msg.metadata
.insert("test_key".to_string(), "test_value".to_string());
client.send_batch(vec![msg.clone()]).await.unwrap();
let received = server.recv_batch().await.unwrap();
assert_eq!(received.len(), 1);
assert_eq!(received[0].payload.as_ref(), b"Hello IPC!");
assert_eq!(
received[0].metadata.get("test_key"),
Some(&"test_value".to_string())
);
}
#[cfg(unix)]
#[tokio::test]
async fn test_unix_ipc_multiple_messages() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test-multi.sock");
let server = UnixIpcTransport::new_server(&socket_path, 10)
.await
.unwrap();
let socket_path_clone = socket_path.clone();
let client_handle = tokio::spawn(async move {
connect_with_retry(|| UnixIpcTransport::new_client(&socket_path_clone, 10)).await
});
let client = client_handle.await.unwrap();
let messages: Vec<_> = (0..5)
.map(|i| CanonicalMessage::from_vec(format!("Message {}", i).into_bytes()))
.collect();
client.send_batch(messages.clone()).await.unwrap();
let received = server.recv_batch().await.unwrap();
assert_eq!(received.len(), 5);
for (i, msg) in received.iter().enumerate() {
assert_eq!(msg.payload.as_ref(), format!("Message {}", i).as_bytes());
}
}
#[cfg(unix)]
#[tokio::test]
async fn test_unix_ipc_url_parsing() {
let url = TransportUrl::parse("memory://test-topic").unwrap();
assert!(matches!(url, TransportUrl::Memory { .. }));
let url = TransportUrl::parse("unix:///tmp/test.sock").unwrap();
assert!(matches!(url, TransportUrl::Unix { .. }));
let url = TransportUrl::parse("ipc:///var/run/test.sock").unwrap();
assert!(matches!(url, TransportUrl::Unix { .. }));
let url = TransportUrl::parse("ipc://worker-queue").unwrap();
assert!(matches!(url, TransportUrl::Unix { .. }));
}
#[cfg(windows)]
#[tokio::test]
async fn test_windows_pipe_basic_roundtrip() {
let pipe_name = format!("mq-bridge-test-{}", uuid::Uuid::new_v4());
let server = WindowsIpcTransport::new_server(&pipe_name, 10)
.await
.unwrap();
let pipe_name_clone = pipe_name.clone();
let client_handle = tokio::spawn(async move {
connect_with_retry(|| WindowsIpcTransport::new_client(&pipe_name_clone, 10)).await
});
server.wait_for_connection().await.unwrap();
let client = client_handle.await.unwrap();
let msg = CanonicalMessage::from_vec(b"Hello Named Pipe!");
client.send_batch(vec![msg.clone()]).await.unwrap();
let received = server.recv_batch().await.unwrap();
assert_eq!(received.len(), 1);
assert_eq!(received[0].payload.as_ref(), b"Hello Named Pipe!");
}
#[cfg(windows)]
#[tokio::test]
async fn test_windows_pipe_url_parsing() {
let url = TransportUrl::parse("pipe://worker-queue").unwrap();
assert!(matches!(url, TransportUrl::Pipe { .. }));
let url = TransportUrl::parse("ipc://worker-queue").unwrap();
assert!(matches!(url, TransportUrl::Pipe { .. }));
}
#[tokio::test]
async fn test_transport_url_display() {
let url = TransportUrl::parse("memory://test").unwrap();
assert_eq!(url.display_name(), "memory://test");
#[cfg(unix)]
{
let url = TransportUrl::parse("unix:///tmp/test.sock").unwrap();
assert_eq!(url.display_name(), "unix:///tmp/test.sock");
}
#[cfg(windows)]
{
let url = TransportUrl::parse("pipe://test").unwrap();
assert_eq!(url.display_name(), "pipe://test");
}
}