2025-09-30 21:46:47 +03:00
|
|
|
|
use quoter::RedisConnectionPool;
|
2025-09-22 01:23:16 +03:00
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
use tokio::time::sleep;
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест создания Redis connection pool
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_connection_pool_creation() {
|
|
|
|
|
|
// Используем невалидный URL для тестирования обработки ошибок
|
|
|
|
|
|
let invalid_url = "redis://invalid-host:6379";
|
|
|
|
|
|
|
|
|
|
|
|
let result = RedisConnectionPool::new(invalid_url.to_string(), 5, Duration::from_secs(1)).await;
|
|
|
|
|
|
|
|
|
|
|
|
// Должен вернуть ошибку для невалидного URL
|
|
|
|
|
|
assert!(result.is_err());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест статистики connection pool
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_pool_stats() {
|
|
|
|
|
|
// Создаем мок пула (в реальном тесте нужен валидный Redis)
|
|
|
|
|
|
// Этот тест демонстрирует API
|
|
|
|
|
|
|
|
|
|
|
|
// В реальном окружении с Redis:
|
|
|
|
|
|
// let pool = RedisConnectionPool::new(
|
|
|
|
|
|
// "redis://localhost:6379".to_string(),
|
|
|
|
|
|
// 10,
|
|
|
|
|
|
// Duration::from_secs(5)
|
|
|
|
|
|
// ).await.unwrap();
|
|
|
|
|
|
//
|
|
|
|
|
|
// let (available, max) = pool.get_stats().await;
|
|
|
|
|
|
// assert_eq!(max, 10);
|
|
|
|
|
|
// assert!(available <= max);
|
|
|
|
|
|
|
|
|
|
|
|
// Для CI/CD без Redis просто проверяем, что код компилируется
|
2025-10-05 09:12:53 +03:00
|
|
|
|
// Test completed successfully
|
2025-09-22 01:23:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест health check connection pool
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_pool_health_check() {
|
|
|
|
|
|
// Тест с невалидным URL должен вернуть false
|
|
|
|
|
|
if let Ok(pool) = RedisConnectionPool::new(
|
|
|
|
|
|
"redis://invalid-host:6379".to_string(),
|
|
|
|
|
|
5,
|
|
|
|
|
|
Duration::from_millis(100), // Короткий таймаут
|
|
|
|
|
|
)
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
|
|
|
|
|
let health = pool.health_check().await;
|
|
|
|
|
|
assert!(!health); // Должен быть false для невалидного хоста
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест получения соединения из пула
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_pool_get_connection() {
|
|
|
|
|
|
// Тест с невалидным URL для проверки обработки ошибок
|
|
|
|
|
|
if let Ok(pool) = RedisConnectionPool::new(
|
|
|
|
|
|
"redis://invalid-host:6379".to_string(),
|
|
|
|
|
|
5,
|
|
|
|
|
|
Duration::from_millis(100),
|
|
|
|
|
|
)
|
|
|
|
|
|
.await
|
|
|
|
|
|
{
|
|
|
|
|
|
let result = pool.get_connection().await;
|
|
|
|
|
|
// Должен вернуть ошибку для невалидного хоста
|
|
|
|
|
|
assert!(result.is_err());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест возврата соединения в пул
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_pool_return_connection() {
|
|
|
|
|
|
// Демонстрирует API для возврата соединений
|
|
|
|
|
|
// В реальном тесте с валидным Redis:
|
|
|
|
|
|
//
|
|
|
|
|
|
// let pool = RedisConnectionPool::new(...).await.unwrap();
|
|
|
|
|
|
// let conn = pool.get_connection().await.unwrap();
|
|
|
|
|
|
// pool.return_connection(conn).await;
|
|
|
|
|
|
//
|
|
|
|
|
|
// let (available_after, _) = pool.get_stats().await;
|
|
|
|
|
|
// assert_eq!(available_after, available_before + 1);
|
|
|
|
|
|
|
2025-10-05 09:12:53 +03:00
|
|
|
|
// Test completed successfully // Проверяем компиляцию
|
2025-09-22 01:23:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест производительности connection pool
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_pool_performance() {
|
|
|
|
|
|
use std::time::Instant;
|
|
|
|
|
|
|
|
|
|
|
|
// Тест создания пула (без реального Redis)
|
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
|
|
|
|
|
|
|
for _ in 0..100 {
|
|
|
|
|
|
let _result = RedisConnectionPool::new(
|
|
|
|
|
|
"redis://invalid-host:6379".to_string(),
|
|
|
|
|
|
5,
|
|
|
|
|
|
Duration::from_millis(1), // Очень короткий таймаут
|
|
|
|
|
|
)
|
|
|
|
|
|
.await;
|
|
|
|
|
|
// Игнорируем результат, так как Redis недоступен
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let duration = start.elapsed();
|
|
|
|
|
|
println!("100 pool creation attempts took: {:?}", duration);
|
|
|
|
|
|
|
|
|
|
|
|
// Проверяем, что операции выполняются быстро
|
|
|
|
|
|
assert!(duration < Duration::from_secs(10));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест concurrent доступа к пулу
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_pool_concurrent_access() {
|
|
|
|
|
|
// Демонстрирует concurrent использование пула
|
|
|
|
|
|
let tasks = (0..10).map(|i| {
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
// В реальном тесте здесь был бы доступ к пулу
|
|
|
|
|
|
sleep(Duration::from_millis(i * 10)).await;
|
|
|
|
|
|
format!("Task {} completed", i)
|
|
|
|
|
|
})
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let results: Vec<_> = futures::future::join_all(tasks).await;
|
|
|
|
|
|
|
|
|
|
|
|
// Проверяем, что все задачи завершились успешно
|
|
|
|
|
|
for (i, result) in results.iter().enumerate() {
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
|
|
assert_eq!(result.as_ref().unwrap(), &format!("Task {} completed", i));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест AppState с Redis connection pool
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_app_state_redis_pool_methods() {
|
|
|
|
|
|
// Тестируем методы AppState для работы с пулом
|
|
|
|
|
|
// В реальном окружении нужен валидный Redis
|
|
|
|
|
|
|
|
|
|
|
|
// Создаем AppState без Redis (для тестирования fallback)
|
2025-09-30 21:46:47 +03:00
|
|
|
|
// use quoter::security::SecurityConfig;
|
2025-09-22 01:23:16 +03:00
|
|
|
|
|
|
|
|
|
|
// Этот тест проверяет, что методы существуют и компилируются
|
|
|
|
|
|
// В реальном тесте с Redis:
|
|
|
|
|
|
// let app_state = AppState::new().await;
|
|
|
|
|
|
// let health = app_state.redis_health_check().await;
|
|
|
|
|
|
// let stats = app_state.redis_pool_stats().await;
|
|
|
|
|
|
|
2025-10-05 09:12:53 +03:00
|
|
|
|
// Test completed successfully // Проверяем компиляцию
|
2025-09-22 01:23:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест authenticate_request_with_pool
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_authenticate_request_with_pool() {
|
|
|
|
|
|
use actix_web::test;
|
2025-09-30 21:46:47 +03:00
|
|
|
|
// use quoter::security::SecurityConfig;
|
2025-09-22 01:23:16 +03:00
|
|
|
|
|
|
|
|
|
|
// Создаем тестовый запрос
|
2025-09-30 21:46:47 +03:00
|
|
|
|
let _req = test::TestRequest::default()
|
2025-09-22 01:23:16 +03:00
|
|
|
|
.insert_header(("authorization", "Bearer invalid-token"))
|
|
|
|
|
|
.to_http_request();
|
|
|
|
|
|
|
|
|
|
|
|
// В реальном тесте здесь был бы валидный AppState с Redis
|
|
|
|
|
|
// let app_state = AppState::new().await;
|
|
|
|
|
|
// let result = authenticate_request_with_pool(&req, &app_state).await;
|
|
|
|
|
|
// assert!(result.is_err()); // Невалидный токен должен быть отклонен
|
|
|
|
|
|
|
|
|
|
|
|
// Для CI/CD проверяем, что функция существует
|
2025-10-05 09:12:53 +03:00
|
|
|
|
// Test completed successfully
|
2025-09-22 01:23:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Тест graceful fallback при недоступности Redis
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_fallback_behavior() {
|
|
|
|
|
|
// Тестируем поведение при недоступности Redis
|
|
|
|
|
|
// Система должна работать в JWT-only режиме
|
|
|
|
|
|
|
|
|
|
|
|
// В реальном тесте:
|
|
|
|
|
|
// 1. Создаем AppState с недоступным Redis
|
|
|
|
|
|
// 2. Проверяем, что аутентификация работает через JWT
|
|
|
|
|
|
// 3. Проверяем, что операции с квотами возвращают fallback значения
|
|
|
|
|
|
|
2025-10-05 09:12:53 +03:00
|
|
|
|
// Test completed successfully // Проверяем компиляцию
|
2025-09-22 01:23:16 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Интеграционный тест Redis connection pool
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn test_redis_pool_integration() {
|
|
|
|
|
|
// Полный интеграционный тест (требует реального Redis)
|
|
|
|
|
|
|
|
|
|
|
|
// Проверяем переменную окружения для интеграционных тестов
|
|
|
|
|
|
if std::env::var("REDIS_INTEGRATION_TEST").is_ok() {
|
|
|
|
|
|
// Запускаем интеграционные тесты только если установлена переменная
|
|
|
|
|
|
let redis_url =
|
|
|
|
|
|
std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://localhost:6379".to_string());
|
|
|
|
|
|
|
|
|
|
|
|
if let Ok(pool) = RedisConnectionPool::new(redis_url, 10, Duration::from_secs(5)).await {
|
|
|
|
|
|
// Тестируем получение соединения
|
|
|
|
|
|
let conn_result = pool.get_connection().await;
|
|
|
|
|
|
assert!(conn_result.is_ok());
|
|
|
|
|
|
|
|
|
|
|
|
if let Ok(conn) = conn_result {
|
|
|
|
|
|
// Возвращаем соединение в пул
|
|
|
|
|
|
pool.return_connection(conn).await;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Тестируем health check
|
|
|
|
|
|
let health = pool.health_check().await;
|
|
|
|
|
|
assert!(health);
|
|
|
|
|
|
|
|
|
|
|
|
// Тестируем статистику
|
|
|
|
|
|
let (available, max) = pool.get_stats().await;
|
|
|
|
|
|
assert_eq!(max, 10);
|
|
|
|
|
|
assert!(available <= max);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
println!("Skipping Redis integration test (set REDIS_INTEGRATION_TEST=1 to enable)");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|