use crate::rpc::auth::{RpcAuthManager, UserId};
use std::net::SocketAddr;
use tokio::time::{sleep, Duration};
#[tokio::test]
async fn test_rest_api_auth_required() {
let auth = RpcAuthManager::new(true);
auth.add_token("test-token".to_string()).await.unwrap();
let mut headers = hyper::HeaderMap::new();
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
let result = auth.authenticate_request(&headers, addr).await;
assert!(result.user_id.is_none());
assert!(result.error.is_some());
assert!(result.requires_auth);
headers.insert("authorization", "Bearer invalid-token".parse().unwrap());
let result = auth.authenticate_request(&headers, addr).await;
assert!(result.user_id.is_none());
assert!(result.error.is_some());
headers.insert("authorization", "Bearer test-token".parse().unwrap());
let result = auth.authenticate_request(&headers, addr).await;
assert!(result.user_id.is_some());
assert!(result.error.is_none());
}
#[tokio::test]
async fn test_rest_api_auth_optional() {
let auth = RpcAuthManager::new(false);
let headers = hyper::HeaderMap::new();
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
let result = auth.authenticate_request(&headers, addr).await;
assert!(result.user_id.is_some());
assert!(!result.requires_auth);
assert!(result.error.is_none());
}
#[tokio::test]
async fn test_rest_api_rate_limiting_with_endpoint() {
let auth = RpcAuthManager::with_rate_limits(false, 5, 1); let user_id = UserId::Ip("127.0.0.1:8080".parse().unwrap());
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
for i in 0..5 {
assert!(
auth.check_rate_limit_with_endpoint(&user_id, Some(addr), Some("/api/v1/blocks/123"))
.await,
"Request {} should be allowed",
i + 1
);
}
assert!(
!auth
.check_rate_limit_with_endpoint(&user_id, Some(addr), Some("/api/v1/blocks/123"))
.await,
"6th request should be rate limited"
);
}
#[tokio::test]
async fn test_rest_api_ip_rate_limiting() {
let auth = RpcAuthManager::with_rate_limits(false, 5, 1); let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
for i in 0..5 {
assert!(
auth.check_ip_rate_limit_with_endpoint(addr, Some("/api/v1/chain/info"))
.await,
"Request {} should be allowed",
i + 1
);
}
assert!(
!auth
.check_ip_rate_limit_with_endpoint(addr, Some("/api/v1/chain/info"))
.await,
"Request should be rate limited after IP limit"
);
}
#[tokio::test]
async fn test_brute_force_detection() {
let auth = RpcAuthManager::new(true);
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
for i in 0..10 {
let mut headers = hyper::HeaderMap::new();
headers.insert(
"authorization",
format!("Bearer invalid-token-{i}").parse().unwrap(),
);
let result = auth.authenticate_request(&headers, addr).await;
assert!(result.user_id.is_none());
assert!(result.error.is_some());
}
}
#[tokio::test]
async fn test_input_validation_transaction_hex() {
use crate::rpc::rest::validation;
assert!(validation::validate_transaction_hex("deadbeef").is_ok());
assert!(validation::validate_transaction_hex("deadbee").is_err());
assert!(validation::validate_transaction_hex("deadbeef!").is_err());
assert!(validation::validate_transaction_hex("").is_err());
}
#[tokio::test]
async fn test_input_validation_block_hash() {
use crate::rpc::rest::validation;
let valid_hash = "0".repeat(64);
assert!(validation::validate_hash_string(&valid_hash).is_ok());
assert!(validation::validate_hash_string("deadbeef").is_err());
let odd_hash = "0".repeat(63);
assert!(validation::validate_hash_string(&odd_hash).is_err());
}
#[tokio::test]
async fn test_input_validation_address() {
use crate::rpc::rest::validation;
assert!(
validation::validate_address_string("bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh").is_ok()
);
assert!(validation::validate_address_string("").is_err());
}
#[tokio::test]
async fn test_input_validation_block_height() {
use crate::rpc::rest::validation;
assert!(validation::validate_block_height(0).is_ok());
assert!(validation::validate_block_height(800000).is_ok());
assert!(validation::validate_block_height(3_000_000_000).is_err());
}
#[tokio::test]
async fn test_security_event_logging() {
use crate::rpc::auth::SecurityEvent;
use std::net::SocketAddr;
let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
let event = SecurityEvent::AuthFailure {
client_addr: addr,
reason: "Invalid token".to_string(),
};
event.log();
let event = SecurityEvent::RateLimitViolation {
user_id: "test-user".to_string(),
client_addr: addr,
endpoint: "/api/v1/blocks/123".to_string(),
};
event.log();
let event = SecurityEvent::RepeatedAuthFailures {
client_addr: addr,
failure_count: 5,
time_window_seconds: 300,
};
event.log();
let event = SecurityEvent::AuthSuccess {
user_id: "test-user".to_string(),
client_addr: addr,
auth_method: "token".to_string(),
};
event.log(); }
#[tokio::test]
async fn test_rest_oversized_body_returns_error() {
use crate::rpc::rest::types::MAX_REQUEST_SIZE;
use bytes::Bytes;
use http_body_util::{BodyExt, Full, Limited};
let oversized = vec![0u8; MAX_REQUEST_SIZE + 1];
let body = Full::new(Bytes::from(oversized));
let limited = Limited::new(body, MAX_REQUEST_SIZE);
let result = limited.collect().await;
assert!(
result.is_err(),
"Oversized body (>1MB) should fail when collected with size limit"
);
}
#[tokio::test]
async fn test_rate_limiting_time_window() {
let auth = RpcAuthManager::with_rate_limits(false, 5, 1); let user_id = UserId::Ip("127.0.0.1:8080".parse().unwrap());
for _ in 0..5 {
assert!(auth.check_rate_limit(&user_id).await);
}
assert!(!auth.check_rate_limit(&user_id).await);
sleep(Duration::from_secs(2)).await;
assert!(auth.check_rate_limit(&user_id).await);
assert!(auth.check_rate_limit(&user_id).await);
assert!(!auth.check_rate_limit(&user_id).await);
}