Files
quoter/tests/redis_connection_test.rs
Untone 86ad1f1695
Some checks failed
Deploy quoter Microservice on push / deploy (push) Failing after 36m41s
[0.6.10] - 2025-10-04
### 🔒 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 начался до истечения, закончился после
2025-10-05 09:12:53 +03:00

216 lines
8.3 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use redis::{AsyncCommands, Client};
use std::env;
#[tokio::test]
async fn test_redis_url_parsing() {
// Тестируем различные форматы Redis URL
let test_urls = vec![
// Простой URL без пароля
"redis://localhost:6379",
// URL с паролем (должен использовать дефолтное имя пользователя 'redis')
"redis://:password@localhost:6379",
// URL с пользователем и паролем
"redis://user:password@localhost:6379",
// URL с длинным hex паролем (как в Dokku) - должен использовать дефолтное имя 'redis'
"redis://:dbc5f9f9007c555e209964454c9d6abecae5f1db72e490acd5c94354dc012282@dokku-redis-discoursio-redis:6379",
// URL с IP адресом
"redis://172.17.0.3:6379",
// URL с exposed портом
"redis://localhost:12389",
// Тест с явным указанием дефолтного пользователя 'redis'
"redis://redis:password@localhost:6379",
// Тест с пустым пользователем и паролем (должен работать как без аутентификации)
"redis://:@localhost:6379",
];
for url in test_urls {
// Парсим URL для детального вывода
if let Ok(parsed) = url::Url::parse(url) {
println!(
"Testing Redis URL: {}",
url.replace(url.split('@').next().unwrap_or(""), "***")
);
println!(" Host: {}", parsed.host_str().unwrap_or("none"));
println!(" Port: {}", parsed.port().unwrap_or(0));
println!(" Username: '{}'", parsed.username());
println!(
" Password: '{}'",
if parsed.password().is_some() {
"***"
} else {
"none"
}
);
} else {
println!("Testing Redis URL: {}", url);
}
match Client::open(url) {
Ok(client) => {
println!("✅ URL parsed successfully");
// Попробуем подключиться (с коротким таймаутом)
match tokio::time::timeout(
std::time::Duration::from_secs(2),
client.get_multiplexed_async_connection(),
)
.await
{
Ok(Ok(_)) => println!("✅ Connection successful"),
Ok(Err(e)) => println!("❌ Connection failed: {}", e),
Err(_) => println!("⏰ Connection timeout"),
}
}
Err(e) => {
println!("❌ URL parsing failed: {}", e);
}
}
println!("---");
}
}
#[tokio::test]
async fn test_redis_connection_with_env() {
// Тестируем с реальным REDIS_URL из окружения
if let Ok(redis_url) = env::var("REDIS_URL") {
println!(
"Testing with real REDIS_URL: {}",
redis_url.replace(redis_url.split('@').next().unwrap_or(""), "***")
);
// Парсим реальный URL для детального вывода
if let Ok(parsed) = url::Url::parse(&redis_url) {
println!(" Host: {}", parsed.host_str().unwrap_or("none"));
println!(" Port: {}", parsed.port().unwrap_or(0));
println!(" Username: '{}'", parsed.username());
println!(
" Password: '{}'",
if parsed.password().is_some() {
"***"
} else {
"none"
}
);
}
match Client::open(redis_url) {
Ok(client) => {
println!("✅ Real URL parsed successfully");
match tokio::time::timeout(
std::time::Duration::from_secs(5),
client.get_multiplexed_async_connection(),
)
.await
{
Ok(Ok(mut conn)) => {
println!("✅ Real connection successful");
// Попробуем выполнить простую команду
match tokio::time::timeout(
std::time::Duration::from_secs(2),
conn.ping::<String>(),
)
.await
{
Ok(Ok(result)) => println!("✅ PING successful: {}", result),
Ok(Err(e)) => println!("❌ PING failed: {}", e),
Err(_) => println!("⏰ PING timeout"),
}
}
Ok(Err(e)) => println!("❌ Real connection failed: {}", e),
Err(_) => println!("⏰ Real connection timeout"),
}
}
Err(e) => {
println!("❌ Real URL parsing failed: {}", e);
}
}
} else {
println!("⚠️ REDIS_URL not set, skipping real connection test");
}
}
#[test]
fn test_redis_url_components() {
// Тестируем парсинг компонентов URL
let test_url = "redis://:dbc5f9f9007c555e209964454c9d6abecae5f1db72e490acd5c94354dc012282@dokku-redis-discoursio-redis:6379";
if let Ok(parsed) = url::Url::parse(test_url) {
println!("✅ URL parsed successfully");
println!(" Scheme: {}", parsed.scheme());
println!(" Host: {}", parsed.host_str().unwrap_or("none"));
println!(" Port: {}", parsed.port().unwrap_or(0));
println!(" Username: '{}'", parsed.username());
println!(
" Password: {}",
if parsed.password().is_some() {
"***"
} else {
"none"
}
);
println!(" Path: {}", parsed.path());
// Проверяем, что пустое имя пользователя означает дефолтное 'redis'
if parsed.username().is_empty() && parsed.password().is_some() {
println!(
" ⚠️ Empty username with password - Redis client should use default 'redis' user"
);
}
} else {
println!("❌ URL parsing failed");
}
}
#[tokio::test]
async fn test_redis_default_username_behavior() {
// Тестируем поведение Redis client с пустым именем пользователя
println!("Testing Redis default username behavior...");
let test_cases = vec![
(
"redis://:password@localhost:6379",
"Empty username with password",
),
(
"redis://redis:password@localhost:6379",
"Explicit redis username",
),
("redis://:@localhost:6379", "Empty username and password"),
("redis://localhost:6379", "No authentication"),
];
for (url, description) in test_cases {
println!("\n--- {} ---", description);
println!(
"URL: {}",
url.replace(url.split('@').next().unwrap_or(""), "***")
);
if let Ok(parsed) = url::Url::parse(url) {
println!(" Parsed Username: '{}'", parsed.username());
println!(
" Parsed Password: {}",
if parsed.password().is_some() {
"***"
} else {
"none"
}
);
// Redis client поведение:
// - Если username пустой, но есть password -> использует дефолтное имя 'redis'
// - Если username и password пустые -> без аутентификации
if parsed.username().is_empty() && parsed.password().is_some() {
println!(" Expected Redis behavior: Use default username 'redis'");
} else if parsed.username().is_empty() && parsed.password().is_none() {
println!(" Expected Redis behavior: No authentication");
} else {
println!(" Expected Redis behavior: Use explicit credentials");
}
}
}
}