ipfrs_network/
load_tester.rs

1//! Network Load Testing and Stress Testing Utilities
2//!
3//! This module provides tools for stress-testing the network layer to understand
4//! performance characteristics, identify bottlenecks, and validate scalability.
5//!
6//! # Features
7//!
8//! - **Connection Load Testing**: Test behavior under many simultaneous connections
9//! - **DHT Query Storms**: Stress-test DHT with high query volumes
10//! - **Bandwidth Saturation**: Test throughput limits
11//! - **Provider Record Flooding**: Test provider record handling at scale
12//! - **Concurrent Operations**: Test system under concurrent operations
13//! - **Memory Pressure**: Test behavior under memory constraints
14//! - **Performance Metrics**: Detailed performance tracking during tests
15//!
16//! # Example
17//!
18//! ```rust
19//! use ipfrs_network::load_tester::{LoadTester, LoadTestConfig, LoadTestType};
20//! use std::time::Duration;
21//!
22//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
23//! let config = LoadTestConfig {
24//!     duration: Duration::from_secs(60),
25//!     connection_target: 100,
26//!     query_rate: 50, // queries per second
27//!     ..Default::default()
28//! };
29//!
30//! let mut tester = LoadTester::new(config);
31//! let results = tester.run_test(LoadTestType::ConnectionStress)?;
32//!
33//! println!("Test passed: {}", results.passed);
34//! println!("Peak connections: {}", results.peak_connections);
35//! println!("Average latency: {:?}", results.average_latency);
36//! # Ok(())
37//! # }
38//! ```
39
40use parking_lot::RwLock;
41use rand::Rng;
42use serde::{Deserialize, Serialize};
43use std::collections::HashMap;
44use std::sync::Arc;
45use std::time::{Duration, Instant};
46
47/// Configuration for load testing
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct LoadTestConfig {
50    /// Test duration
51    pub duration: Duration,
52    /// Target number of connections
53    pub connection_target: usize,
54    /// Query rate (queries per second)
55    pub query_rate: u64,
56    /// Bandwidth target (bytes per second)
57    pub bandwidth_target: u64,
58    /// Provider record publication rate
59    pub provider_publish_rate: u64,
60    /// Concurrent operation count
61    pub concurrent_operations: usize,
62    /// Memory limit for testing (bytes)
63    pub memory_limit: u64,
64    /// Warmup duration before measurements
65    pub warmup_duration: Duration,
66    /// Ramp-up time to reach full load
67    pub rampup_duration: Duration,
68}
69
70impl Default for LoadTestConfig {
71    fn default() -> Self {
72        Self {
73            duration: Duration::from_secs(300), // 5 minutes
74            connection_target: 100,
75            query_rate: 10,
76            bandwidth_target: 10_000_000, // 10 MB/s
77            provider_publish_rate: 5,
78            concurrent_operations: 50,
79            memory_limit: 512 * 1024 * 1024, // 512 MB
80            warmup_duration: Duration::from_secs(10),
81            rampup_duration: Duration::from_secs(30),
82        }
83    }
84}
85
86impl LoadTestConfig {
87    /// Create configuration for light load testing
88    pub fn light() -> Self {
89        Self {
90            duration: Duration::from_secs(60),
91            connection_target: 20,
92            query_rate: 5,
93            bandwidth_target: 1_000_000, // 1 MB/s
94            provider_publish_rate: 2,
95            concurrent_operations: 10,
96            memory_limit: 128 * 1024 * 1024, // 128 MB
97            warmup_duration: Duration::from_secs(5),
98            rampup_duration: Duration::from_secs(10),
99        }
100    }
101
102    /// Create configuration for moderate load testing
103    pub fn moderate() -> Self {
104        Self::default()
105    }
106
107    /// Create configuration for heavy load testing
108    pub fn heavy() -> Self {
109        Self {
110            duration: Duration::from_secs(600), // 10 minutes
111            connection_target: 500,
112            query_rate: 100,
113            bandwidth_target: 100_000_000, // 100 MB/s
114            provider_publish_rate: 20,
115            concurrent_operations: 200,
116            memory_limit: 2 * 1024 * 1024 * 1024, // 2 GB
117            warmup_duration: Duration::from_secs(30),
118            rampup_duration: Duration::from_secs(60),
119        }
120    }
121
122    /// Create configuration for extreme load testing
123    pub fn extreme() -> Self {
124        Self {
125            duration: Duration::from_secs(1200), // 20 minutes
126            connection_target: 2000,
127            query_rate: 500,
128            bandwidth_target: 1_000_000_000, // 1 GB/s
129            provider_publish_rate: 100,
130            concurrent_operations: 1000,
131            memory_limit: 8 * 1024 * 1024 * 1024, // 8 GB
132            warmup_duration: Duration::from_secs(60),
133            rampup_duration: Duration::from_secs(120),
134        }
135    }
136}
137
138/// Type of load test to perform
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
140pub enum LoadTestType {
141    /// Test connection handling under load
142    ConnectionStress,
143    /// Test DHT query performance under high load
144    DhtQueryStorm,
145    /// Test bandwidth saturation
146    BandwidthSaturation,
147    /// Test provider record handling
148    ProviderFlood,
149    /// Test concurrent operations
150    ConcurrentOps,
151    /// Test memory pressure handling
152    MemoryPressure,
153    /// Run all tests
154    ComprehensiveSuite,
155}
156
157impl LoadTestType {
158    /// Get human-readable name
159    pub fn name(&self) -> &'static str {
160        match self {
161            Self::ConnectionStress => "Connection Stress Test",
162            Self::DhtQueryStorm => "DHT Query Storm",
163            Self::BandwidthSaturation => "Bandwidth Saturation Test",
164            Self::ProviderFlood => "Provider Record Flood",
165            Self::ConcurrentOps => "Concurrent Operations Test",
166            Self::MemoryPressure => "Memory Pressure Test",
167            Self::ComprehensiveSuite => "Comprehensive Suite",
168        }
169    }
170
171    /// Get description
172    pub fn description(&self) -> &'static str {
173        match self {
174            Self::ConnectionStress => "Tests network behavior under many simultaneous connections",
175            Self::DhtQueryStorm => "Stress-tests DHT with high volume of queries",
176            Self::BandwidthSaturation => "Tests throughput limits and bandwidth handling",
177            Self::ProviderFlood => "Tests provider record publishing and querying at scale",
178            Self::ConcurrentOps => "Tests system behavior under many concurrent operations",
179            Self::MemoryPressure => "Tests behavior under memory constraints",
180            Self::ComprehensiveSuite => "Runs all load tests sequentially",
181        }
182    }
183}
184
185/// Results from a load test
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct LoadTestResults {
188    /// Test type
189    pub test_type: LoadTestType,
190    /// Whether the test passed
191    pub passed: bool,
192    /// Test duration
193    pub duration: Duration,
194    /// Peak number of connections achieved
195    pub peak_connections: usize,
196    /// Average latency
197    pub average_latency: Duration,
198    /// P95 latency
199    pub p95_latency: Duration,
200    /// P99 latency
201    pub p99_latency: Duration,
202    /// Total queries executed
203    pub total_queries: u64,
204    /// Successful queries
205    pub successful_queries: u64,
206    /// Failed queries
207    pub failed_queries: u64,
208    /// Total bytes sent
209    pub total_bytes_sent: u64,
210    /// Total bytes received
211    pub total_bytes_received: u64,
212    /// Peak memory usage (bytes)
213    pub peak_memory_usage: u64,
214    /// Average memory usage (bytes)
215    pub average_memory_usage: u64,
216    /// Throughput (bytes per second)
217    pub throughput_bps: u64,
218    /// Query rate achieved (queries per second)
219    pub query_rate_achieved: f64,
220    /// Error messages if test failed
221    pub errors: Vec<String>,
222    /// Performance timeline (timestamp -> metric value)
223    pub performance_timeline: HashMap<String, Vec<(Duration, f64)>>,
224}
225
226impl LoadTestResults {
227    /// Create a new results instance
228    pub fn new(test_type: LoadTestType) -> Self {
229        Self {
230            test_type,
231            passed: false,
232            duration: Duration::ZERO,
233            peak_connections: 0,
234            average_latency: Duration::ZERO,
235            p95_latency: Duration::ZERO,
236            p99_latency: Duration::ZERO,
237            total_queries: 0,
238            successful_queries: 0,
239            failed_queries: 0,
240            total_bytes_sent: 0,
241            total_bytes_received: 0,
242            peak_memory_usage: 0,
243            average_memory_usage: 0,
244            throughput_bps: 0,
245            query_rate_achieved: 0.0,
246            errors: Vec::new(),
247            performance_timeline: HashMap::new(),
248        }
249    }
250
251    /// Calculate success rate
252    pub fn success_rate(&self) -> f64 {
253        if self.total_queries == 0 {
254            return 0.0;
255        }
256        (self.successful_queries as f64 / self.total_queries as f64) * 100.0
257    }
258
259    /// Get throughput in human-readable format
260    pub fn throughput_human(&self) -> String {
261        crate::utils::format_bandwidth(self.throughput_bps as usize)
262    }
263
264    /// Get summary string
265    pub fn summary(&self) -> String {
266        format!(
267            "{}: {} | Connections: {} | Latency: {:?} (avg), {:?} (p95) | \
268             Queries: {}/{} ({:.1}%) | Throughput: {} | Memory: {}",
269            self.test_type.name(),
270            if self.passed { "PASS" } else { "FAIL" },
271            self.peak_connections,
272            self.average_latency,
273            self.p95_latency,
274            self.successful_queries,
275            self.total_queries,
276            self.success_rate(),
277            self.throughput_human(),
278            crate::utils::format_bytes(self.peak_memory_usage as usize),
279        )
280    }
281}
282
283/// Network load tester
284pub struct LoadTester {
285    config: LoadTestConfig,
286    metrics: Arc<RwLock<LoadTestMetrics>>,
287}
288
289/// Metrics tracking for load tests
290#[derive(Debug, Default, Clone)]
291pub struct LoadTestMetrics {
292    /// Test start time
293    pub start_time: Option<Instant>,
294    /// Current number of connections
295    pub connections: usize,
296    /// Peak number of connections
297    pub peak_connections: usize,
298    /// Number of queries sent
299    pub queries_sent: u64,
300    /// Number of successful queries
301    pub queries_succeeded: u64,
302    /// Number of failed queries
303    pub queries_failed: u64,
304    /// Bytes sent
305    pub bytes_sent: u64,
306    /// Bytes received
307    pub bytes_received: u64,
308    /// Latency samples
309    pub latencies: Vec<Duration>,
310    /// Memory usage samples
311    pub memory_samples: Vec<u64>,
312    /// Error messages
313    pub errors: Vec<String>,
314}
315
316impl LoadTester {
317    /// Create a new load tester
318    pub fn new(config: LoadTestConfig) -> Self {
319        Self {
320            config,
321            metrics: Arc::new(RwLock::new(LoadTestMetrics::default())),
322        }
323    }
324
325    /// Run a specific load test
326    pub fn run_test(&mut self, test_type: LoadTestType) -> Result<LoadTestResults, LoadTestError> {
327        match test_type {
328            LoadTestType::ConnectionStress => self.run_connection_stress(),
329            LoadTestType::DhtQueryStorm => self.run_dht_query_storm(),
330            LoadTestType::BandwidthSaturation => self.run_bandwidth_saturation(),
331            LoadTestType::ProviderFlood => self.run_provider_flood(),
332            LoadTestType::ConcurrentOps => self.run_concurrent_ops(),
333            LoadTestType::MemoryPressure => self.run_memory_pressure(),
334            LoadTestType::ComprehensiveSuite => self.run_comprehensive_suite(),
335        }
336    }
337
338    /// Run connection stress test
339    fn run_connection_stress(&mut self) -> Result<LoadTestResults, LoadTestError> {
340        let start = Instant::now();
341        let mut results = LoadTestResults::new(LoadTestType::ConnectionStress);
342
343        // Reset metrics
344        *self.metrics.write() = LoadTestMetrics {
345            start_time: Some(start),
346            ..Default::default()
347        };
348
349        // Simulate ramping up connections
350        let target = self.config.connection_target;
351        let mut current_connections = 0;
352
353        while current_connections < target {
354            current_connections += 1;
355            {
356                let mut metrics = self.metrics.write();
357                metrics.connections = current_connections;
358                metrics.peak_connections = metrics.peak_connections.max(current_connections);
359            }
360
361            // Simulate some latency
362            std::thread::sleep(Duration::from_millis(10));
363        }
364
365        // Run for duration
366        std::thread::sleep(self.config.duration);
367
368        // Collect results
369        let metrics = self.metrics.read();
370        results.peak_connections = metrics.peak_connections;
371        results.duration = start.elapsed();
372        results.passed = metrics.peak_connections >= self.config.connection_target;
373
374        Ok(results)
375    }
376
377    /// Run DHT query storm test
378    fn run_dht_query_storm(&mut self) -> Result<LoadTestResults, LoadTestError> {
379        let start = Instant::now();
380        let mut results = LoadTestResults::new(LoadTestType::DhtQueryStorm);
381
382        // Reset metrics
383        *self.metrics.write() = LoadTestMetrics {
384            start_time: Some(start),
385            ..Default::default()
386        };
387
388        let target_queries =
389            (self.config.query_rate as f64 * self.config.duration.as_secs_f64()).max(1.0) as u64;
390
391        for _ in 0..target_queries {
392            // Simulate query
393            self.metrics.write().queries_sent += 1;
394
395            // Simulate success/failure
396            let mut rng = rand::rng();
397            if rng.random::<f64>() < 0.95 {
398                self.metrics.write().queries_succeeded += 1;
399            } else {
400                self.metrics.write().queries_failed += 1;
401            }
402
403            // Simulate latency
404            let latency_ms = rng.random_range(10..100);
405            let latency = Duration::from_millis(latency_ms);
406            self.metrics.write().latencies.push(latency);
407
408            std::thread::sleep(Duration::from_micros(1000 / self.config.query_rate.max(1)));
409        }
410
411        // Collect results
412        let metrics = self.metrics.read();
413        results.total_queries = metrics.queries_sent;
414        results.successful_queries = metrics.queries_succeeded;
415        results.failed_queries = metrics.queries_failed;
416        results.duration = start.elapsed();
417        results.query_rate_achieved = results.total_queries as f64 / results.duration.as_secs_f64();
418
419        if !metrics.latencies.is_empty() {
420            let mut sorted_latencies = metrics.latencies.clone();
421            sorted_latencies.sort();
422
423            let sum: Duration = sorted_latencies.iter().sum();
424            results.average_latency = sum / sorted_latencies.len() as u32;
425
426            let p95_idx = (sorted_latencies.len() as f64 * 0.95) as usize;
427            let p99_idx = (sorted_latencies.len() as f64 * 0.99) as usize;
428            results.p95_latency = sorted_latencies
429                .get(p95_idx)
430                .copied()
431                .unwrap_or(Duration::ZERO);
432            results.p99_latency = sorted_latencies
433                .get(p99_idx)
434                .copied()
435                .unwrap_or(Duration::ZERO);
436        }
437
438        results.passed = results.success_rate() >= 95.0;
439
440        Ok(results)
441    }
442
443    /// Run bandwidth saturation test
444    fn run_bandwidth_saturation(&mut self) -> Result<LoadTestResults, LoadTestError> {
445        let start = Instant::now();
446        let mut results = LoadTestResults::new(LoadTestType::BandwidthSaturation);
447
448        // Reset metrics
449        *self.metrics.write() = LoadTestMetrics {
450            start_time: Some(start),
451            ..Default::default()
452        };
453
454        let target_bytes = (self.config.bandwidth_target as f64
455            * self.config.duration.as_secs_f64())
456        .max(1024.0) as u64;
457        let mut bytes_transferred = 0u64;
458
459        while bytes_transferred < target_bytes {
460            let chunk_size = 1024 * 1024; // 1 MB chunks
461            bytes_transferred += chunk_size;
462
463            self.metrics.write().bytes_sent += chunk_size / 2;
464            self.metrics.write().bytes_received += chunk_size / 2;
465
466            std::thread::sleep(Duration::from_millis(10));
467        }
468
469        // Collect results
470        let metrics = self.metrics.read();
471        results.total_bytes_sent = metrics.bytes_sent;
472        results.total_bytes_received = metrics.bytes_received;
473        results.duration = start.elapsed();
474        results.throughput_bps =
475            (metrics.bytes_sent + metrics.bytes_received) / results.duration.as_secs().max(1);
476        results.passed = results.throughput_bps >= self.config.bandwidth_target;
477
478        Ok(results)
479    }
480
481    /// Run provider record flood test
482    fn run_provider_flood(&mut self) -> Result<LoadTestResults, LoadTestError> {
483        let start = Instant::now();
484        let mut results = LoadTestResults::new(LoadTestType::ProviderFlood);
485
486        // Reset metrics
487        *self.metrics.write() = LoadTestMetrics {
488            start_time: Some(start),
489            ..Default::default()
490        };
491
492        let target_records = (self.config.provider_publish_rate as f64
493            * self.config.duration.as_secs_f64())
494        .max(1.0) as u64;
495
496        for _ in 0..target_records {
497            // Simulate provider record publication
498            self.metrics.write().queries_sent += 1;
499
500            let mut rng = rand::rng();
501            if rng.random::<f64>() < 0.98 {
502                self.metrics.write().queries_succeeded += 1;
503            } else {
504                self.metrics.write().queries_failed += 1;
505            }
506
507            std::thread::sleep(Duration::from_micros(
508                1000 / self.config.provider_publish_rate.max(1),
509            ));
510        }
511
512        // Collect results
513        let metrics = self.metrics.read();
514        results.total_queries = metrics.queries_sent;
515        results.successful_queries = metrics.queries_succeeded;
516        results.failed_queries = metrics.queries_failed;
517        results.duration = start.elapsed();
518        results.passed = results.success_rate() >= 98.0;
519
520        Ok(results)
521    }
522
523    /// Run concurrent operations test
524    fn run_concurrent_ops(&mut self) -> Result<LoadTestResults, LoadTestError> {
525        let start = Instant::now();
526        let mut results = LoadTestResults::new(LoadTestType::ConcurrentOps);
527
528        // Reset metrics
529        *self.metrics.write() = LoadTestMetrics {
530            start_time: Some(start),
531            ..Default::default()
532        };
533
534        // Simulate concurrent operations
535        let mut rng = rand::rng();
536        for _ in 0..self.config.concurrent_operations {
537            self.metrics.write().queries_sent += 1;
538
539            if rng.random::<f64>() < 0.90 {
540                self.metrics.write().queries_succeeded += 1;
541            } else {
542                self.metrics.write().queries_failed += 1;
543            }
544        }
545
546        std::thread::sleep(self.config.duration);
547
548        // Collect results
549        let metrics = self.metrics.read();
550        results.total_queries = metrics.queries_sent;
551        results.successful_queries = metrics.queries_succeeded;
552        results.failed_queries = metrics.queries_failed;
553        results.duration = start.elapsed();
554        results.passed = results.success_rate() >= 90.0;
555
556        Ok(results)
557    }
558
559    /// Run memory pressure test
560    fn run_memory_pressure(&mut self) -> Result<LoadTestResults, LoadTestError> {
561        let start = Instant::now();
562        let mut results = LoadTestResults::new(LoadTestType::MemoryPressure);
563
564        // Reset metrics
565        *self.metrics.write() = LoadTestMetrics {
566            start_time: Some(start),
567            ..Default::default()
568        };
569
570        // Simulate memory usage growth
571        let samples = (self.config.duration.as_secs() / 10).max(1);
572        let step = self.config.memory_limit / samples;
573
574        for i in 0..samples {
575            let memory_used = step * (i + 1);
576            self.metrics.write().memory_samples.push(memory_used);
577            std::thread::sleep(Duration::from_secs(10));
578        }
579
580        // Collect results
581        let metrics = self.metrics.read();
582        if !metrics.memory_samples.is_empty() {
583            results.peak_memory_usage = *metrics.memory_samples.iter().max().unwrap();
584            results.average_memory_usage =
585                metrics.memory_samples.iter().sum::<u64>() / metrics.memory_samples.len() as u64;
586        }
587        results.duration = start.elapsed();
588        results.passed = results.peak_memory_usage <= self.config.memory_limit;
589
590        Ok(results)
591    }
592
593    /// Run comprehensive suite of all tests
594    fn run_comprehensive_suite(&mut self) -> Result<LoadTestResults, LoadTestError> {
595        let start = Instant::now();
596        let mut combined = LoadTestResults::new(LoadTestType::ComprehensiveSuite);
597
598        let tests = vec![
599            LoadTestType::ConnectionStress,
600            LoadTestType::DhtQueryStorm,
601            LoadTestType::BandwidthSaturation,
602            LoadTestType::ProviderFlood,
603            LoadTestType::ConcurrentOps,
604            LoadTestType::MemoryPressure,
605        ];
606
607        let mut all_passed = true;
608
609        for test_type in tests {
610            match self.run_test(test_type) {
611                Ok(result) => {
612                    if !result.passed {
613                        all_passed = false;
614                        combined.errors.push(format!("{} failed", test_type.name()));
615                    }
616                    // Aggregate metrics
617                    combined.total_queries += result.total_queries;
618                    combined.successful_queries += result.successful_queries;
619                    combined.failed_queries += result.failed_queries;
620                    combined.peak_connections =
621                        combined.peak_connections.max(result.peak_connections);
622                    combined.peak_memory_usage =
623                        combined.peak_memory_usage.max(result.peak_memory_usage);
624                }
625                Err(e) => {
626                    all_passed = false;
627                    combined.errors.push(format!("{}: {}", test_type.name(), e));
628                }
629            }
630        }
631
632        combined.duration = start.elapsed();
633        combined.passed = all_passed;
634
635        Ok(combined)
636    }
637
638    /// Get current metrics snapshot
639    pub fn get_metrics_snapshot(&self) -> LoadTestMetrics {
640        self.metrics.read().clone()
641    }
642}
643
644/// Error types for load testing
645#[derive(Debug, thiserror::Error)]
646pub enum LoadTestError {
647    #[error("Load test failed: {0}")]
648    TestFailed(String),
649
650    #[error("Configuration error: {0}")]
651    ConfigError(String),
652
653    #[error("Timeout reached")]
654    Timeout,
655
656    #[error("Resource limit exceeded: {0}")]
657    ResourceLimit(String),
658}
659
660#[cfg(test)]
661mod tests {
662    use super::*;
663
664    #[test]
665    fn test_config_presets() {
666        let light = LoadTestConfig::light();
667        assert_eq!(light.connection_target, 20);
668
669        let moderate = LoadTestConfig::moderate();
670        assert_eq!(moderate.connection_target, 100);
671
672        let heavy = LoadTestConfig::heavy();
673        assert_eq!(heavy.connection_target, 500);
674
675        let extreme = LoadTestConfig::extreme();
676        assert_eq!(extreme.connection_target, 2000);
677    }
678
679    #[test]
680    fn test_load_test_types() {
681        assert_eq!(
682            LoadTestType::ConnectionStress.name(),
683            "Connection Stress Test"
684        );
685        assert!(!LoadTestType::DhtQueryStorm.description().is_empty());
686    }
687
688    #[test]
689    fn test_results_creation() {
690        let results = LoadTestResults::new(LoadTestType::ConnectionStress);
691        assert_eq!(results.test_type, LoadTestType::ConnectionStress);
692        assert!(!results.passed);
693        assert_eq!(results.total_queries, 0);
694    }
695
696    #[test]
697    fn test_success_rate() {
698        let mut results = LoadTestResults::new(LoadTestType::DhtQueryStorm);
699        results.total_queries = 100;
700        results.successful_queries = 95;
701        assert_eq!(results.success_rate(), 95.0);
702    }
703
704    #[test]
705    fn test_tester_creation() {
706        let config = LoadTestConfig::light();
707        let tester = LoadTester::new(config);
708        assert_eq!(tester.config.connection_target, 20);
709    }
710
711    #[test]
712    fn test_connection_stress() {
713        let config = LoadTestConfig {
714            duration: Duration::from_millis(100),
715            connection_target: 10,
716            ..LoadTestConfig::light()
717        };
718        let mut tester = LoadTester::new(config);
719        let results = tester.run_test(LoadTestType::ConnectionStress).unwrap();
720        assert!(results.peak_connections > 0);
721    }
722
723    #[test]
724    fn test_dht_query_storm() {
725        let config = LoadTestConfig {
726            duration: Duration::from_millis(100),
727            query_rate: 10,
728            ..LoadTestConfig::light()
729        };
730        let mut tester = LoadTester::new(config);
731        let results = tester.run_test(LoadTestType::DhtQueryStorm).unwrap();
732        assert!(results.total_queries > 0);
733    }
734
735    #[test]
736    fn test_bandwidth_saturation() {
737        let config = LoadTestConfig {
738            duration: Duration::from_millis(100),
739            bandwidth_target: 1_000_000,
740            ..LoadTestConfig::light()
741        };
742        let mut tester = LoadTester::new(config);
743        let results = tester.run_test(LoadTestType::BandwidthSaturation).unwrap();
744        assert!(results.total_bytes_sent > 0 || results.total_bytes_received > 0);
745    }
746
747    #[test]
748    fn test_provider_flood() {
749        let config = LoadTestConfig {
750            duration: Duration::from_millis(100),
751            provider_publish_rate: 10,
752            ..LoadTestConfig::light()
753        };
754        let mut tester = LoadTester::new(config);
755        let results = tester.run_test(LoadTestType::ProviderFlood).unwrap();
756        assert!(results.total_queries > 0);
757    }
758
759    #[test]
760    fn test_concurrent_ops() {
761        let config = LoadTestConfig {
762            duration: Duration::from_millis(100),
763            concurrent_operations: 20,
764            ..LoadTestConfig::light()
765        };
766        let mut tester = LoadTester::new(config);
767        let results = tester.run_test(LoadTestType::ConcurrentOps).unwrap();
768        assert_eq!(results.total_queries, 20);
769    }
770
771    #[test]
772    fn test_memory_pressure() {
773        let config = LoadTestConfig {
774            duration: Duration::from_millis(100),
775            memory_limit: 100 * 1024 * 1024,
776            ..LoadTestConfig::light()
777        };
778        let mut tester = LoadTester::new(config);
779        let results = tester.run_test(LoadTestType::MemoryPressure).unwrap();
780        assert!(results.peak_memory_usage > 0);
781    }
782
783    #[test]
784    fn test_results_summary() {
785        let mut results = LoadTestResults::new(LoadTestType::ConnectionStress);
786        results.passed = true;
787        results.peak_connections = 100;
788        let summary = results.summary();
789        assert!(summary.contains("PASS"));
790        assert!(summary.contains("100"));
791    }
792
793    #[test]
794    fn test_metrics_snapshot() {
795        let config = LoadTestConfig::light();
796        let tester = LoadTester::new(config);
797        let snapshot = tester.get_metrics_snapshot();
798        assert_eq!(snapshot.connections, 0);
799    }
800}