#![cfg(all(feature = "net", feature = "nat-traversal"))]
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use net::adapter::net::traversal::TraversalError;
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(200))
.with_session_timeout(Duration::from_secs(5))
.with_handshake(3, Duration::from_secs(2));
cfg.socket_buffers = SocketBufferConfig {
send_buffer_size: TEST_BUFFER_SIZE,
recv_buffer_size: TEST_BUFFER_SIZE,
};
cfg
}
async fn build_node() -> Arc<MeshNode> {
let cfg = test_config();
let keypair = EntityKeypair::generate();
Arc::new(MeshNode::new(keypair, cfg).await.expect("MeshNode::new"))
}
async fn handshake(a: &Arc<MeshNode>, b: &Arc<MeshNode>) {
let a_id = a.node_id();
let b_id = b.node_id();
let b_pub = *b.public_key();
let b_addr = b.local_addr();
let b_clone = b.clone();
let accept = tokio::spawn(async move { b_clone.accept(a_id).await });
a.connect(b_addr, &b_pub, b_id)
.await
.expect("connect failed");
accept
.await
.expect("accept task panicked")
.expect("accept failed");
a.start();
b.start();
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn reflex_probe_returns_observed_source_address() {
let a = build_node().await;
let b = build_node().await;
handshake(&a, &b).await;
let a_bind = a.local_addr();
let b_id = b.node_id();
let observed = a
.probe_reflex(b_id)
.await
.expect("reflex probe should succeed on localhost");
assert_eq!(
observed, a_bind,
"reflex response should echo A's bind addr; got {observed}, expected {a_bind}",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn reflex_probe_unknown_peer_fails_fast() {
let a = build_node().await;
a.start();
let bogus_node_id: u64 = 0xDEAD_BEEF_FEED_CAFE;
let start = tokio::time::Instant::now();
let err = a
.probe_reflex(bogus_node_id)
.await
.expect_err("unknown peer should fail");
let elapsed = start.elapsed();
match err {
TraversalError::PeerNotReachable => {}
other => panic!("expected PeerNotReachable, got {other:?}"),
}
assert!(
elapsed < Duration::from_millis(500),
"fast-fail took {elapsed:?}; want < 500 ms",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn reflex_probe_is_bidirectional() {
let a = build_node().await;
let b = build_node().await;
handshake(&a, &b).await;
let a_bind = a.local_addr();
let b_bind = b.local_addr();
let from_a = a.probe_reflex(b.node_id()).await.expect("A → B probe");
let from_b = b.probe_reflex(a.node_id()).await.expect("B → A probe");
assert_eq!(from_a, a_bind, "A's reflex from B's POV");
assert_eq!(from_b, b_bind, "B's reflex from A's POV");
}