#![cfg(test)]
use crate::{
Call, Reply,
connection::write_connection::WriteConnection,
test_utils::mock_socket::{MockWriteHalf, TestWriteHalf},
};
use serde::{Deserialize, Serialize};
use std::os::unix::net::UnixStream;
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "method", content = "parameters")]
enum TestMethod {
#[serde(rename = "org.example.Test")]
Test { value: u32 },
}
#[derive(Debug, Serialize, Deserialize)]
struct TestReply {
result: String,
}
#[tokio::test]
async fn send_call_with_single_fd() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
write_conn
.send_call(&call, vec![r_fd.into()])
.await
.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 1);
assert_eq!(write_conn.write_half().fds_written().len(), 1);
}
#[tokio::test]
async fn send_call_with_multiple_fds() {
let (r1, _w1) = UnixStream::pair().unwrap();
let (r2, _w2) = UnixStream::pair().unwrap();
let (r3, _w3) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
write_conn
.send_call(&call, vec![r1.into(), r2.into(), r3.into()])
.await
.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 1);
let fds_written = write_conn.write_half().fds_written();
assert_eq!(fds_written.len(), 1);
assert_eq!(fds_written[0].len(), 3);
}
#[tokio::test]
async fn send_call_with_no_fds() {
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
write_conn.send_call(&call, vec![]).await.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 0);
}
#[tokio::test]
async fn send_call_with_empty_fd_vec() {
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
write_conn.send_call(&call, vec![]).await.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 0);
}
#[tokio::test]
async fn send_reply() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let reply = Reply::new(Some(TestReply {
result: "success".to_string(),
}));
write_conn
.send_reply(&reply, vec![r_fd.into()])
.await
.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 1);
}
#[tokio::test]
async fn enqueue_call_with_fds_succeeds() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
write_conn.enqueue_call(&call, vec![r_fd.into()]).unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 0);
}
#[tokio::test]
async fn enqueue_call_without_fds_succeeds() {
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
write_conn.enqueue_call(&call, vec![]).unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 0);
}
#[tokio::test]
async fn multiple_sequential_fd_sends() {
let (r1, _w1) = UnixStream::pair().unwrap();
let (r2, _w2) = UnixStream::pair().unwrap();
let (r3, _w3) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call1 = Call::new(TestMethod::Test { value: 1 });
write_conn.send_call(&call1, vec![r1.into()]).await.unwrap();
let call2 = Call::new(TestMethod::Test { value: 2 });
write_conn.send_call(&call2, vec![r2.into()]).await.unwrap();
let call3 = Call::new(TestMethod::Test { value: 3 });
write_conn.send_call(&call3, vec![r3.into()]).await.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 3);
}
#[tokio::test]
async fn send_call_validates_data_length() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let call = Call::new(TestMethod::Test { value: 42 });
let serialized_len = serde_json::to_vec(&call).unwrap().len() + 1;
let mut write_conn = WriteConnection::new(TestWriteHalf::new_with_fds(serialized_len, 1), 0);
write_conn
.send_call(&call, vec![r_fd.into()])
.await
.unwrap();
assert_eq!(write_conn.write_half().write_count(), 1);
}
#[tokio::test]
async fn send_call_preserves_call_flags() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 })
.set_oneway(true)
.set_more(true);
write_conn
.send_call(&call, vec![r_fd.into()])
.await
.unwrap();
let written = write_conn.write_half().written_data();
let written_str = core::str::from_utf8(written).unwrap();
assert!(written_str.contains("\"oneway\":true"));
assert!(written_str.contains("\"more\":true"));
}
#[tokio::test]
async fn backward_compat_send_call_works() {
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
write_conn.send_call(&call, vec![]).await.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 0);
}
#[tokio::test]
async fn mixed_send_call_and_send_call() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call1 = Call::new(TestMethod::Test { value: 1 });
write_conn.send_call(&call1, vec![]).await.unwrap();
let call2 = Call::new(TestMethod::Test { value: 2 });
write_conn
.send_call(&call2, vec![r_fd.into()])
.await
.unwrap();
let call3 = Call::new(TestMethod::Test { value: 3 });
write_conn.send_call(&call3, vec![]).await.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 1);
}
#[tokio::test]
async fn send_call_buffer_growth() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let _large_data = vec![0u8; 10000];
let call = Call::new(TestMethod::Test { value: 42 });
write_conn
.send_call(&call, vec![r_fd.into()])
.await
.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 1);
}
#[tokio::test]
async fn send_reply_with_multiple_fds() {
let (r1, _w1) = UnixStream::pair().unwrap();
let (r2, _w2) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let reply = Reply::new(Some(TestReply {
result: "success".to_string(),
}));
write_conn
.send_reply(&reply, vec![r1.into(), r2.into()])
.await
.unwrap();
let fds_written = write_conn.write_half().fds_written();
assert_eq!(fds_written.len(), 1);
assert_eq!(fds_written[0].len(), 2);
}
#[tokio::test]
async fn send_reply_with_no_fds() {
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let reply = Reply::new(Some(TestReply {
result: "success".to_string(),
}));
write_conn.send_reply(&reply, vec![]).await.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 0);
}
#[tokio::test]
async fn send_error() {
use crate::ReplyError;
#[derive(Debug, ReplyError)]
#[zlink(interface = "org.example")]
enum TestError {
NotFound { code: i32 },
}
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let error = TestError::NotFound { code: 404 };
write_conn
.send_error(&error, vec![r_fd.into()])
.await
.unwrap();
assert_eq!(write_conn.write_half().fd_write_count(), 1);
}
#[tokio::test]
async fn enqueue_normal_then_send_with_fds_clears_queue() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call1 = Call::new(TestMethod::Test { value: 1 });
write_conn.enqueue_call(&call1, vec![]).unwrap();
let call2 = Call::new(TestMethod::Test { value: 2 });
let result = write_conn.send_call(&call2, vec![r_fd.into()]).await;
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn fd_ownership_with_borrowed_fd() {
let (r_fd, _w_fd) = UnixStream::pair().unwrap();
let mut write_conn = WriteConnection::new(MockWriteHalf::new(), 0);
let call = Call::new(TestMethod::Test { value: 42 });
{
write_conn
.send_call(&call, vec![r_fd.into()])
.await
.unwrap();
}
assert_eq!(write_conn.write_half().fd_write_count(), 1);
}