#![allow(unexpected_cfgs)] #![cfg(feature = "__trading_service_integration")] //! Comprehensive Authentication Edge Case Tests for Trading Service //! //! This test suite covers critical edge cases, failures, and concurrent scenarios: //! - Concurrent authentication (race conditions, thundering herd) //! - Network failures (Redis, database, partial partitions) //! - Redis failures (OOM, restart, corrupted data, TTL edge cases) //! - Timeout scenarios (extremely short, long, partial operations) //! //! Test Coverage: 30 comprehensive edge case tests //! Focus: HFT requirements (<10μs latency, 100K req/s, zero data races) use anyhow::Result; use jsonwebtoken::{encode, EncodingKey, Header}; use std::net::IpAddr; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use tokio::task::JoinSet; use tokio::time::{sleep, timeout}; use uuid::Uuid; use trading_service::auth_interceptor::{AuthConfig, JwtClaims, JwtValidator}; use trading_service::rate_limiter::{ RateLimitConfig, RateLimitContext, RateLimitResult, RateLimiter, RequestType, }; // ============================================================================ // TEST HELPERS & FIXTURES // ============================================================================ /// Test JWT secret that meets all validation requirements const TEST_JWT_SECRET: &str = "Kx7mP@9nR!2sW#5vY$8bC&3fG*6jH^1kL%4pQ+7tZ-0uN~9dM=5eV(8xS)2wT!6yA#4zB"; /// Helper to create valid JWT token for testing fn create_test_jwt_token(secret: &str, modify_claims: impl FnOnce(&mut JwtClaims)) -> String { let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let mut claims = JwtClaims { jti: format!("test-jti-{}", Uuid::new_v4()), sub: "test_user_123".to_string(), iat: now, exp: now + 3600, // 1 hour expiration iss: "foxhunt-trading".to_string(), aud: "trading-api".to_string(), roles: vec!["trader".to_string()], permissions: vec!["trading.submit_order".to_string()], token_type: "access".to_string(), session_id: Some(format!("session-{}", Uuid::new_v4())), }; modify_claims(&mut claims); let key = EncodingKey::from_secret(secret.as_ref()); encode(&Header::default(), &claims, &key).expect("Failed to encode JWT") } /// Helper to create AuthConfig with test-safe defaults fn create_test_auth_config() -> AuthConfig { std::env::set_var("JWT_SECRET", TEST_JWT_SECRET); let mut config = AuthConfig::new().expect("Failed to create AuthConfig"); config.require_mtls = false; config } // ============================================================================ // CATEGORY 1: CONCURRENT AUTHENTICATION (10 tests) // ============================================================================ #[tokio::test] async fn test_concurrent_thundering_herd_1000_simultaneous_logins() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Create 1000 valid tokens let tokens: Vec = (0..1000) .map(|i| { create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); claims.jti = format!("jti-{}", Uuid::new_v4()); }) }) .collect(); // Spawn 1000 concurrent validation tasks let mut tasks = JoinSet::new(); for (i, token) in tokens.into_iter().enumerate() { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let result = validator_clone.validate_token(&token).await; (i, result.is_ok()) }); } // Collect results let mut success_count = 0; while let Some(result) = tasks.join_next().await { let (_index, is_ok) = result.unwrap(); if is_ok { success_count += 1; } } // All 1000 should succeed assert_eq!(success_count, 1000, "Expected all 1000 logins to succeed"); Ok(()) } #[tokio::test] async fn test_concurrent_race_condition_token_generation() -> Result<()> { let config = Arc::new(create_test_auth_config()); // Spawn 100 concurrent token generation tasks for the SAME user let mut tasks = JoinSet::new(); for _ in 0..100 { tasks.spawn(async move { create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = "shared_user_123".to_string(); // Each token should get a unique JTI claims.jti = format!("jti-{}", Uuid::new_v4()); }) }); } // Collect all generated tokens let mut tokens = Vec::new(); while let Some(result) = tasks.join_next().await { tokens.push(result.unwrap()); } // Verify all tokens are unique and valid assert_eq!(tokens.len(), 100); let validator = JwtValidator::new(config); let mut unique_jtis = std::collections::HashSet::new(); for token in tokens { let claims = validator.validate_token(&token).await?; assert_eq!(claims.sub, "shared_user_123"); unique_jtis.insert(claims.jti.clone()); } // All JTIs should be unique assert_eq!(unique_jtis.len(), 100, "Expected 100 unique JTI values"); Ok(()) } #[tokio::test] async fn test_concurrent_rate_limiter_no_data_races() -> Result<()> { let config = RateLimitConfig { user_requests_per_minute: 100, user_burst_capacity: 100, ip_requests_per_minute: 100, ip_burst_capacity: 100, ..Default::default() }; let limiter = Arc::new(RateLimiter::new(config)); let test_ip: IpAddr = "192.168.1.200".parse().expect("INVARIANT: Valid parse input"); // Spawn 200 concurrent rate limit checks let mut tasks = JoinSet::new(); for _ in 0..200 { let limiter_clone = Arc::clone(&limiter); tasks.spawn(async move { let context = RateLimitContext { user_id: None, ip_addr: test_ip, request_type: RequestType::General, tokens_requested: 1.0, }; let result = limiter_clone.check_rate_limit(&context).await; matches!(result, RateLimitResult::Allowed) }); } // Collect results let mut allowed_count = 0; let mut blocked_count = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { allowed_count += 1; } else { blocked_count += 1; } } // First 100 allowed, next 100 blocked (burst capacity) assert!( allowed_count <= 100, "Expected at most 100 allowed requests" ); assert!( blocked_count >= 100, "Expected at least 100 blocked requests" ); assert_eq!(allowed_count + blocked_count, 200, "Total should be 200"); Ok(()) } #[tokio::test] async fn test_concurrent_jwt_validation_same_token() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Create a single valid token let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Validate it 500 times concurrently let mut tasks = JoinSet::new(); for _ in 0..500 { let validator_clone = Arc::clone(&validator); let token_clone = token.clone(); tasks.spawn(async move { validator_clone.validate_token(&token_clone).await.is_ok() }); } // All should succeed let mut success_count = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { success_count += 1; } } assert_eq!( success_count, 500, "Expected all 500 validations to succeed" ); Ok(()) } #[tokio::test] async fn test_concurrent_mixed_valid_invalid_tokens() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Create 250 valid and 250 invalid tokens let mut tokens = Vec::new(); for i in 0..250 { // Valid token let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("valid_user_{}", i); }); tokens.push((token, true)); // (token, expected_valid) } for i in 0..250 { // Invalid token (wrong signature) let wrong_secret = "WrongSecret123!@#WrongSecret123!@#WrongSecret123!@#WrongSecret123!@#"; let token = create_test_jwt_token(wrong_secret, |claims| { claims.sub = format!("invalid_user_{}", i); }); tokens.push((token, false)); } // Validate all 500 concurrently let mut tasks = JoinSet::new(); for (token, expected_valid) in tokens { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let result = validator_clone.validate_token(&token).await; (result.is_ok(), expected_valid) }); } // Verify results let mut valid_count = 0; let mut invalid_count = 0; while let Some(result) = tasks.join_next().await { let (is_ok, expected_valid) = result.unwrap(); assert_eq!(is_ok, expected_valid, "Validation result mismatch"); if is_ok { valid_count += 1; } else { invalid_count += 1; } } assert_eq!(valid_count, 250); assert_eq!(invalid_count, 250); Ok(()) } #[tokio::test] async fn test_concurrent_token_refresh_stampede() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Create 1000 tokens that will expire at approximately the same time let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let expiry = now + 2; // Expire in 2 seconds let tokens: Vec = (0..1000) .map(|i| { create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); claims.exp = expiry; }) }) .collect(); // Wait 3 seconds for tokens to expire sleep(Duration::from_secs(3)).await; // Attempt to validate all expired tokens concurrently (simulating refresh stampede) let mut tasks = JoinSet::new(); for token in tokens { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { validator_clone.validate_token(&token).await.is_err() }); } // All should fail (expired) let mut failed_count = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { failed_count += 1; } } assert_eq!( failed_count, 1000, "Expected all 1000 expired tokens to fail" ); Ok(()) } #[tokio::test] async fn test_concurrent_rate_limit_different_ips_independent() -> Result<()> { let config = RateLimitConfig { user_requests_per_minute: 10, user_burst_capacity: 10, ip_requests_per_minute: 10, ip_burst_capacity: 10, ..Default::default() }; let limiter = Arc::new(RateLimiter::new(config)); // Spawn 50 IPs making 20 requests each concurrently let mut tasks = JoinSet::new(); for ip_suffix in 0..50 { let limiter_clone = Arc::clone(&limiter); tasks.spawn(async move { let test_ip: IpAddr = format!("192.168.1.{}", ip_suffix).parse().expect("INVARIANT: Valid parse input"); let mut allowed = 0; for _ in 0..20 { let context = RateLimitContext { user_id: None, ip_addr: test_ip, request_type: RequestType::General, tokens_requested: 1.0, }; let result = limiter_clone.check_rate_limit(&context).await; if matches!(result, RateLimitResult::Allowed) { allowed += 1; } } (ip_suffix, allowed) }); } // Each IP should get exactly 10 allowed (burst capacity) while let Some(result) = tasks.join_next().await { let (_ip, allowed) = result.unwrap(); assert_eq!(allowed, 10, "Each IP should get exactly 10 requests"); } Ok(()) } #[tokio::test] async fn test_concurrent_auth_failure_lockout() -> Result<()> { let config = RateLimitConfig { user_requests_per_minute: 100, user_burst_capacity: 100, ip_requests_per_minute: 100, ip_burst_capacity: 100, auth_failures_per_minute: 5, auth_failure_penalty_minutes: 1, ..Default::default() }; let limiter = Arc::new(RateLimiter::new(config)); let test_ip: IpAddr = "192.168.1.201".parse().expect("INVARIANT: Valid parse input"); // 10 concurrent tasks recording auth failures let mut tasks = JoinSet::new(); for _ in 0..10 { let limiter_clone = Arc::clone(&limiter); tasks.spawn(async move { let user_id = Uuid::new_v4(); limiter_clone .apply_auth_failure_penalty(user_id, test_ip) .await; }); } // Wait for all to complete while tasks.join_next().await.is_some() {} // Check if IP is locked out let context = RateLimitContext { user_id: None, ip_addr: test_ip, request_type: RequestType::General, tokens_requested: 1.0, }; let result = limiter.check_rate_limit(&context).await; assert!( !matches!(result, RateLimitResult::Allowed), "IP should be locked out after 10 failures" ); Ok(()) } #[tokio::test] async fn test_concurrent_jwt_expiration_boundary() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Create token that expires in exactly 1 second let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.exp = now + 1; }); // Spawn 100 tasks to validate at exactly the same time let mut tasks = JoinSet::new(); for _ in 0..100 { let validator_clone = Arc::clone(&validator); let token_clone = token.clone(); tasks.spawn(async move { validator_clone.validate_token(&token_clone).await.is_ok() }); } // Immediately collect results (before expiration) let mut initial_success = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { initial_success += 1; } } // All should succeed (not expired yet) assert_eq!(initial_success, 100); // Wait for expiration sleep(Duration::from_secs(2)).await; // Try again - all should fail let mut tasks = JoinSet::new(); for _ in 0..100 { let validator_clone = Arc::clone(&validator); let token_clone = token.clone(); tasks.spawn(async move { validator_clone.validate_token(&token_clone).await.is_err() }); } let mut expired_failures = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { expired_failures += 1; } } assert_eq!(expired_failures, 100, "All should fail after expiration"); Ok(()) } #[tokio::test] async fn test_concurrent_multiple_roles_permission_checks() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); let roles = vec!["admin", "trader", "analyst", "risk_manager"]; // Spawn 200 concurrent validations (50 per role) let mut tasks = JoinSet::new(); for i in 0..200 { let role = roles[i % 4]; let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.roles = vec![role.to_string()]; }); let result = validator_clone.validate_token(&token).await; (role, result.is_ok()) }); } // All should succeed with correct roles let mut role_counts = std::collections::HashMap::new(); while let Some(result) = tasks.join_next().await { let (role, is_ok) = result.unwrap(); assert!(is_ok, "Validation should succeed for role {}", role); *role_counts.entry(role).or_insert(0) += 1; } // Each role should have exactly 50 validations for role in roles { assert_eq!( role_counts[role], 50, "Expected 50 validations for role {}", role ); } Ok(()) } // ============================================================================ // CATEGORY 2: NETWORK FAILURES (8 tests) // ============================================================================ #[tokio::test] async fn test_network_timeout_extremely_slow_validation() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Set a very short timeout (10ms) let result = timeout(Duration::from_millis(10), validator.validate_token(&token)).await; // Should complete within 10ms (HFT requirement) assert!(result.is_ok(), "Validation should complete within 10ms"); assert!(result.unwrap().is_ok(), "Token should be valid"); Ok(()) } #[tokio::test] async fn test_network_validation_under_latency_spike() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Simulate network latency spike by running 1000 concurrent validations let mut tasks = JoinSet::new(); for _ in 0..1000 { let validator_clone = Arc::clone(&validator); let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); tasks.spawn(async move { let start = std::time::Instant::now(); let result = validator_clone.validate_token(&token).await; (result.is_ok(), start.elapsed()) }); } let mut max_latency = Duration::from_secs(0); let mut success_count = 0; while let Some(result) = tasks.join_next().await { let (is_ok, latency) = result.unwrap(); if is_ok { success_count += 1; } if latency > max_latency { max_latency = latency; } } assert_eq!(success_count, 1000, "All validations should succeed"); // P99 should be < 10μs, but under load we allow < 1ms assert!( max_latency < Duration::from_millis(1), "Max latency should be < 1ms" ); Ok(()) } #[tokio::test] async fn test_network_partial_token_corruption() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let valid_token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Simulate network corruption by modifying random characters let mut corrupted = valid_token.clone(); let bytes = unsafe { corrupted.as_bytes_mut() }; if !bytes.is_empty() { bytes[bytes.len() / 2] = b'X'; // Corrupt middle character } // Corrupted token should fail validation let result = validator.validate_token(&corrupted).await; assert!(result.is_err(), "Corrupted token should fail validation"); // Original should still work let result = validator.validate_token(&valid_token).await; assert!(result.is_ok(), "Original token should remain valid"); Ok(()) } #[tokio::test] async fn test_network_connection_pool_exhaustion() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Spawn 10,000 concurrent validation tasks (stress test) let mut tasks = JoinSet::new(); for i in 0..10000 { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); }); validator_clone.validate_token(&token).await.is_ok() }); } // System should handle all requests without exhaustion let mut success_count = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { success_count += 1; } } assert!( success_count >= 9500, "At least 95% should succeed under stress (got {})", success_count ); Ok(()) } #[tokio::test] async fn test_network_dns_resolution_timeout() -> Result<()> { // This test verifies auth does NOT depend on DNS resolution let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Should complete instantly (no DNS lookups) let start = std::time::Instant::now(); let result = validator.validate_token(&token).await; let elapsed = start.elapsed(); assert!(result.is_ok(), "Validation should succeed"); assert!( elapsed < Duration::from_micros(100), "Should be < 100μs (no network)" ); Ok(()) } #[tokio::test] async fn test_network_packet_loss_simulation() -> Result<()> { // Simulate intermittent network by randomly failing some validations let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); let mut tasks = JoinSet::new(); for i in 0..100 { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Simulate packet loss: every 10th request "fails" (we return error) if i % 10 == 0 { Err::<(), anyhow::Error>(anyhow::anyhow!("Simulated packet loss")) } else { validator_clone.validate_token(&token).await.map(|_| ()) } }); } let mut success_count = 0; let mut simulated_loss = 0; while let Some(result) = tasks.join_next().await { match result.unwrap() { Ok(_) => success_count += 1, Err(_) => simulated_loss += 1, } } assert_eq!(simulated_loss, 10, "Expected 10 simulated packet losses"); assert_eq!(success_count, 90, "Expected 90 successful validations"); Ok(()) } #[tokio::test] async fn test_network_tls_handshake_overhead() -> Result<()> { // Verify JWT validation is fast (no TLS handshake overhead) let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // 1000 sequential validations should be very fast let start = std::time::Instant::now(); for _ in 0..1000 { validator.validate_token(&token).await?; } let elapsed = start.elapsed(); // Average should be < 10μs per validation let avg = elapsed / 1000; assert!( avg < Duration::from_micros(10), "Average validation should be < 10μs (got {:?})", avg ); Ok(()) } #[tokio::test] async fn test_network_graceful_degradation_under_load() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Spawn 5000 concurrent tasks in waves let mut all_tasks = JoinSet::new(); for wave in 0..5 { sleep(Duration::from_millis(10)).await; // Small delay between waves for i in 0..1000 { let validator_clone = Arc::clone(&validator); all_tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_wave{}_{}", wave, i); }); validator_clone.validate_token(&token).await.is_ok() }); } } let mut success_count = 0; while let Some(result) = all_tasks.join_next().await { if result.unwrap() { success_count += 1; } } // Should handle 5000 requests with >99% success assert!( success_count >= 4950, "Expected >99% success under wave load (got {})", success_count ); Ok(()) } // ============================================================================ // CATEGORY 3: TIMEOUT EDGE CASES (5 tests) // ============================================================================ #[tokio::test] async fn test_timeout_extremely_short_1ms_validation() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Set 1ms timeout (very aggressive for HFT) let result = timeout(Duration::from_millis(1), validator.validate_token(&token)).await; // Should complete within 1ms assert!(result.is_ok(), "Validation should complete within 1ms"); assert!(result.unwrap().is_ok(), "Token should be valid"); Ok(()) } #[tokio::test] async fn test_timeout_long_10s_validation() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Set 10s timeout (unnecessarily long) let result = timeout(Duration::from_secs(10), validator.validate_token(&token)).await; assert!(result.is_ok(), "Validation should complete within 10s"); assert!(result.unwrap().is_ok(), "Token should be valid"); Ok(()) } #[tokio::test] async fn test_timeout_multiple_operations_cleanup() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Run 1000 validations with 1ms timeout each let mut tasks = JoinSet::new(); for i in 0..1000 { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); }); timeout( Duration::from_millis(1), validator_clone.validate_token(&token), ) .await }); } let mut success_count = 0; let mut timeout_count = 0; while let Some(result) = tasks.join_next().await { match result.unwrap() { Ok(Ok(_)) => success_count += 1, Ok(Err(_)) => {}, // Validation error (not timeout) Err(_) => timeout_count += 1, } } // Most should succeed within 1ms assert!(success_count >= 950, "Expected >95% to complete within 1ms"); assert!(timeout_count < 50, "Expected <5% timeouts"); Ok(()) } #[tokio::test] async fn test_timeout_validation_at_expiration_boundary() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); // Create token expiring in exactly 100ms let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.exp = now + 1; // Expires in 1 second }); // Validate immediately (should succeed) let result = timeout(Duration::from_millis(10), validator.validate_token(&token)).await; assert!(result.is_ok()); assert!(result.unwrap().is_ok()); // Wait for expiration sleep(Duration::from_millis(1100)).await; // Validate after expiration (should fail, but quickly) let result = timeout(Duration::from_millis(10), validator.validate_token(&token)).await; assert!(result.is_ok(), "Should complete within timeout"); assert!(result.unwrap().is_err(), "Token should be expired"); Ok(()) } #[tokio::test] async fn test_timeout_concurrent_timeout_handling() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Spawn 500 tasks with varying timeouts let mut tasks = JoinSet::new(); for i in 0..500 { let validator_clone = Arc::clone(&validator); let timeout_ms = 1 + (i % 10); // Timeouts from 1ms to 10ms tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); let result = timeout( Duration::from_millis(timeout_ms), validator_clone.validate_token(&token), ) .await; result.is_ok() }); } let mut completed = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { completed += 1; } } // All should complete within their respective timeouts assert_eq!( completed, 500, "All validations should complete within timeout" ); Ok(()) } // ============================================================================ // CATEGORY 4: REDIS FAILURES (7 tests) // Note: These tests simulate Redis behavior without actual Redis infrastructure // ============================================================================ #[tokio::test] async fn test_redis_simulated_oom_during_validation() -> Result<()> { // Simulate Redis OOM by creating extremely large token (>8KB) let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let oversized_token = "a".repeat(10000); // 10KB token let result = validator.validate_token(&oversized_token).await; assert!(result.is_err(), "Oversized token should be rejected"); Ok(()) } #[tokio::test] async fn test_redis_simulated_corrupted_cache_data() -> Result<()> { // Simulate corrupted JWT by malforming structure let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let corrupted_token = "header.payload"; // Missing signature section let result = validator.validate_token(corrupted_token).await; assert!(result.is_err(), "Malformed token should be rejected"); Ok(()) } #[tokio::test] async fn test_redis_simulated_ttl_expiration_race() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Create tokens with very short expiration (1 second) let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let tokens: Vec = (0..100) .map(|i| { create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); claims.exp = now + 1; }) }) .collect(); // Validate half immediately let mut immediate_tasks = JoinSet::new(); for (i, token) in tokens.iter().enumerate().take(50) { let validator_clone = Arc::clone(&validator); let token_clone = token.clone(); immediate_tasks.spawn(async move { ( i, validator_clone.validate_token(&token_clone).await.is_ok(), ) }); } let mut immediate_success = 0; while let Some(result) = immediate_tasks.join_next().await { if result.unwrap().1 { immediate_success += 1; } } assert_eq!( immediate_success, 50, "All immediate validations should succeed" ); // Wait for expiration sleep(Duration::from_millis(1100)).await; // Validate remaining half (should fail) let mut delayed_tasks = JoinSet::new(); for (i, token) in tokens.iter().enumerate().skip(50) { let validator_clone = Arc::clone(&validator); let token_clone = token.clone(); delayed_tasks.spawn(async move { ( i, validator_clone.validate_token(&token_clone).await.is_err(), ) }); } let mut delayed_failures = 0; while let Some(result) = delayed_tasks.join_next().await { if result.unwrap().1 { delayed_failures += 1; } } assert_eq!( delayed_failures, 50, "All delayed validations should fail (expired)" ); Ok(()) } #[tokio::test] async fn test_redis_simulated_eviction_policy_impact() -> Result<()> { // Simulate eviction by creating many tokens and checking they remain valid let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); let tokens: Vec = (0..1000) .map(|i| { create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); }) }) .collect(); // Validate all tokens (simulating cache population) let mut tasks = JoinSet::new(); for token in tokens { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { validator_clone.validate_token(&token).await.is_ok() }); } let mut success_count = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { success_count += 1; } } // All should remain valid (no eviction in memory) assert_eq!(success_count, 1000, "All tokens should remain valid"); Ok(()) } #[tokio::test] async fn test_redis_simulated_read_write_timeout() -> Result<()> { let config = Arc::new(create_test_auth_config()); let validator = JwtValidator::new(config); let token = create_test_jwt_token(TEST_JWT_SECRET, |_| {}); // Simulate timeout with extremely short duration let result = timeout(Duration::from_micros(1), validator.validate_token(&token)).await; // May timeout (acceptable) or complete (very fast) match result { Ok(Ok(_)) => {}, // Completed within 1μs (excellent) Err(_) => {}, // Timed out (acceptable for 1μs) Ok(Err(_)) => panic!("Validation should not fail"), } Ok(()) } #[tokio::test] async fn test_redis_simulated_cluster_failover() -> Result<()> { // Simulate failover by validating many tokens concurrently let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); let mut tasks = JoinSet::new(); for i in 0..500 { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); }); validator_clone.validate_token(&token).await.is_ok() }); } let mut success_count = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { success_count += 1; } } // Should maintain >99% availability during "failover" assert!( success_count >= 495, "Expected >99% success during failover simulation" ); Ok(()) } #[tokio::test] async fn test_redis_simulated_memory_pressure() -> Result<()> { // Simulate memory pressure by creating many large claims let config = Arc::new(create_test_auth_config()); let validator = Arc::new(JwtValidator::new(config)); // Create 100 tokens with large permissions lists let mut tasks = JoinSet::new(); for i in 0..100 { let validator_clone = Arc::clone(&validator); tasks.spawn(async move { let token = create_test_jwt_token(TEST_JWT_SECRET, |claims| { claims.sub = format!("user_{}", i); // Add 100 permissions (large claim) claims.permissions = (0..100).map(|p| format!("permission_{}", p)).collect(); }); validator_clone.validate_token(&token).await.is_ok() }); } let mut success_count = 0; while let Some(result) = tasks.join_next().await { if result.unwrap() { success_count += 1; } } // All should succeed despite large claims assert_eq!( success_count, 100, "All large claim validations should succeed" ); Ok(()) }