Some checks failed
Deploy quoter Microservice on push / deploy (push) Failing after 36m41s
### 🔒 FIX: JWT Token Grace Period - **✅ Добавлен grace period для истекших токенов**: 60 секунд - Изменена логика проверки JWT `exp` в `auth.rs` - Токены принимаются в течение 60 секунд после истечения - Это даёт клиенту время автоматически обновить токен через `refreshToken()` - Логирование разделено: `info` для grace period, `warn` для полного истечения - Решает проблему "Invalid or expired token" при параллельных запросах - Формула: `if exp + 60 < current_time` → reject, иначе accept - Предотвращает race condition: upload начался до истечения, закончился после
219 lines
8.4 KiB
Rust
219 lines
8.4 KiB
Rust
use quoter::RedisConnectionPool;
|
||
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 просто проверяем, что код компилируется
|
||
// Test completed successfully
|
||
}
|
||
|
||
/// Тест 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);
|
||
|
||
// Test completed successfully // Проверяем компиляцию
|
||
}
|
||
|
||
/// Тест производительности 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)
|
||
// use quoter::security::SecurityConfig;
|
||
|
||
// Этот тест проверяет, что методы существуют и компилируются
|
||
// В реальном тесте с Redis:
|
||
// let app_state = AppState::new().await;
|
||
// let health = app_state.redis_health_check().await;
|
||
// let stats = app_state.redis_pool_stats().await;
|
||
|
||
// Test completed successfully // Проверяем компиляцию
|
||
}
|
||
|
||
/// Тест authenticate_request_with_pool
|
||
#[tokio::test]
|
||
async fn test_authenticate_request_with_pool() {
|
||
use actix_web::test;
|
||
// use quoter::security::SecurityConfig;
|
||
|
||
// Создаем тестовый запрос
|
||
let _req = test::TestRequest::default()
|
||
.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 проверяем, что функция существует
|
||
// Test completed successfully
|
||
}
|
||
|
||
/// Тест graceful fallback при недоступности Redis
|
||
#[tokio::test]
|
||
async fn test_redis_fallback_behavior() {
|
||
// Тестируем поведение при недоступности Redis
|
||
// Система должна работать в JWT-only режиме
|
||
|
||
// В реальном тесте:
|
||
// 1. Создаем AppState с недоступным Redis
|
||
// 2. Проверяем, что аутентификация работает через JWT
|
||
// 3. Проверяем, что операции с квотами возвращают fallback значения
|
||
|
||
// Test completed successfully // Проверяем компиляцию
|
||
}
|
||
|
||
/// Интеграционный тест 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)");
|
||
}
|
||
}
|