#![allow(
clippy::arithmetic_side_effects,
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic
)]
use super::*;
#[test]
fn test_pool_config_default() {
let config = ConnectionPoolConfig::default();
assert_eq!(config.max_connections, 100);
assert_eq!(config.max_queue_size, 500);
assert_eq!(config.queue_timeout_ms, 30000);
}
#[test]
fn test_pool_config_builder() {
let config = ConnectionPoolConfig::new(50)
.with_queue_timeout(Duration::from_secs(10))
.with_max_queue_size(100);
assert_eq!(config.max_connections, 50);
assert_eq!(config.max_queue_size, 100);
assert_eq!(config.queue_timeout_ms, 10000);
}
#[test]
fn test_pooling_mode_only_request() {
assert_eq!(PoolingMode::Request.as_str(), "request");
assert_eq!(PoolingMode::default(), PoolingMode::Request);
let mode: PoolingMode = serde_json::from_str(r#""request""#).unwrap();
assert_eq!(mode, PoolingMode::Request);
}
#[tokio::test]
async fn test_pool_acquire_immediate() {
let pool = ConnectionPool::with_max_connections(10);
let conn = pool.acquire().await.unwrap();
assert_eq!(pool.active_connections(), 1);
assert_eq!(pool.available_permits(), 9);
drop(conn);
assert_eq!(pool.active_connections(), 0);
assert_eq!(pool.available_permits(), 10);
let stats = pool.stats();
assert_eq!(stats.total_requests, 1);
assert_eq!(stats.immediate_permits, 1);
assert_eq!(stats.queued_requests, 0);
}
#[tokio::test]
async fn test_pool_exhaustion_and_queue() {
let config = ConnectionPoolConfig::new(2).with_queue_timeout(Duration::from_millis(100));
let pool = Arc::new(ConnectionPool::new(config));
let _conn1 = pool.acquire().await.unwrap();
let _conn2 = pool.acquire().await.unwrap();
assert_eq!(pool.active_connections(), 2);
assert_eq!(pool.available_permits(), 0);
let pool2 = pool.clone();
let result = tokio::time::timeout(Duration::from_millis(200), pool2.acquire()).await;
assert!(result.is_ok()); let inner = result.unwrap();
assert!(matches!(inner, Err(PoolError::Timeout)));
let stats = pool.stats();
assert_eq!(stats.queue_timeouts, 1);
}
#[tokio::test]
async fn test_pool_queue_full() {
let config = ConnectionPoolConfig::new(1)
.with_max_queue_size(1)
.with_queue_timeout(Duration::from_secs(10));
let pool = Arc::new(ConnectionPool::new(config));
let _conn = pool.acquire().await.unwrap();
let pool2 = pool.clone();
let handle1 = tokio::spawn(async move { pool2.acquire().await.is_ok() });
tokio::time::sleep(Duration::from_millis(10)).await;
assert_eq!(pool.queue_depth(), 1);
let result = pool.acquire().await;
assert!(matches!(result, Err(PoolError::QueueFull)));
let stats = pool.stats();
assert_eq!(stats.queue_rejections, 1);
handle1.abort();
}
#[tokio::test]
async fn test_pool_waiter_signaling() {
let config = ConnectionPoolConfig::new(1).with_queue_timeout(Duration::from_secs(5));
let pool = Arc::new(ConnectionPool::new(config));
let conn = pool.acquire().await.unwrap();
let pool2 = pool.clone();
let handle = tokio::spawn(async move { pool2.acquire().await.is_ok() });
tokio::time::sleep(Duration::from_millis(10)).await;
assert_eq!(pool.queue_depth(), 1);
drop(conn);
let result = handle.await.unwrap();
assert!(result);
let stats = pool.stats();
assert_eq!(stats.queued_requests, 1);
assert!(stats.avg_wait_time_ms > 0.0);
}
#[test]
fn test_pool_snapshot_serializable() {
let pool = ConnectionPool::with_max_connections(10);
let snapshot = pool.stats();
let json = serde_json::to_string(&snapshot).unwrap();
assert!(json.contains("active_connections"));
assert!(json.contains("queue_depth"));
}
#[test]
fn test_pool_error_display() {
assert_eq!(
PoolError::QueueFull.to_string(),
"Connection pool queue is full"
);
assert_eq!(
PoolError::Timeout.to_string(),
"Timed out waiting for connection"
);
assert_eq!(
PoolError::Shutdown.to_string(),
"Connection pool is shutting down"
);
}
#[tokio::test]
async fn test_pool_drain_queue() {
let config = ConnectionPoolConfig::new(1).with_queue_timeout(Duration::from_secs(10));
let pool = Arc::new(ConnectionPool::new(config));
let _conn = pool.acquire().await.unwrap();
let pool2 = pool.clone();
let pool3 = pool.clone();
let _h1 = tokio::spawn(async move { pool2.acquire().await.is_ok() });
let _h2 = tokio::spawn(async move { pool3.acquire().await.is_ok() });
tokio::time::sleep(Duration::from_millis(10)).await;
assert!(pool.queue_depth() >= 1);
pool.drain_queue();
assert_eq!(pool.queue_depth(), 0);
}
#[test]
fn test_pool_has_capacity() {
let pool = ConnectionPool::with_max_connections(2);
assert!(pool.has_capacity());
}
#[tokio::test]
async fn test_pool_stress_100_acquires_on_10() {
use std::sync::Arc;
use tokio::sync::Barrier;
let pool = Arc::new(ConnectionPool::with_max_connections(10));
let num_tasks = 100;
let barrier = Arc::new(Barrier::new(num_tasks));
let mut handles = Vec::with_capacity(num_tasks);
for _ in 0..num_tasks {
let pool = pool.clone();
let barrier = barrier.clone();
handles.push(tokio::spawn(async move {
barrier.wait().await;
let conn = pool.acquire().await;
assert!(conn.is_ok(), "Should acquire connection eventually");
tokio::time::sleep(Duration::from_millis(1)).await;
drop(conn);
}));
}
for handle in handles {
handle.await.unwrap();
}
let stats = pool.stats();
assert_eq!(
stats.active_connections, 0,
"All connections should be released"
);
assert_eq!(
stats.total_requests, 100,
"Should have processed 100 requests"
);
}
#[tokio::test]
async fn test_pool_stress_queue_full_rejection() {
use std::sync::Arc;
let config = ConnectionPoolConfig::new(2)
.with_max_queue_size(3)
.with_queue_timeout(Duration::from_secs(5));
let pool = Arc::new(ConnectionPool::new(config));
let _conn1 = pool.acquire().await.unwrap();
let _conn2 = pool.acquire().await.unwrap();
let pool_clone = pool.clone();
let waiters: Vec<_> = (0..3)
.map(|_| {
let p = pool_clone.clone();
tokio::spawn(async move { p.acquire().await.is_ok() })
})
.collect();
tokio::time::sleep(Duration::from_millis(50)).await;
let result = pool.acquire().await;
assert!(matches!(result, Err(PoolError::QueueFull)));
drop(_conn1);
drop(_conn2);
for w in waiters {
let result = w.await.unwrap();
assert!(result);
}
}
#[tokio::test]
async fn test_pool_stress_concurrent_acquire_release_cycles() {
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use tokio::sync::Barrier;
let config = ConnectionPoolConfig::new(5)
.with_queue_timeout(Duration::from_secs(60))
.with_max_queue_size(1000);
let pool = Arc::new(ConnectionPool::new(config));
let num_tasks = 20;
let cycles_per_task = 10;
let barrier = Arc::new(Barrier::new(num_tasks));
let success_count = Arc::new(AtomicU64::new(0));
let failure_count = Arc::new(AtomicU64::new(0));
let mut handles = Vec::with_capacity(num_tasks);
for _ in 0..num_tasks {
let pool = pool.clone();
let barrier = barrier.clone();
let success_count = success_count.clone();
let failure_count = failure_count.clone();
handles.push(tokio::spawn(async move {
barrier.wait().await;
for _ in 0..cycles_per_task {
match pool.acquire().await {
Ok(conn) => {
tokio::time::sleep(Duration::from_millis(1)).await;
drop(conn);
success_count.fetch_add(1, Ordering::Relaxed);
}
Err(PoolError::Timeout) => {
failure_count.fetch_add(1, Ordering::Relaxed);
}
Err(e) => panic!("Unexpected pool error: {:?}", e),
}
}
}));
}
for handle in handles {
handle.await.unwrap();
}
let stats = pool.stats();
assert_eq!(
stats.active_connections, 0,
"All connections should be released"
);
let successes = success_count.load(Ordering::Relaxed);
let failures = failure_count.load(Ordering::Relaxed);
let expected = (num_tasks * cycles_per_task) as u64;
assert_eq!(successes + failures, expected, "All cycles should complete");
assert!(
successes > expected / 2,
"At least half of acquires should succeed, got {}/{}",
successes,
expected
);
assert_eq!(
stats.total_requests, expected,
"Should have tracked all {} requests",
expected
);
}