#![cfg(feature = "net")]
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use net::adapter::net::{EntityKeypair, MeshNode, MeshNodeConfig, SocketBufferConfig};
const TEST_BUFFER_SIZE: usize = 256 * 1024;
const PSK: [u8; 32] = [0x42u8; 32];
fn test_config() -> MeshNodeConfig {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let mut cfg = MeshNodeConfig::new(addr, PSK)
.with_heartbeat_interval(Duration::from_millis(500))
.with_session_timeout(Duration::from_secs(5))
.with_handshake(3, Duration::from_secs(3));
cfg.socket_buffers = SocketBufferConfig {
send_buffer_size: TEST_BUFFER_SIZE,
recv_buffer_size: TEST_BUFFER_SIZE,
};
cfg
}
async fn build_node() -> Arc<MeshNode> {
Arc::new(
MeshNode::new(EntityKeypair::generate(), test_config())
.await
.expect("MeshNode::new"),
)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn initiator_connect_after_start_completes_handshake() {
let a = build_node().await;
let b = build_node().await;
let b_pub = *b.public_key();
let b_addr = b.local_addr();
let b_id = b.node_id();
let a_id = a.node_id();
let b_clone = b.clone();
let accept = tokio::spawn(async move { b_clone.accept(a_id).await });
tokio::time::sleep(Duration::from_millis(20)).await;
a.start();
a.connect(b_addr, &b_pub, b_id)
.await
.expect("connect after start must complete the handshake");
accept
.await
.expect("accept task panicked")
.expect("accept must complete");
assert!(a.peer_count() > 0, "A must have registered the peer");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn accept_after_start_returns_explicit_error() {
let a = build_node().await;
a.start();
let result = tokio::time::timeout(Duration::from_millis(500), a.accept(0xDEADBEEF)).await;
let inner = result.expect("accept-after-start must NOT hang past the guard timeout");
assert!(
inner.is_err(),
"accept-after-start must return Err, got Ok({:?})",
inner
);
let msg = format!("{}", inner.unwrap_err());
assert!(
msg.contains("CR-7") || msg.contains("after start"),
"error message must reference the ordering contract; got: {}",
msg
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn cubic_p1_start_refuses_while_accept_in_flight() {
let a = build_node().await;
let a_clone = a.clone();
let accept_handle = tokio::spawn(async move { a_clone.accept(0xCAFEBEEF).await });
tokio::time::sleep(Duration::from_millis(50)).await;
a.start();
accept_handle.abort();
let _ = accept_handle.await;
tokio::time::sleep(Duration::from_millis(10)).await;
a.start();
assert_eq!(a.peer_count(), 0);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn second_connect_after_first_uses_registry_path() {
let a = build_node().await;
let b = build_node().await;
let b_pub = *b.public_key();
let b_addr = b.local_addr();
let b_id = b.node_id();
let a_id = a.node_id();
let b_clone = b.clone();
let accept = tokio::spawn(async move { b_clone.accept(a_id).await });
tokio::time::sleep(Duration::from_millis(20)).await;
a.start();
a.connect(b_addr, &b_pub, b_id)
.await
.expect("first connect must complete");
accept
.await
.expect("first accept panicked")
.expect("first accept must complete");
assert!(
a.peer_count() > 0,
"A must have registered the peer via the registry path"
);
}