telemt/src/tests/ip_tracker_encapsulation_ad...

115 lines
3.3 KiB
Rust

use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
use crate::ip_tracker::UserIpTracker;
fn ip_from_idx(idx: u32) -> IpAddr {
IpAddr::V4(Ipv4Addr::new(
172,
((idx >> 16) & 0xff) as u8,
((idx >> 8) & 0xff) as u8,
(idx & 0xff) as u8,
))
}
#[tokio::test]
async fn encapsulation_queue_len_helper_matches_enqueue_and_drain_lifecycle() {
let tracker = UserIpTracker::new();
let user = "encap-len-user";
for idx in 0..32 {
tracker.enqueue_cleanup(user.to_string(), ip_from_idx(idx));
}
assert_eq!(
tracker.cleanup_queue_len_for_tests(),
32,
"test helper must reflect queued cleanup entries before drain"
);
tracker.drain_cleanup_queue().await;
assert_eq!(
tracker.cleanup_queue_len_for_tests(),
0,
"cleanup queue must be empty after drain"
);
}
#[tokio::test]
async fn encapsulation_repeated_queue_poison_recovery_preserves_forward_progress() {
let tracker = UserIpTracker::new();
tracker.set_user_limit("encap-poison", 1).await;
let ip_primary = ip_from_idx(10_001);
let ip_alt = ip_from_idx(10_002);
tracker.check_and_add("encap-poison", ip_primary).await.unwrap();
for _ in 0..128 {
let queue = tracker.cleanup_queue_mutex_for_tests();
let _ = std::panic::catch_unwind(move || {
let _guard = queue.lock().unwrap();
panic!("intentional cleanup queue poison in encapsulation regression test");
});
tracker.enqueue_cleanup("encap-poison".to_string(), ip_primary);
assert!(
tracker.check_and_add("encap-poison", ip_alt).await.is_ok(),
"poison recovery must not block admission progress"
);
tracker.remove_ip("encap-poison", ip_alt).await;
tracker
.check_and_add("encap-poison", ip_primary)
.await
.unwrap();
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn encapsulation_parallel_poison_and_churn_maintains_queue_and_limit_invariants() {
let tracker = Arc::new(UserIpTracker::new());
tracker.set_user_limit("encap-stress", 4).await;
let mut tasks = Vec::new();
for worker in 0..32u32 {
let t = tracker.clone();
tasks.push(tokio::spawn(async move {
let user = "encap-stress";
let ip = ip_from_idx(20_000 + worker);
for iter in 0..64u32 {
let _ = t.check_and_add(user, ip).await;
t.enqueue_cleanup(user.to_string(), ip);
if iter % 3 == 0 {
let queue = t.cleanup_queue_mutex_for_tests();
let _ = std::panic::catch_unwind(move || {
let _guard = queue.lock().unwrap();
panic!("intentional lock poison during parallel stress");
});
}
t.drain_cleanup_queue().await;
}
}));
}
for task in tasks {
task.await.expect("stress worker must not panic");
}
tracker.drain_cleanup_queue().await;
assert_eq!(
tracker.cleanup_queue_len_for_tests(),
0,
"queue must converge to empty after stress drain"
);
assert!(
tracker.get_active_ip_count("encap-stress").await <= 4,
"active unique IP count must remain bounded by configured limit"
);
}