#[cfg(test)]
use std::collections::{HashMap, HashSet};
#[test]
fn test_csrf_token_uniqueness_and_entropy() {
use crate::handlers::generate_secure_state;
let mut tokens = HashSet::new();
let iterations = 100;
for _ in 0..iterations {
let token = generate_secure_state();
assert!(
token.len() >= 64,
"CSRF token too short: {} (should be >= 64 hex chars = 256 bits)",
token.len()
);
assert!(
token.chars().all(|c| c.is_ascii_hexdigit()),
"Token contains non-hex characters: {}",
token
);
tokens.insert(token);
}
assert_eq!(
tokens.len(),
iterations,
"CSRF token collisions detected! Only {} unique out of {}",
tokens.len(),
iterations
);
}
#[test]
fn test_csrf_state_is_cryptographically_random() {
use crate::handlers::generate_secure_state;
let states: Vec<String> = (0..50).map(|_| generate_secure_state()).collect();
let unique_count = states.iter().collect::<HashSet<_>>().len();
assert_eq!(
unique_count, 50,
"CSRF state generator produced duplicates! Only {} unique",
unique_count
);
for state in &states {
assert!(hex::decode(state).is_ok(), "CSRF state is not valid hex: {}", state);
}
}
#[test]
fn test_jwt_expiration_enforcement() {
use std::time::{SystemTime, UNIX_EPOCH};
use crate::jwt::Claims;
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("System time error")
.as_secs();
let expired_token = Claims {
iss: "test_issuer".to_string(),
sub: "user123".to_string(),
aud: vec!["api".to_string()],
exp: now - 1,
iat: now - 3600,
extra: HashMap::default(),
};
assert!(expired_token.is_expired(), "Expired token should be rejected");
let valid_token = Claims {
iss: "test_issuer".to_string(),
sub: "user123".to_string(),
aud: vec!["api".to_string()],
exp: now + 3600,
iat: now,
extra: HashMap::default(),
};
assert!(!valid_token.is_expired(), "Valid token should not be rejected");
}
#[test]
fn test_jwt_audience_validation_support() {
use jsonwebtoken::Algorithm;
use crate::jwt::JwtValidator;
let validator = JwtValidator::new("https://issuer.example.com", Algorithm::HS256)
.expect("Valid issuer config");
let _validator_with_aud = validator.with_audiences(&["api", "web"]).expect("Valid audiences");
}
#[test]
fn test_jwt_invalid_issuer_rejection() {
use jsonwebtoken::Algorithm;
use crate::jwt::JwtValidator;
let result = JwtValidator::new("", Algorithm::HS256);
assert!(result.is_err(), "Empty issuer should be rejected");
}
#[test]
fn test_csrf_token_url_safe_format() {
use crate::handlers::generate_secure_state;
let tokens: Vec<String> = (0..20).map(|_| generate_secure_state()).collect();
for token in tokens {
assert!(
token.chars().all(|c| c.is_ascii_hexdigit()),
"Token should be hex-safe for URLs: {}",
token
);
assert_eq!(token.len(), 64, "Token length should be consistent: {}", token.len());
}
}
#[test]
fn test_state_expiry_property() {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("System time error")
.as_secs();
let future_expiry = now + 600; let past_expiry = now - 1;
assert!(future_expiry > now, "Future expiry should be after current time");
assert!(past_expiry < now, "Past expiry should be before current time");
}
#[test]
fn test_randomness_quality() {
use crate::handlers::generate_secure_state;
let states: Vec<String> = (0..10).map(|_| generate_secure_state()).collect();
for state in states {
let bytes = hex::decode(&state).expect("Valid hex");
let mut transitions = 0;
for i in 0..bytes.len() - 1 {
if bytes[i] != bytes[i + 1] {
transitions += 1;
}
}
let byte_count = bytes.len();
let min_transitions = byte_count / 5;
assert!(
transitions > min_transitions,
"Insufficient entropy in random bytes: {} transitions in {} bytes",
transitions,
byte_count
);
}
}