use pmcp::shared::{ConnectionPoolConfig, HealthStatus, LoadBalanceStrategy, PoolStats};
use proptest::prelude::*;
use std::time::Duration;
#[cfg(test)]
mod pool_configuration {
use super::*;
proptest! {
#[test]
fn property_pool_size_constraints(
max_connections in 1usize..100,
min_connections in 1usize..50
) {
let min_connections = std::cmp::min(min_connections, max_connections);
let config = ConnectionPoolConfig {
max_connections,
min_connections,
..Default::default()
};
prop_assert!(config.min_connections <= config.max_connections);
prop_assert!(config.min_connections > 0);
prop_assert_eq!(config.min_connections, min_connections);
prop_assert_eq!(config.max_connections, max_connections);
}
#[test]
fn property_load_balance_strategies(
strategy in prop::sample::select(&[
LoadBalanceStrategy::RoundRobin,
LoadBalanceStrategy::LeastConnections,
LoadBalanceStrategy::WeightedRoundRobin,
LoadBalanceStrategy::Random,
])
) {
let config = ConnectionPoolConfig {
strategy,
..Default::default()
};
prop_assert_eq!(config.strategy, strategy);
}
#[test]
fn property_health_check_intervals(
interval_secs in 1u64..300
) {
let config = ConnectionPoolConfig {
health_check_interval: Duration::from_secs(interval_secs),
..Default::default()
};
prop_assert_eq!(config.health_check_interval.as_secs(), interval_secs);
prop_assert!(config.health_check_interval > Duration::ZERO);
}
#[test]
fn property_operation_timeouts(
timeout_secs in 1u64..60
) {
let config = ConnectionPoolConfig {
operation_timeout: Duration::from_secs(timeout_secs),
..Default::default()
};
prop_assert_eq!(config.operation_timeout.as_secs(), timeout_secs);
prop_assert!(config.operation_timeout > Duration::ZERO);
}
#[test]
fn property_auto_scaling_config(
auto_scaling in any::<bool>()
) {
let config = ConnectionPoolConfig {
auto_scaling,
..Default::default()
};
prop_assert_eq!(config.auto_scaling, auto_scaling);
}
#[test]
fn property_retry_configuration(
max_retries in 0usize..10,
retry_delay_secs in 1u64..30
) {
let config = ConnectionPoolConfig {
max_retries,
retry_delay: Duration::from_secs(retry_delay_secs),
..Default::default()
};
prop_assert_eq!(config.max_retries, max_retries);
prop_assert_eq!(config.retry_delay.as_secs(), retry_delay_secs);
}
}
}
#[cfg(test)]
mod health_status {
use super::*;
proptest! {
#[test]
fn property_health_status_transitions(
initial_status in prop::sample::select(&[
HealthStatus::Healthy,
HealthStatus::Degraded,
HealthStatus::Unhealthy,
HealthStatus::Checking,
])
) {
match initial_status {
HealthStatus::Healthy => prop_assert!(true),
HealthStatus::Degraded => prop_assert!(true),
HealthStatus::Unhealthy => prop_assert!(true),
HealthStatus::Checking => prop_assert!(true),
}
prop_assert_eq!(initial_status, initial_status);
}
#[test]
fn property_health_status_priority(
status in prop::sample::select(&[
HealthStatus::Healthy,
HealthStatus::Degraded,
HealthStatus::Unhealthy,
HealthStatus::Checking,
])
) {
let is_usable = matches!(status,
HealthStatus::Healthy | HealthStatus::Degraded
);
match status {
HealthStatus::Healthy => prop_assert!(is_usable),
HealthStatus::Degraded => prop_assert!(is_usable),
HealthStatus::Unhealthy => prop_assert!(!is_usable),
HealthStatus::Checking => prop_assert!(!is_usable),
}
}
}
}
#[cfg(test)]
mod pool_stats {
use super::*;
proptest! {
#[test]
fn property_pool_stats_consistency(
healthy in 0usize..50,
degraded in 0usize..30,
unhealthy in 0usize..20,
total_requests in 0u64..10000,
active_requests_percentage in 0u8..=100
) {
let total_connections = healthy + degraded + unhealthy;
let max_active_by_requests = total_requests as usize;
let max_active_by_connections = if total_connections > 0 { total_connections * 100 } else { 0 };
let max_active = max_active_by_requests.min(max_active_by_connections);
let active_requests = (max_active * active_requests_percentage as usize) / 100;
let stats = PoolStats {
total_connections,
healthy_connections: healthy,
degraded_connections: degraded,
unhealthy_connections: unhealthy,
total_requests,
active_requests,
strategy: LoadBalanceStrategy::RoundRobin,
};
prop_assert_eq!(
stats.total_connections,
stats.healthy_connections + stats.degraded_connections + stats.unhealthy_connections
);
if total_connections > 0 {
prop_assert!(stats.active_requests <= total_connections * 100); }
prop_assert!(stats.total_requests >= stats.active_requests as u64);
}
#[test]
fn property_pool_utilization(
total_connections in 1usize..100,
active_requests in 0usize..1000
) {
let avg_requests_per_connection = if total_connections > 0 {
active_requests as f64 / total_connections as f64
} else {
0.0
};
prop_assert!(avg_requests_per_connection >= 0.0);
prop_assert!(avg_requests_per_connection <= active_requests as f64);
}
}
}
#[cfg(test)]
mod load_balancing {
use super::*;
proptest! {
#[test]
fn property_load_balancing_selection(
connection_count in 1usize..20,
strategy in prop::sample::select(&[
LoadBalanceStrategy::RoundRobin,
LoadBalanceStrategy::LeastConnections,
LoadBalanceStrategy::WeightedRoundRobin,
LoadBalanceStrategy::Random,
])
) {
let connections: Vec<usize> = (0..connection_count).collect();
if strategy == LoadBalanceStrategy::RoundRobin {
for i in 0..connection_count * 2 {
let selected_index = i % connection_count;
prop_assert!(selected_index < connection_count);
prop_assert!(connections.contains(&selected_index));
}
}
prop_assert!(connection_count > 0);
prop_assert_eq!(connections.len(), connection_count);
}
#[test]
fn property_round_robin_distribution(
connection_count in 2usize..10,
request_count in 10usize..100
) {
let mut selection_counts = vec![0usize; connection_count];
for i in 0..request_count {
let selected = i % connection_count;
selection_counts[selected] += 1;
}
let expected_per_connection = request_count / connection_count;
let remainder = request_count % connection_count;
for (i, &count) in selection_counts.iter().enumerate() {
if i < remainder {
prop_assert_eq!(count, expected_per_connection + 1);
} else {
prop_assert_eq!(count, expected_per_connection);
}
}
}
#[test]
fn property_least_connections_preference(
active_requests in prop::collection::vec(0usize..50, 2..10)
) {
let min_requests = *active_requests.iter().min().unwrap();
let min_indices: Vec<usize> = active_requests
.iter()
.enumerate()
.filter(|(_, &requests)| requests == min_requests)
.map(|(i, _)| i)
.collect();
prop_assert!(!min_indices.is_empty());
prop_assert!(min_indices.iter().all(|&i| i < active_requests.len()));
for &index in &min_indices {
prop_assert_eq!(active_requests[index], min_requests);
}
}
}
}