#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::testnet::{
DEFAULT_BOOTSTRAP_COUNT, DEFAULT_NODE_COUNT, MINIMAL_BOOTSTRAP_COUNT, MINIMAL_NODE_COUNT,
SMALL_NODE_COUNT, TEST_PORT_RANGE_MAX, TEST_PORT_RANGE_MIN,
};
use super::{NetworkState, TestHarness, TestNetwork, TestNetworkConfig};
use saorsa_core::P2PEvent;
use serial_test::serial;
use std::time::Duration;
#[tokio::test]
async fn test_minimal_network_formation() {
let harness = TestHarness::setup_minimal()
.await
.expect("Failed to setup harness");
assert!(harness.is_ready().await);
assert_eq!(harness.node_count(), MINIMAL_NODE_COUNT);
let total_connections = harness.total_connections().await;
assert!(
total_connections > 0,
"Should have at least some connections"
);
harness.teardown().await.expect("Failed to teardown");
}
#[tokio::test]
async fn test_small_network_formation() {
let harness = TestHarness::setup_small()
.await
.expect("Failed to setup harness");
assert!(harness.is_ready().await);
assert_eq!(harness.node_count(), SMALL_NODE_COUNT);
for i in 0..SMALL_NODE_COUNT {
assert!(harness.node(i).is_some(), "Node {i} should be accessible");
}
harness.teardown().await.expect("Failed to teardown");
}
#[tokio::test]
async fn test_full_network_formation() {
let harness = TestHarness::setup().await.expect("Failed to setup harness");
assert!(harness.is_ready().await);
assert_eq!(harness.node_count(), DEFAULT_NODE_COUNT);
let network = harness.network();
assert_eq!(network.bootstrap_nodes().len(), DEFAULT_BOOTSTRAP_COUNT);
let expected_regular = DEFAULT_NODE_COUNT - DEFAULT_BOOTSTRAP_COUNT;
assert_eq!(network.regular_nodes().len(), expected_regular);
assert!(harness.random_node().is_some());
assert!(harness.random_bootstrap_node().is_some());
harness.teardown().await.expect("Failed to teardown");
}
#[tokio::test]
async fn test_custom_network_config() {
let config = TestNetworkConfig {
node_count: 7,
bootstrap_count: 2,
spawn_delay: Duration::from_millis(100),
stabilization_timeout: Duration::from_secs(60),
..Default::default()
};
let harness = TestHarness::setup_with_config(config)
.await
.expect("Failed to setup harness");
assert_eq!(harness.node_count(), 7);
assert_eq!(harness.network().bootstrap_nodes().len(), 2);
assert_eq!(harness.network().regular_nodes().len(), 5);
harness.teardown().await.expect("Failed to teardown");
}
#[tokio::test]
#[serial]
async fn test_network_with_evm() {
let harness = TestHarness::setup_with_evm()
.await
.expect("Failed to setup harness with EVM");
assert!(harness.has_evm());
let anvil = harness.anvil().expect("Anvil should be present");
let _network = anvil.to_network();
assert!(!anvil.default_wallet_key().expect("wallet key").is_empty());
harness.teardown().await.expect("Failed to teardown");
}
#[tokio::test]
async fn test_network_config_validation() {
let config = TestNetworkConfig {
node_count: 5,
bootstrap_count: 5,
..Default::default()
};
let result = TestNetwork::new(config).await;
assert!(result.is_err());
let config = TestNetworkConfig {
node_count: 5,
bootstrap_count: 0,
..Default::default()
};
let result = TestNetwork::new(config).await;
assert!(result.is_err());
}
#[test]
fn test_network_state() {
assert!(!NetworkState::Uninitialized.is_running());
assert!(!NetworkState::BootstrappingPhase.is_running());
assert!(!NetworkState::NodeSpawningPhase.is_running());
assert!(NetworkState::Stabilizing.is_running());
assert!(NetworkState::Ready.is_running());
assert!(!NetworkState::ShuttingDown.is_running());
assert!(!NetworkState::Stopped.is_running());
assert!(!NetworkState::Failed("error".to_string()).is_running());
}
#[test]
fn test_config_presets() {
let default = TestNetworkConfig::default();
assert_eq!(default.node_count, DEFAULT_NODE_COUNT);
assert_eq!(default.bootstrap_count, DEFAULT_BOOTSTRAP_COUNT);
assert!(default.base_port >= TEST_PORT_RANGE_MIN && default.base_port < TEST_PORT_RANGE_MAX);
let minimal = TestNetworkConfig::minimal();
assert_eq!(minimal.node_count, MINIMAL_NODE_COUNT);
assert_eq!(minimal.bootstrap_count, MINIMAL_BOOTSTRAP_COUNT);
let small = TestNetworkConfig::small();
assert_eq!(small.node_count, SMALL_NODE_COUNT);
assert_eq!(small.bootstrap_count, DEFAULT_BOOTSTRAP_COUNT);
assert_ne!(default.test_data_dir, minimal.test_data_dir);
assert_ne!(minimal.test_data_dir, small.test_data_dir);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_node_to_node_messaging() {
const TEST_TOPIC: &str = "test/ping/v1";
const PAYLOAD: &[u8] = b"hello from node 3";
const DELIVERY_TIMEOUT_SECS: u64 = 10;
let harness = TestHarness::setup_minimal()
.await
.expect("Failed to setup test harness");
let all_nodes = harness.all_nodes();
let mut all_rx: Vec<_> = all_nodes.iter().map(|n| n.subscribe_events()).collect();
let sender = harness.test_node(3).expect("Node 3 should exist");
let peers = sender.connected_peers().await;
assert!(
!peers.is_empty(),
"Node 3 should have at least one connected peer"
);
let target_peer_id = peers[0];
let sender_p2p = sender.p2p_node.as_ref().expect("Node 3 should be running");
sender_p2p
.send_message(&target_peer_id, TEST_TOPIC, PAYLOAD.to_vec(), &[])
.await
.expect("Failed to send message to connected peer");
let mut received = false;
let deadline = tokio::time::Instant::now() + Duration::from_secs(DELIVERY_TIMEOUT_SECS);
let timeout = tokio::time::sleep_until(deadline);
tokio::pin!(timeout);
while !received {
let futs: Vec<_> = all_rx
.iter_mut()
.map(|rx| Box::pin(async move { rx.recv().await }))
.collect();
tokio::select! {
(result, _idx, _remaining) = futures::future::select_all(futs) => {
if let Ok(P2PEvent::Message { ref topic, ref data, .. }) = result {
if topic == TEST_TOPIC && data.as_slice() == PAYLOAD {
received = true;
}
}
}
() = &mut timeout => {
break;
}
}
}
assert!(
received,
"No node received the message on topic '{TEST_TOPIC}'"
);
drop(all_rx);
drop(all_nodes);
harness
.teardown()
.await
.expect("Failed to teardown test harness");
}