#[cfg(feature = "dev-certs")]
mod tests {
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use fastnet::{SecureSocket, SecureEvent};
use rcgen::generate_simple_self_signed;
use rustls::pki_types::PrivateKeyDer;
use tokio::sync::Mutex;
fn gen_certs() -> (Vec<rustls::pki_types::CertificateDer<'static>>, PrivateKeyDer<'static>) {
let cert = generate_simple_self_signed(vec!["localhost".into()])
.expect("Failed to generate certificate");
let certs = vec![cert.cert.der().clone()];
let key = PrivateKeyDer::Pkcs8(cert.key_pair.serialize_der().into());
(certs, key)
}
async fn setup_connected_pair() -> (SecureSocket, SecureSocket, u16, u16) {
let (certs, key) = gen_certs();
let udp_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let tcp_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let mut server = SecureSocket::bind_server(udp_addr, tcp_addr, certs, key)
.await
.expect("Failed to bind server");
let actual_tcp = server.local_tcp_addr().unwrap().unwrap();
let server_handle = tokio::spawn(async move {
let events = server.poll().await.expect("Server poll failed");
let server_peer_id = events.iter().find_map(|e| {
if let SecureEvent::Connected(id) = e { Some(*id) } else { None }
}).expect("Server should get Connected event");
(server, server_peer_id)
});
let mut client = SecureSocket::connect(actual_tcp)
.await
.expect("Client failed to connect");
let client_events = client.poll().await.expect("Client poll failed");
let client_peer_id = client_events.iter().find_map(|e| {
if let SecureEvent::Connected(id) = e { Some(*id) } else { None }
}).expect("Client should get Connected event");
let (server, server_peer_id) = server_handle.await.unwrap();
(server, client, server_peer_id, client_peer_id)
}
#[tokio::test]
async fn test_connection() {
println!("\n=== TEST: Basic Connection ===");
let (server, _client, server_peer_id, client_peer_id) = setup_connected_pair().await;
println!("[client] Connected as peer {}", client_peer_id);
println!("[server] Peer {} connected", server_peer_id);
assert_eq!(server.peer_count(), 1, "Server should have 1 peer");
println!("[OK] Connection test passed\n");
}
#[tokio::test]
async fn test_send_receive() {
println!("\n=== TEST: Send and Receive ===");
let (mut server, mut client, server_peer_id, client_peer_id) =
setup_connected_pair().await;
let test_data = b"Hello, FastNet!".to_vec();
client.send(client_peer_id, 0, test_data.clone()).await
.expect("Client send failed");
println!("[client] Sent: {:?}", String::from_utf8_lossy(&test_data));
let mut received = false;
for _ in 0..50 {
let events = server.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(peer_id, channel, data) = event {
println!("[server] Received from peer {}, channel {}: {:?}",
peer_id, channel, String::from_utf8_lossy(data));
assert_eq!(*peer_id, server_peer_id);
assert_eq!(data, &test_data);
assert_eq!(*channel, 0);
received = true;
server.send(*peer_id, *channel, data.clone()).await
.expect("Server echo failed");
println!("[server] Echoed back");
}
}
if received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(received, "Server should have received data");
let mut echo_received = false;
for _ in 0..50 {
let events = client.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, data) = event {
println!("[client] Echo received: {:?}", String::from_utf8_lossy(data));
assert_eq!(data, &test_data);
echo_received = true;
}
}
if echo_received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(echo_received, "Client should have received echo");
println!("[OK] Send/Receive test passed\n");
}
#[tokio::test]
async fn test_data_serialization() {
println!("\n=== TEST: Data Serialization ===");
let (mut server, mut client, _server_peer_id, client_peer_id) =
setup_connected_pair().await;
let test_cases: Vec<(&str, Vec<u8>)> = vec![
("Single byte", vec![0xFF]),
("Binary zeros", vec![0, 0, 0, 0]),
("UTF-8 string", "Olá, mundo! 🌍".as_bytes().to_vec()),
("u32 LE", 42u32.to_le_bytes().to_vec()),
("f64 LE", std::f64::consts::PI.to_le_bytes().to_vec()),
("Mixed struct", {
let mut buf = Vec::new();
buf.extend_from_slice(&100.5f32.to_le_bytes());
buf.extend_from_slice(&200.75f32.to_le_bytes());
buf.extend_from_slice(&95u16.to_le_bytes());
buf.push(0b1010_0101);
buf
}),
("Large payload", vec![0xAB; 1000]),
];
for (name, data) in &test_cases {
println!("[test] Sending: {} ({} bytes)", name, data.len());
client.send(client_peer_id, 0, data.clone()).await
.expect("Send failed");
let mut received = false;
for _ in 0..50 {
let events = server.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, recv_data) = event {
assert_eq!(recv_data, data,
"Data mismatch for test case: {}", name);
println!("[ ok] Received correctly: {} ({} bytes)", name, recv_data.len());
received = true;
}
}
if received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(received, "Failed to receive data for: {}", name);
}
println!("[OK] Serialization test passed\n");
}
#[tokio::test]
async fn test_client_disconnect() {
println!("\n=== TEST: Client Disconnection ===");
let (mut server, mut client, server_peer_id, client_peer_id) =
setup_connected_pair().await;
println!("[client] peer_id={}, [server] sees peer_id={}", client_peer_id, server_peer_id);
assert_eq!(server.peer_count(), 1);
client.send(client_peer_id, 0, b"pre-disconnect data".to_vec()).await.unwrap();
println!("[client] Sent pre-disconnect data");
let mut data_received = false;
for _ in 0..50 {
let events = server.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, _) = event {
data_received = true;
}
}
if data_received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(data_received, "Server should have received pre-disconnect data");
println!("[client] Calling disconnect({})...", client_peer_id);
let disconnect_result = client.disconnect(client_peer_id).await;
println!("[client] disconnect() returned: {:?}", disconnect_result);
assert!(disconnect_result.is_ok(), "Client disconnect should succeed");
assert_eq!(client.peer_count(), 0, "Client should have 0 peers after disconnect");
println!("[client] peer_count={}", client.peer_count());
let mut server_got_disconnect = false;
for attempt in 0..100 {
let events = server.poll().await.unwrap();
for event in &events {
match event {
SecureEvent::Disconnected(id) => {
println!("[server] Peer {} disconnected (attempt {})", id, attempt);
assert_eq!(*id, server_peer_id);
server_got_disconnect = true;
}
other => {
println!("[server] Got other event: {:?}", other);
}
}
}
if server_got_disconnect { break; }
tokio::time::sleep(Duration::from_millis(50)).await;
}
assert!(server_got_disconnect, "Server should receive Disconnected event for the client");
assert_eq!(server.peer_count(), 0, "Server should have 0 peers after client disconnect");
println!("[OK] Client disconnection test passed\n");
}
#[tokio::test]
async fn test_server_disconnect() {
println!("\n=== TEST: Server Disconnects Client ===");
let (mut server, mut client, server_peer_id, client_peer_id) =
setup_connected_pair().await;
client.send(client_peer_id, 0, b"hello from client".to_vec()).await.unwrap();
let mut data_received = false;
for _ in 0..50 {
let events = server.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, _) = event {
data_received = true;
}
}
if data_received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(data_received);
println!("[server] Calling disconnect({})...", server_peer_id);
let result = server.disconnect(server_peer_id).await;
println!("[server] disconnect() returned: {:?}", result);
assert!(result.is_ok(), "Server disconnect should succeed");
assert_eq!(server.peer_count(), 0, "Server should have 0 peers");
let mut client_got_disconnect = false;
for attempt in 0..100 {
let events = client.poll().await.unwrap();
for event in &events {
match event {
SecureEvent::Disconnected(id) => {
println!("[client] Disconnected by server, peer_id={} (attempt {})", id, attempt);
client_got_disconnect = true;
}
other => {
println!("[client] Got other event: {:?}", other);
}
}
}
if client_got_disconnect { break; }
tokio::time::sleep(Duration::from_millis(50)).await;
}
assert!(client_got_disconnect, "Client should receive Disconnected event from server");
assert_eq!(client.peer_count(), 0, "Client should have 0 peers after server disconnect");
println!("[OK] Server disconnection test passed\n");
}
#[tokio::test]
async fn test_multiple_clients() {
println!("\n=== TEST: Multiple Clients ===");
let (certs, key) = gen_certs();
let udp_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let tcp_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let server = Arc::new(Mutex::new(
SecureSocket::bind_server(udp_addr, tcp_addr, certs, key).await.unwrap()
));
let actual_tcp = server.lock().await.local_tcp_addr().unwrap().unwrap();
let mut clients = Vec::new();
let mut client_peer_ids = Vec::new();
let mut server_peer_ids = Vec::new();
for i in 0..3 {
let server_clone = server.clone();
let server_handle = tokio::spawn(async move {
let mut srv = server_clone.lock().await;
let events = srv.poll().await.expect("Server poll failed");
events.iter().find_map(|e| {
if let SecureEvent::Connected(id) = e { Some(*id) } else { None }
})
});
let mut client = SecureSocket::connect(actual_tcp).await.unwrap();
let cpid = client.poll().await.unwrap().iter().find_map(|e| {
if let SecureEvent::Connected(id) = e { Some(*id) } else { None }
}).unwrap();
let spid = server_handle.await.unwrap().expect("Server should accept");
println!("[client {}] peer_id={}, [server] sees peer_id={}", i, cpid, spid);
client_peer_ids.push(cpid);
server_peer_ids.push(spid);
clients.push(client);
}
{
let srv = server.lock().await;
assert_eq!(srv.peer_count(), 3, "Server should have 3 peers");
println!("[server] peer_count={}", srv.peer_count());
}
for (i, client) in clients.iter_mut().enumerate() {
let msg = format!("Hello from client {}", i);
client.send(client_peer_ids[i], 0, msg.into_bytes()).await.unwrap();
}
{
let mut srv = server.lock().await;
let mut messages_received = 0;
for _ in 0..100 {
let events = srv.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, _) = event {
messages_received += 1;
}
}
if messages_received >= 3 { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert_eq!(messages_received, 3, "Server should receive 3 messages");
}
println!("[client 0] Disconnecting...");
clients[0].disconnect(client_peer_ids[0]).await.unwrap();
{
let mut srv = server.lock().await;
let mut disconnect_count = 0;
for _ in 0..100 {
let events = srv.poll().await.unwrap();
for event in &events {
if let SecureEvent::Disconnected(_) = event {
disconnect_count += 1;
}
}
if disconnect_count >= 1 { break; }
tokio::time::sleep(Duration::from_millis(50)).await;
}
assert!(disconnect_count >= 1, "Server should see at least 1 disconnection");
assert_eq!(srv.peer_count(), 2, "Server should have 2 peers remaining");
println!("[server] peer_count={} after disconnect", srv.peer_count());
}
println!("[OK] Multiple clients test passed\n");
}
#[tokio::test]
async fn test_send_after_disconnect() {
println!("\n=== TEST: Send After Disconnect ===");
let (_server, mut client, _server_peer_id, client_peer_id) =
setup_connected_pair().await;
client.disconnect(client_peer_id).await.unwrap();
let result = client.send(client_peer_id, 0, b"should fail".to_vec()).await;
println!("[client] Send after disconnect: {:?}", result);
assert!(result.is_err(), "Send after disconnect should fail with error");
println!("[OK] Send after disconnect test passed\n");
}
#[tokio::test]
async fn test_crash_detection_timeout() {
println!("\n=== TEST: Crash Detection via Timeout ===");
let (mut server, mut client, server_peer_id, client_peer_id) =
setup_connected_pair().await;
client.send(client_peer_id, 0, b"I will crash soon".to_vec()).await.unwrap();
let mut data_received = false;
for _ in 0..50 {
let events = server.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, _) = event {
data_received = true;
}
}
if data_received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(data_received);
println!("[client] Dropping client WITHOUT calling disconnect (simulating crash)...");
assert_eq!(server.peer_count(), 1, "Server should have 1 peer before crash");
drop(client);
let mut server_detected_disconnect = false;
let start = std::time::Instant::now();
let max_wait = Duration::from_secs(15);
while start.elapsed() < max_wait {
let events = server.poll().await.unwrap();
for event in &events {
if let SecureEvent::Disconnected(id) = event {
println!("[server] Detected crashed peer {} after {:?}", id, start.elapsed());
assert_eq!(*id, server_peer_id);
server_detected_disconnect = true;
}
}
if server_detected_disconnect { break; }
tokio::time::sleep(Duration::from_millis(100)).await;
}
assert!(server_detected_disconnect,
"BUG: Server should detect crashed client via timeout, but timeout is None by default!");
assert_eq!(server.peer_count(), 0, "Server should have 0 peers after timeout");
println!("[OK] Crash detection test passed\n");
}
#[tokio::test]
async fn test_server_crash_detection() {
println!("\n=== TEST: Server Crash Detection (client side) ===");
let (mut server, mut client, server_peer_id, client_peer_id) =
setup_connected_pair().await;
client.send(client_peer_id, 0, b"hello server".to_vec()).await.unwrap();
let mut data_received = false;
for _ in 0..50 {
let events = server.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, _) = event {
data_received = true;
}
}
if data_received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(data_received);
server.send(server_peer_id, 0, b"hello client".to_vec()).await.unwrap();
let mut echo_received = false;
for _ in 0..50 {
let events = client.poll().await.unwrap();
for event in &events {
if let SecureEvent::Data(_, _, _) = event {
echo_received = true;
}
}
if echo_received { break; }
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(echo_received);
println!("[server] Dropping server WITHOUT calling disconnect (simulating crash)...");
assert_eq!(client.peer_count(), 1, "Client should have 1 peer before server crash");
drop(server);
let mut client_detected_disconnect = false;
let start = std::time::Instant::now();
let max_wait = Duration::from_secs(15);
while start.elapsed() < max_wait {
let events = client.poll().await.unwrap();
for event in &events {
if let SecureEvent::Disconnected(id) = event {
println!("[client] Detected crashed server (peer {}) after {:?}", id, start.elapsed());
assert_eq!(*id, client_peer_id);
client_detected_disconnect = true;
}
}
if client_detected_disconnect { break; }
tokio::time::sleep(Duration::from_millis(100)).await;
}
assert!(client_detected_disconnect,
"Client should detect crashed server via timeout!");
assert_eq!(client.peer_count(), 0, "Client should have 0 peers after server crash");
println!("[OK] Server crash detection test passed\n");
}
#[tokio::test]
async fn test_double_disconnect() {
println!("\n=== TEST: Double Disconnect ===");
let (_server, mut client, _server_peer_id, client_peer_id) =
setup_connected_pair().await;
client.disconnect(client_peer_id).await.unwrap();
println!("[client] First disconnect succeeded");
let result = client.disconnect(client_peer_id).await;
println!("[client] Second disconnect result: {:?}", result);
assert!(result.is_err(), "Double disconnect should return error");
println!("[OK] Double disconnect test passed\n");
}
}
#[cfg(not(feature = "dev-certs"))]
#[test]
fn test_requires_dev_certs_feature() {
eprintln!("Integration tests require the 'dev-certs' feature.");
eprintln!("Run with: cargo test --test secure_socket_integration --features dev-certs -- --nocapture");
}