#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::error::Error;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::Duration;
use ant_quic::{
VarInt,
config::nat_timeouts::TimeoutConfig,
nat_traversal_api::{NatTraversalConfig, NatTraversalEndpoint, NatTraversalError, PeerId},
};
fn create_test_peer_id(id: u8) -> PeerId {
let mut bytes = [0u8; 32];
bytes[0] = id;
bytes[31] = id; PeerId(bytes)
}
#[cfg(test)]
mod nat_traversal_api_tests {
use super::*;
#[test]
fn test_peer_id_creation_and_display() {
let peer_id = create_test_peer_id(42);
assert_eq!(peer_id.0[0], 42);
assert_eq!(peer_id.0[31], 42);
let display_string = format!("{peer_id}");
assert_eq!(display_string.len(), 16); assert!(display_string.starts_with("2a")); }
#[test]
fn test_peer_id_from_bytes() {
let bytes = [1u8; 32];
let peer_id = PeerId::from(bytes);
assert_eq!(peer_id.0, bytes);
}
#[test]
fn test_peer_id_uniqueness() {
let peer1 = create_test_peer_id(1);
let peer2 = create_test_peer_id(2);
let peer1_copy = create_test_peer_id(1);
assert_ne!(peer1, peer2);
assert_eq!(peer1, peer1_copy);
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(peer1, "peer1");
map.insert(peer2, "peer2");
assert_eq!(map.get(&peer1_copy), Some(&"peer1"));
assert_eq!(map.len(), 2);
}
#[test]
fn test_nat_traversal_config_default() {
let config = NatTraversalConfig::default();
assert_eq!(config.max_candidates, 8);
assert_eq!(config.coordination_timeout, Duration::from_secs(10));
assert!(config.enable_symmetric_nat);
assert!(config.enable_relay_fallback);
assert_eq!(config.max_concurrent_attempts, 3);
assert!(config.known_peers.is_empty());
}
#[test]
fn test_nat_traversal_config_with_known_peers() {
let known_peer_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 1)), 8080);
let config = NatTraversalConfig {
known_peers: vec![known_peer_addr],
max_candidates: 16,
coordination_timeout: Duration::from_secs(30),
enable_symmetric_nat: false,
enable_relay_fallback: false,
max_concurrent_attempts: 5,
bind_addr: None,
prefer_rfc_nat_traversal: false,
pqc: None,
timeouts: TimeoutConfig::default(),
identity_key: None,
relay_nodes: vec![],
enable_relay_service: true,
allow_ipv4_mapped: true,
transport_registry: None,
max_message_size: ant_quic::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE,
max_concurrent_uni_streams: 100,
additional_bind_addrs: Vec::new(),
};
assert_eq!(config.known_peers.len(), 1);
assert_eq!(config.known_peers[0], known_peer_addr);
assert_eq!(config.max_candidates, 16);
assert_eq!(config.coordination_timeout, Duration::from_secs(30));
assert!(!config.enable_symmetric_nat);
assert!(!config.enable_relay_fallback);
assert_eq!(config.max_concurrent_attempts, 5);
}
#[tokio::test]
async fn test_nat_traversal_endpoint_creation_without_known_peers() {
let config = NatTraversalConfig {
known_peers: vec![],
bind_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
..NatTraversalConfig::default()
};
let result = NatTraversalEndpoint::new(config, None, None).await;
let _ = result;
}
#[tokio::test]
async fn test_nat_traversal_endpoint_creation_with_known_peers() {
let known_peer_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 1)), 8080);
let config = NatTraversalConfig {
known_peers: vec![known_peer_addr],
..NatTraversalConfig::default()
};
let result = NatTraversalEndpoint::new(config, None, None).await;
if let Err(e) = result {
assert!(!e.to_string().contains("bootstrap node"));
}
}
#[tokio::test]
async fn test_known_peer_management() {
let known_peer_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 1)), 8080);
let known_peer_addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 2)), 8080);
let config = NatTraversalConfig {
known_peers: vec![known_peer_addr1],
..NatTraversalConfig::default()
};
if let Ok(endpoint) = NatTraversalEndpoint::new(config, None, None).await {
let result = endpoint.add_bootstrap_node(known_peer_addr2);
assert!(result.is_ok());
let result = endpoint.remove_bootstrap_node(known_peer_addr1);
assert!(result.is_ok());
let stats = endpoint.get_statistics();
assert!(stats.is_ok());
if let Ok(stats) = stats {
assert_eq!(stats.active_sessions, 0); }
}
}
}
#[cfg(test)]
mod functional_tests {
use super::*;
#[test]
fn test_varint_compatibility() {
let small_value = VarInt::from_u32(42);
let medium_value = VarInt::from_u32(10000);
let large_value = VarInt::from_u32(1000000);
assert_eq!(small_value.into_inner(), 42);
assert_eq!(medium_value.into_inner(), 10000);
assert_eq!(large_value.into_inner(), 1000000);
let max_value = VarInt::from_u32(u32::MAX);
assert_eq!(max_value.into_inner(), u32::MAX as u64);
}
#[test]
fn test_socket_address_handling() {
let ipv4_local = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)), 5000);
let ipv4_public = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 1)), 8080);
let ipv6_addr = SocketAddr::new(IpAddr::V6("2001:db8::1".parse().unwrap()), 9000);
assert!(ipv4_local.ip().is_ipv4());
assert!(!ipv4_local.ip().is_loopback());
assert!(ipv4_public.ip().is_ipv4());
assert!(!ipv4_public.ip().is_loopback());
assert!(ipv6_addr.ip().is_ipv6());
assert_eq!(ipv4_local.port(), 5000);
assert_eq!(ipv4_public.port(), 8080);
assert_eq!(ipv6_addr.port(), 9000);
}
#[tokio::test]
async fn test_configuration_validation() {
let zero_values_config = NatTraversalConfig {
known_peers: vec![],
max_candidates: 0,
coordination_timeout: Duration::from_secs(0),
enable_symmetric_nat: true,
enable_relay_fallback: true,
max_concurrent_attempts: 0,
bind_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
prefer_rfc_nat_traversal: false,
pqc: None,
timeouts: TimeoutConfig::default(),
identity_key: None,
relay_nodes: vec![],
enable_relay_service: true,
allow_ipv4_mapped: true,
transport_registry: None,
max_message_size: ant_quic::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE,
max_concurrent_uni_streams: 100,
additional_bind_addrs: Vec::new(),
};
let result = NatTraversalEndpoint::new(zero_values_config, None, None).await;
let _ = result;
let valid_config = NatTraversalConfig {
known_peers: vec![],
max_candidates: 8,
coordination_timeout: Duration::from_secs(10),
enable_symmetric_nat: true,
enable_relay_fallback: true,
max_concurrent_attempts: 3,
bind_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
prefer_rfc_nat_traversal: false,
pqc: None,
timeouts: TimeoutConfig::default(),
identity_key: None,
relay_nodes: vec![],
enable_relay_service: true,
allow_ipv4_mapped: true,
transport_registry: None,
max_message_size: ant_quic::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE,
max_concurrent_uni_streams: 100,
additional_bind_addrs: Vec::new(),
};
let result = NatTraversalEndpoint::new(valid_config, None, None).await;
let _ = result;
}
}
#[cfg(test)]
mod error_handling_tests {
use super::*;
#[test]
fn test_nat_traversal_error_display() {
let errors = vec![
NatTraversalError::NoBootstrapNodes,
NatTraversalError::NoCandidatesFound,
NatTraversalError::CandidateDiscoveryFailed("test error".to_string()),
NatTraversalError::CoordinationFailed("coordination error".to_string()),
NatTraversalError::HolePunchingFailed,
NatTraversalError::ValidationTimeout,
NatTraversalError::NetworkError("network issue".to_string()),
NatTraversalError::ConfigError("config issue".to_string()),
NatTraversalError::ProtocolError("protocol issue".to_string()),
NatTraversalError::Timeout,
NatTraversalError::ConnectionFailed("connection error".to_string()),
NatTraversalError::TraversalFailed("traversal error".to_string()),
];
for error in errors {
let error_string = format!("{error}");
assert!(!error_string.is_empty());
assert!(!error_string.starts_with("NatTraversalError")); }
}
#[test]
fn test_error_chain_compatibility() {
let error = NatTraversalError::ConfigError("test error".to_string());
let _source: Option<&dyn Error> = error.source();
let result: Result<(), NatTraversalError> = Err(error);
assert!(result.is_err());
if let Err(e) = result {
assert!(e.to_string().contains("config"));
assert!(e.to_string().contains("test error"));
}
}
}
#[cfg(test)]
mod nat_traversal_integration_tests {
use super::*;
#[tokio::test]
async fn test_nat_traversal_initiation() {
let known_peer_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 1)), 8080);
let config = NatTraversalConfig {
known_peers: vec![known_peer_addr],
..NatTraversalConfig::default()
};
if let Ok(endpoint) = NatTraversalEndpoint::new(config, None, None).await {
let target_peer = create_test_peer_id(42);
let result = endpoint.initiate_nat_traversal(target_peer, known_peer_addr);
let _ = result;
}
}
#[tokio::test]
async fn test_polling_without_active_sessions() {
let config = NatTraversalConfig {
known_peers: vec![],
bind_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
..NatTraversalConfig::default()
};
if let Ok(endpoint) = NatTraversalEndpoint::new(config, None, None).await {
let now = std::time::Instant::now();
let result = endpoint.poll(now);
assert!(result.is_ok());
if let Ok(events) = result {
let _ = events;
}
}
}
#[tokio::test]
async fn test_statistics_without_activity() {
let config = NatTraversalConfig {
known_peers: vec![],
bind_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
..NatTraversalConfig::default()
};
if let Ok(endpoint) = NatTraversalEndpoint::new(config, None, None).await {
let stats = endpoint.get_statistics();
assert!(stats.is_ok());
if let Ok(stats) = stats {
assert_eq!(stats.active_sessions, 0);
assert_eq!(stats.successful_coordinations, 0);
assert!(stats.average_coordination_time > Duration::ZERO);
}
}
}
}
#[cfg(test)]
mod performance_tests {
use super::*;
#[test]
#[ignore = "performance test"]
fn bench_peer_id_operations() {
use std::collections::HashMap;
let start = std::time::Instant::now();
let mut peer_map = HashMap::new();
for i in 0..10000 {
let peer_id = create_test_peer_id(i as u8);
peer_map.insert(peer_id, i);
}
for i in 0..1000 {
let peer_id = create_test_peer_id(i as u8);
let _value = peer_map.get(&peer_id);
}
let duration = start.elapsed();
println!("Created and looked up peer IDs in {duration:?}");
assert!(duration < Duration::from_millis(100));
}
#[test]
#[ignore = "performance test"]
fn bench_configuration_creation() {
let start = std::time::Instant::now();
for i in 0..1000 {
let config = NatTraversalConfig {
known_peers: vec![SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(203, 0, 113, i as u8)),
8080,
)],
max_candidates: i as usize % 32 + 1,
coordination_timeout: Duration::from_secs(i as u64 % 60 + 1),
enable_symmetric_nat: i % 2 == 0,
enable_relay_fallback: i % 3 == 0,
max_concurrent_attempts: i as usize % 10 + 1,
bind_addr: None,
prefer_rfc_nat_traversal: false,
pqc: None,
timeouts: TimeoutConfig::default(),
identity_key: None,
relay_nodes: vec![],
enable_relay_service: true,
allow_ipv4_mapped: true,
transport_registry: None,
max_message_size: ant_quic::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE,
max_concurrent_uni_streams: 100,
additional_bind_addrs: Vec::new(),
};
assert!(config.max_candidates > 0);
}
let duration = start.elapsed();
println!("Created configurations in {duration:?}");
assert!(duration < Duration::from_millis(50));
}
}
#[cfg(test)]
mod relay_functionality_tests {
use super::*;
#[test]
fn test_multiple_known_peers() {
let known_peer_addrs = vec![
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 1)), 8080),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 2)), 8080),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 3)), 8080),
];
let config = NatTraversalConfig {
known_peers: known_peer_addrs.clone(),
..NatTraversalConfig::default()
};
assert_eq!(config.known_peers.len(), 3);
for (i, addr) in config.known_peers.iter().enumerate() {
assert_eq!(*addr, known_peer_addrs[i]);
}
}
#[tokio::test]
async fn test_invalid_configuration_scenarios() {
let no_peers_config = NatTraversalConfig {
known_peers: vec![],
bind_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
..NatTraversalConfig::default()
};
let result = NatTraversalEndpoint::new(no_peers_config, None, None).await;
let _ = result;
let zero_values_config = NatTraversalConfig {
known_peers: vec![],
max_candidates: 0,
coordination_timeout: Duration::ZERO,
enable_symmetric_nat: true,
enable_relay_fallback: true,
max_concurrent_attempts: 0,
bind_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
prefer_rfc_nat_traversal: false,
pqc: None,
timeouts: TimeoutConfig::default(),
identity_key: None,
relay_nodes: vec![],
enable_relay_service: true,
allow_ipv4_mapped: true,
transport_registry: None,
max_message_size: ant_quic::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE,
max_concurrent_uni_streams: 100,
additional_bind_addrs: Vec::new(),
};
let _result = NatTraversalEndpoint::new(zero_values_config, None, None).await;
}
}