use dist_agent_lang::http_server_security::{Claims, InputValidator, RateLimiter};
use std::net::IpAddr;
use tokio::runtime::Runtime;
#[tokio::test]
async fn test_rate_limiter_exact_limit_boundary() {
let limiter = RateLimiter::new(3, 60); let ip: IpAddr = "127.0.0.1".parse().unwrap();
assert!(
limiter.check_rate_limit(ip).await.is_ok(),
"Request 1 should succeed"
);
assert!(
limiter.check_rate_limit(ip).await.is_ok(),
"Request 2 should succeed"
);
assert!(
limiter.check_rate_limit(ip).await.is_ok(),
"Request 3 should succeed (at limit)"
);
let result = limiter.check_rate_limit(ip).await;
assert!(result.is_err(), "Request 4 should be rate limited");
assert_eq!(
result.unwrap_err(),
axum::http::StatusCode::TOO_MANY_REQUESTS
);
}
#[tokio::test]
async fn test_rate_limiter_exact_count_boundary() {
let limiter = RateLimiter::new(5, 60); let ip: IpAddr = "192.168.1.1".parse().unwrap();
for i in 1..=5 {
let result = limiter.check_rate_limit(ip).await;
assert!(
result.is_ok(),
"Request {} should succeed (at exact limit)",
i
);
}
let result = limiter.check_rate_limit(ip).await;
assert!(result.is_err(), "Request 6 should fail (exceeds limit)");
assert_eq!(
result.unwrap_err(),
axum::http::StatusCode::TOO_MANY_REQUESTS
);
}
#[tokio::test]
async fn test_rate_limiter_boundary_condition() {
let limiter = RateLimiter::new(5, 60);
let ip: IpAddr = "192.168.1.1".parse().unwrap();
for i in 1..=5 {
let result = limiter.check_rate_limit(ip).await;
assert!(result.is_ok(), "Request {} should succeed (at limit)", i);
}
let result = limiter.check_rate_limit(ip).await;
assert!(result.is_err(), "Request 6 should fail (exceeds limit)");
}
#[tokio::test]
async fn test_rate_limiter_different_ips() {
let limiter = RateLimiter::new(2, 60);
let ip1: IpAddr = "127.0.0.1".parse().unwrap();
let ip2: IpAddr = "192.168.1.1".parse().unwrap();
assert!(limiter.check_rate_limit(ip1).await.is_ok());
assert!(limiter.check_rate_limit(ip1).await.is_ok());
assert!(limiter.check_rate_limit(ip2).await.is_ok());
assert!(limiter.check_rate_limit(ip2).await.is_ok());
assert!(limiter.check_rate_limit(ip1).await.is_err());
assert!(limiter.check_rate_limit(ip2).await.is_err());
}
#[test]
fn test_input_validator_string_exact_max_length() {
let max_length = 10;
let exact_length_string = "a".repeat(max_length);
let result = InputValidator::validate_string(&exact_length_string, max_length);
assert!(result.is_ok(), "String at exact max_length should be valid");
}
#[test]
fn test_input_validator_string_one_over_max_length() {
let max_length = 10;
let over_length_string = "a".repeat(max_length + 1);
let result = InputValidator::validate_string(&over_length_string, max_length);
assert!(result.is_err(), "String over max_length should be invalid");
assert!(result.unwrap_err().contains("too long"));
}
#[test]
fn test_input_validator_string_one_under_max_length() {
let max_length = 10;
let under_length_string = "a".repeat(max_length - 1);
let result = InputValidator::validate_string(&under_length_string, max_length);
assert!(result.is_ok(), "String under max_length should be valid");
}
#[test]
fn test_input_validator_string_empty() {
let result = InputValidator::validate_string("", 100);
assert!(result.is_ok(), "Empty string should be valid");
}
#[test]
fn test_input_validator_string_sql_injection_patterns() {
let sql_patterns = vec![
"'; DROP TABLE users; --",
"admin' OR '1'='1",
"'; EXEC xp_cmdshell('dir'); --",
];
for pattern in sql_patterns {
let result = InputValidator::validate_string(pattern, 1000);
assert!(
result.is_err(),
"SQL injection pattern should be rejected: {}",
pattern
);
}
}
#[test]
fn test_input_validator_string_xss_patterns() {
let xss_patterns = vec![
"<script>alert('xss')</script>",
"javascript:alert('xss')",
"onerror=alert('xss')",
];
for pattern in xss_patterns {
let result = InputValidator::validate_string(pattern, 1000);
assert!(
result.is_err(),
"XSS pattern should be rejected: {}",
pattern
);
}
}
#[test]
fn test_input_validator_number_exact_min_boundary() {
let min = 10;
let max = 100;
let result = InputValidator::validate_number(min, min, max);
assert!(result.is_ok(), "Value at exact min should be valid");
}
#[test]
fn test_input_validator_number_exact_max_boundary() {
let min = 10;
let max = 100;
let result = InputValidator::validate_number(max, min, max);
assert!(result.is_ok(), "Value at exact max should be valid");
}
#[test]
fn test_input_validator_number_one_below_min() {
let min = 10;
let max = 100;
let result = InputValidator::validate_number(min - 1, min, max);
assert!(result.is_err(), "Value below min should be invalid");
assert!(result.unwrap_err().contains("between"));
}
#[test]
fn test_input_validator_number_one_above_max() {
let min = 10;
let max = 100;
let result = InputValidator::validate_number(max + 1, min, max);
assert!(result.is_err(), "Value above max should be invalid");
assert!(result.unwrap_err().contains("between"));
}
#[test]
fn test_input_validator_number_middle_value() {
let min = 10;
let max = 100;
let middle = (min + max) / 2;
let result = InputValidator::validate_number(middle, min, max);
assert!(result.is_ok(), "Value in middle of range should be valid");
}
#[test]
fn test_input_validator_number_validation_actually_checks() {
let min = 10;
let max = 100;
let invalid_result = InputValidator::validate_number(5, min, max);
assert!(invalid_result.is_err(), "Invalid value should return error");
let valid_result = InputValidator::validate_number(50, min, max);
assert!(valid_result.is_ok(), "Valid value should return Ok");
assert_ne!(invalid_result.is_ok(), valid_result.is_ok());
}
#[test]
fn test_input_validator_number_logical_operator_boundary() {
let min = 10;
let max = 100;
assert!(InputValidator::validate_number(5, min, max).is_err());
assert!(InputValidator::validate_number(200, min, max).is_err());
assert!(InputValidator::validate_number(50, min, max).is_ok());
}
#[test]
fn test_input_validator_sanitize_string_filters_dangerous_chars() {
let dangerous = "test<script>alert('xss')</script>test";
let sanitized = InputValidator::sanitize_string(dangerous);
assert!(
!sanitized.contains("<script>"),
"Should remove <script> tags"
);
assert!(
!sanitized.contains("</script>"),
"Should remove </script> tags"
);
}
#[test]
fn test_input_validator_sanitize_string_preserves_allowed_chars() {
let input = "test-user_name@example.com";
let sanitized = InputValidator::sanitize_string(input);
assert!(sanitized.contains("test"));
assert!(sanitized.contains("-"));
assert!(sanitized.contains("_"));
assert!(sanitized.contains("@"));
assert!(sanitized.contains("."));
}
#[test]
fn test_input_validator_sanitize_string_removes_special_chars() {
let input = "test!@#$%^&*()[]{}|\\:;\"'<>?,./";
let sanitized = InputValidator::sanitize_string(input);
assert!(!sanitized.contains("!"));
assert!(!sanitized.contains("#"));
assert!(!sanitized.contains("$"));
assert!(!sanitized.contains("%"));
assert!(!sanitized.contains("^"));
assert!(!sanitized.contains("&"));
assert!(!sanitized.contains("*"));
assert!(!sanitized.contains("("));
assert!(!sanitized.contains(")"));
}
#[test]
fn test_claims_is_expired_actually_checks() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
let claims = Claims::new(
"user123".to_string(),
vec![],
vec![],
1, );
assert!(
!claims.is_expired(),
"Claims should not be expired immediately"
);
});
}
#[test]
fn test_claims_is_expired_comparison_operator() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
let claims = Claims::new("user123".to_string(), vec![], vec![], 24);
assert!(
!claims.is_expired(),
"Claims with future expiration should not be expired"
);
});
}
#[test]
fn test_claims_expiration_logic() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
let claims_1h = Claims::new("user1".to_string(), vec![], vec![], 1);
let claims_24h = Claims::new("user2".to_string(), vec![], vec![], 24);
let claims_0h = Claims::new("user3".to_string(), vec![], vec![], 0);
assert!(!claims_1h.is_expired());
assert!(!claims_24h.is_expired());
let expired_0h = claims_0h.is_expired();
let _: bool = expired_0h;
});
}
#[test]
fn test_claims_is_expired_exact_boundary_not_expired() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
use chrono::Utc;
let now = Utc::now().timestamp() as usize;
let claims = Claims {
sub: "user123".to_string(),
exp: now, iat: now - 3600, roles: vec![],
permissions: vec![],
};
assert!(
!claims.is_expired(),
"Claims at exact expiration time should not be expired (exp < now is false)"
);
});
}
#[test]
fn test_claims_is_expired_one_second_before() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
use chrono::Utc;
let now = Utc::now().timestamp() as usize;
let claims = Claims {
sub: "user123".to_string(),
exp: now - 1, iat: now - 3600,
roles: vec![],
permissions: vec![],
};
assert!(
claims.is_expired(),
"Claims expired 1 second ago should be expired"
);
});
}
#[test]
fn test_claims_is_expired_one_second_after() {
let rt = Runtime::new().unwrap();
rt.block_on(async {
use chrono::Utc;
let now = Utc::now().timestamp() as usize;
let claims = Claims {
sub: "user123".to_string(),
exp: now + 1, iat: now - 3600,
roles: vec![],
permissions: vec![],
};
assert!(
!claims.is_expired(),
"Claims expiring 1 second in future should not be expired"
);
});
}
#[test]
fn test_security_headers_middleware_function_exists() {
#[allow(unused_imports)]
use dist_agent_lang::http_server_security::security_headers_middleware;
}
#[test]
fn test_input_validator_address_format() {
let valid_address = "0x1234567890123456789012345678901234567890";
let result = InputValidator::validate_address(valid_address);
assert!(result.is_ok(), "Valid address should pass");
let invalid_no_prefix = "1234567890123456789012345678901234567890";
let result = InputValidator::validate_address(invalid_no_prefix);
assert!(result.is_err(), "Address without 0x prefix should fail");
let invalid_length = "0x123456789012345678901234567890123456789";
let result = InputValidator::validate_address(invalid_length);
assert!(result.is_err(), "Address with wrong length should fail");
let invalid_hex = "0x123456789012345678901234567890123456789g";
let result = InputValidator::validate_address(invalid_hex);
assert!(result.is_err(), "Address with invalid hex should fail");
}
#[test]
fn test_request_size_limiter_body_exact_boundary() {
use axum::http::HeaderMap;
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let headers = HeaderMap::new();
let result = limiter.validate_request(&headers, limiter.max_body_size, 100);
assert!(result.is_ok(), "Body at exact max should be valid");
}
#[test]
fn test_request_size_limiter_body_one_over() {
use axum::http::HeaderMap;
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let headers = HeaderMap::new();
let result = limiter.validate_request(&headers, limiter.max_body_size + 1, 100);
assert!(result.is_err(), "Body over max should be invalid");
assert_eq!(
result.unwrap_err(),
axum::http::StatusCode::PAYLOAD_TOO_LARGE
);
}
#[test]
fn test_request_size_limiter_url_exact_boundary() {
use axum::http::HeaderMap;
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let headers = HeaderMap::new();
let result = limiter.validate_request(&headers, 100, limiter.max_url_length);
assert!(result.is_ok(), "URL at exact max should be valid");
}
#[test]
fn test_request_size_limiter_url_one_over() {
use axum::http::HeaderMap;
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let headers = HeaderMap::new();
let result = limiter.validate_request(&headers, 100, limiter.max_url_length + 1);
assert!(result.is_err(), "URL over max should be invalid");
assert_eq!(result.unwrap_err(), axum::http::StatusCode::URI_TOO_LONG);
}
#[test]
fn test_request_size_limiter_header_size_arithmetic() {
use axum::http::{HeaderMap, HeaderName, HeaderValue};
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let mut headers = HeaderMap::new();
let header_name = HeaderName::from_static("test-header");
let header_value = HeaderValue::from_static("test-value");
headers.insert(&header_name, header_value);
let result = limiter.validate_request(&headers, 100, 100);
assert!(
result.is_ok() || result.is_err(),
"Should handle header size calculation"
);
}
#[test]
fn test_request_size_limiter_header_size_exact_arithmetic() {
use axum::http::{HeaderMap, HeaderName, HeaderValue};
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let mut headers = HeaderMap::new();
let header_name = HeaderName::from_static("x-test-123"); let header_value = HeaderValue::from_static("value-1234"); headers.insert(&header_name, header_value);
let result = limiter.validate_request(&headers, 100, 100);
assert!(
result.is_ok(),
"Small headers should be valid (tests arithmetic is +, not *)"
);
}
#[test]
fn test_request_size_limiter_header_size_arithmetic_verification() {
use axum::http::{HeaderMap, HeaderName, HeaderValue};
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let mut headers = HeaderMap::new();
let header1_name = HeaderName::from_static("test1"); let header1_value = HeaderValue::from_static("value"); headers.insert(&header1_name, header1_value);
let header2_name = HeaderName::from_static("test2"); let header2_value = HeaderValue::from_static("value"); headers.insert(&header2_name, header2_value);
let result = limiter.validate_request(&headers, 100, 100);
assert!(
result.is_ok(),
"Headers with correct arithmetic (addition) should be valid"
);
let mut headers2 = HeaderMap::new();
let header_name = HeaderName::from_static("x"); for _i in 0..100 {
let value = HeaderValue::from_static("x"); headers2.insert(&header_name, value);
}
let result2 = limiter.validate_request(&headers2, 100, 100);
assert!(
result2.is_ok(),
"Multiple small headers should be valid with addition"
);
}
#[test]
fn test_request_size_limiter_header_size_exact_calculation_plus_vs_multiply() {
use axum::http::{HeaderMap, HeaderName, HeaderValue};
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter {
max_header_size: 1000, ..Default::default()
};
let mut headers = HeaderMap::new();
for i in 0..100 {
let name = HeaderName::from_static("test"); let value_str = format!("val{}", i); let value = HeaderValue::from_str(&value_str).unwrap();
headers.insert(&name, value);
}
let result = limiter.validate_request(&headers, 100, 100);
assert!(
result.is_ok(),
"Headers with correct addition arithmetic (900 bytes) should be valid"
);
for i in 100..111 {
let name = HeaderName::from_static("test"); let value_str = format!("val{}", i); let value = HeaderValue::from_str(&value_str).unwrap();
headers.insert(&name, value);
}
let result = limiter.validate_request(&headers, 100, 100);
assert!(
result.is_ok(),
"Headers with correct addition arithmetic (~999 bytes) should still be valid"
);
let mut headers_over_limit = HeaderMap::new();
for i in 0..84 {
let name_str = format!("header{}", i); let name = HeaderName::from_bytes(name_str.as_bytes()).unwrap();
let value_str = format!("value{}", i); let value = HeaderValue::from_str(&value_str).unwrap();
headers_over_limit.insert(name, value);
}
let result = limiter.validate_request(&headers_over_limit, 100, 100);
assert!(
result.is_err(),
"Headers exceeding limit (~1176 bytes) should be rejected"
);
let mut headers_critical = HeaderMap::new();
for i in 0..60 {
let name_str = format!("test{}", i); let name = HeaderName::from_bytes(name_str.as_bytes()).unwrap();
let value_str = format!("value{}", i); let value = HeaderValue::from_str(&value_str).unwrap();
headers_critical.insert(name, value);
}
let result_critical = limiter.validate_request(&headers_critical, 100, 100);
assert!(
result_critical.is_ok(),
"Headers with correct addition (~720 bytes) should be valid - catches + vs * mutation"
);
}
#[test]
fn test_request_size_limiter_header_size_boundary_comparison() {
use axum::http::{HeaderMap, HeaderName, HeaderValue};
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let mut headers = HeaderMap::new();
let header_name = HeaderName::from_static("x-test");
let value_size = limiter.max_header_size.saturating_sub(10); let large_value = "x".repeat(value_size);
let header_value = HeaderValue::from_bytes(large_value.as_bytes()).unwrap();
headers.insert(&header_name, header_value);
let result = limiter.validate_request(&headers, 100, 100);
assert!(
result.is_ok() || result.is_err(),
"Should handle header size boundary correctly"
);
}
#[test]
fn test_request_size_limiter_header_size_boundary() {
use axum::http::{HeaderMap, HeaderName, HeaderValue};
use dist_agent_lang::http_server_security::RequestSizeLimiter;
let limiter = RequestSizeLimiter::default();
let mut headers = HeaderMap::new();
let header_name = HeaderName::from_static("x-test");
let large_value = "x".repeat(limiter.max_header_size - 10); let header_value = HeaderValue::from_bytes(large_value.as_bytes()).unwrap();
headers.insert(&header_name, header_value);
let result = limiter.validate_request(&headers, 100, 100);
assert!(
result.is_ok() || result.is_err(),
"Should handle header size boundary"
);
}
#[test]
fn test_security_logger_log_event_actually_logs() {
use dist_agent_lang::http_server_security::SecurityLogger;
SecurityLogger::log_event("TEST_EVENT", "test details", Some("127.0.0.1"));
SecurityLogger::log_event("ANOTHER_EVENT", "different details", Some("192.168.1.1"));
SecurityLogger::log_event("THIRD_EVENT", "more details", None);
}
#[test]
fn test_security_logger_log_rate_limit() {
use dist_agent_lang::http_server_security::SecurityLogger;
SecurityLogger::log_rate_limit("127.0.0.1");
SecurityLogger::log_rate_limit("192.168.1.1");
}
#[test]
fn test_security_logger_log_auth_failure() {
use dist_agent_lang::http_server_security::SecurityLogger;
SecurityLogger::log_auth_failure("127.0.0.1", "invalid token");
SecurityLogger::log_auth_failure("192.168.1.1", "expired token");
}
#[test]
fn test_security_logger_log_invalid_input() {
use dist_agent_lang::http_server_security::SecurityLogger;
SecurityLogger::log_invalid_input("127.0.0.1", "malformed input");
SecurityLogger::log_invalid_input("192.168.1.1", "sql injection attempt");
}
#[test]
fn test_security_logger_log_auth_success() {
use dist_agent_lang::http_server_security::SecurityLogger;
SecurityLogger::log_auth_success("127.0.0.1", "user123");
SecurityLogger::log_auth_success("192.168.1.1", "user456");
}
#[test]
fn test_security_logger_log_suspicious_activity() {
use dist_agent_lang::http_server_security::SecurityLogger;
SecurityLogger::log_suspicious_activity("127.0.0.1", "multiple failed logins");
SecurityLogger::log_suspicious_activity("192.168.1.1", "unusual access pattern");
}
#[test]
fn test_security_logger_log_token_validation_failure() {
use dist_agent_lang::http_server_security::SecurityLogger;
SecurityLogger::log_token_validation_failure("127.0.0.1", "expired token");
SecurityLogger::log_token_validation_failure("192.168.1.1", "invalid signature");
}