aboutsummaryrefslogtreecommitdiffstats
path: root/tests/scale.rs
diff options
context:
space:
mode:
authormurilo ijanc2026-03-24 15:04:03 -0300
committermurilo ijanc2026-03-24 15:04:03 -0300
commit9821aabf0b50d2487b07502d3d2cd89e7d62bdbe (patch)
tree53da095ff90cc755bac3d4bf699172b5e8cd07d6 /tests/scale.rs
downloadtesseras-dht-e908bc01403f4b8ef2a65fa6be43716fd1c6e003.tar.gz
Initial commitv0.1.0
NAT-aware Kademlia DHT library for peer-to-peer networks. Features: - Distributed key-value storage (iterative FIND_NODE, FIND_VALUE, STORE) - NAT traversal via DTUN hole-punching and proxy relay - Reliable Datagram Protocol (RDP) with 7-state connection machine - Datagram transport with automatic fragmentation/reassembly - Ed25519 packet authentication - 256-bit node IDs (Ed25519 public keys) - Rate limiting, ban list, and eclipse attack mitigation - Persistence and metrics - OpenBSD and Linux support
Diffstat (limited to 'tests/scale.rs')
-rw-r--r--tests/scale.rs138
1 files changed, 138 insertions, 0 deletions
diff --git a/tests/scale.rs b/tests/scale.rs
new file mode 100644
index 0000000..b518385
--- /dev/null
+++ b/tests/scale.rs
@@ -0,0 +1,138 @@
+//! Scale test: 20 nodes with distributed put/get.
+//!
+//! Runs 20 nodes with distributed put/get to verify
+//! correctness at scale.
+
+use std::time::Duration;
+use tesseras_dht::Node;
+use tesseras_dht::nat::NatState;
+
+fn poll_all(nodes: &mut [Node], rounds: usize) {
+ let fast = Duration::from_millis(1);
+ for _ in 0..rounds {
+ for n in nodes.iter_mut() {
+ n.poll_timeout(fast).ok();
+ }
+ }
+}
+
+fn make_network(n: usize) -> Vec<Node> {
+ let mut nodes = Vec::with_capacity(n);
+ let bootstrap = Node::bind(0).unwrap();
+ let bp = bootstrap.local_addr().unwrap().port();
+ nodes.push(bootstrap);
+ nodes[0].set_nat_state(NatState::Global);
+
+ for _ in 1..n {
+ let mut node = Node::bind(0).unwrap();
+ node.set_nat_state(NatState::Global);
+ node.join("127.0.0.1", bp).unwrap();
+ nodes.push(node);
+ }
+
+ std::thread::sleep(Duration::from_millis(100));
+ poll_all(&mut nodes, 10);
+ nodes
+}
+
+#[test]
+fn twenty_nodes_routing() {
+ let nodes = make_network(20);
+
+ // Every node should have at least 1 peer
+ for (i, node) in nodes.iter().enumerate() {
+ assert!(
+ node.routing_table_size() >= 1,
+ "Node {i} has empty routing table"
+ );
+ }
+
+ // Average routing table should be > 5
+ let total: usize = nodes.iter().map(|n| n.routing_table_size()).sum();
+ let avg = total / nodes.len();
+ assert!(avg >= 5, "Average routing table {avg} too low");
+}
+
+#[test]
+fn twenty_nodes_put_get() {
+ let mut nodes = make_network(20);
+
+ // Each node stores one value
+ for i in 0..20u32 {
+ let key = format!("scale-key-{i}");
+ let val = format!("scale-val-{i}");
+ nodes[i as usize].put(key.as_bytes(), val.as_bytes(), 300, false);
+ }
+
+ // Poll to distribute
+ std::thread::sleep(Duration::from_millis(100));
+ poll_all(&mut nodes, 10);
+
+ // Each node should have its own value
+ for i in 0..20u32 {
+ let key = format!("scale-key-{i}");
+ let vals = nodes[i as usize].get(key.as_bytes());
+ assert!(!vals.is_empty(), "Node {i} lost its own value");
+ }
+
+ // Count total stored values across network
+ let total_stored: usize = nodes.iter().map(|n| n.storage_count()).sum();
+ assert!(
+ total_stored >= 20,
+ "Total stored {total_stored} should be >= 20"
+ );
+}
+
+#[test]
+#[ignore] // timing-sensitive, consumes 100% CPU with 20 nodes polling
+fn twenty_nodes_remote_get() {
+ let mut nodes = make_network(20);
+
+ // Node 0 stores a value
+ nodes[0].put(b"find-me", b"found", 300, false);
+
+ std::thread::sleep(Duration::from_millis(100));
+ poll_all(&mut nodes, 10);
+
+ // Node 19 tries to get it — trigger FIND_VALUE
+ let _ = nodes[19].get(b"find-me");
+
+ // Poll all nodes to let FIND_VALUE propagate
+ for _ in 0..40 {
+ poll_all(&mut nodes, 5);
+ std::thread::sleep(Duration::from_millis(30));
+
+ let vals = nodes[19].get(b"find-me");
+ if !vals.is_empty() {
+ assert_eq!(vals[0], b"found");
+ return;
+ }
+ }
+ panic!("Node 19 should find the value via FIND_VALUE");
+}
+
+#[test]
+fn twenty_nodes_multiple_puts() {
+ let mut nodes = make_network(20);
+
+ // 5 nodes store 10 values each
+ for n in 0..5 {
+ for k in 0..10u32 {
+ let key = format!("n{n}-k{k}");
+ let val = format!("n{n}-v{k}");
+ nodes[n].put(key.as_bytes(), val.as_bytes(), 300, false);
+ }
+ }
+
+ std::thread::sleep(Duration::from_millis(100));
+ poll_all(&mut nodes, 10);
+
+ // Verify origin nodes have their values
+ for n in 0..5 {
+ for k in 0..10u32 {
+ let key = format!("n{n}-k{k}");
+ let vals = nodes[n].get(key.as_bytes());
+ assert!(!vals.is_empty(), "Node {n} lost key n{n}-k{k}");
+ }
+ }
+}