Files
tranquil-pds/tests/rate_limit.rs
T

199 lines
6.1 KiB
Rust

mod common;
use common::{base_url, client};
use reqwest::StatusCode;
use serde_json::json;
#[tokio::test]
#[ignore = "rate limiting is disabled in test environment"]
async fn test_login_rate_limiting() {
let client = client();
let url = format!("{}/xrpc/com.atproto.server.createSession", base_url().await);
let payload = json!({
"identifier": "nonexistent_user_for_rate_limit_test",
"password": "wrongpassword"
});
let mut rate_limited_count = 0;
let mut auth_failed_count = 0;
for _ in 0..15 {
let res = client
.post(&url)
.json(&payload)
.send()
.await
.expect("Request failed");
match res.status() {
StatusCode::TOO_MANY_REQUESTS => {
rate_limited_count += 1;
}
StatusCode::UNAUTHORIZED => {
auth_failed_count += 1;
}
status => {
panic!("Unexpected status: {}", status);
}
}
}
assert!(
rate_limited_count > 0,
"Expected at least one rate-limited response after 15 login attempts. Got {} auth failures and {} rate limits.",
auth_failed_count,
rate_limited_count
);
}
#[tokio::test]
#[ignore = "rate limiting is disabled in test environment"]
async fn test_password_reset_rate_limiting() {
let client = client();
let url = format!(
"{}/xrpc/com.atproto.server.requestPasswordReset",
base_url().await
);
let mut rate_limited_count = 0;
let mut success_count = 0;
for i in 0..8 {
let payload = json!({
"email": format!("ratelimit_test_{}@example.com", i)
});
let res = client
.post(&url)
.json(&payload)
.send()
.await
.expect("Request failed");
match res.status() {
StatusCode::TOO_MANY_REQUESTS => {
rate_limited_count += 1;
}
StatusCode::OK => {
success_count += 1;
}
status => {
panic!("Unexpected status: {} - {:?}", status, res.text().await);
}
}
}
assert!(
rate_limited_count > 0,
"Expected rate limiting after {} password reset requests. Got {} successes.",
success_count + rate_limited_count,
success_count
);
}
#[tokio::test]
#[ignore = "rate limiting is disabled in test environment"]
async fn test_account_creation_rate_limiting() {
let client = client();
let url = format!(
"{}/xrpc/com.atproto.server.createAccount",
base_url().await
);
let mut rate_limited_count = 0;
let mut other_count = 0;
for i in 0..15 {
let unique_id = uuid::Uuid::new_v4();
let payload = json!({
"handle": format!("ratelimit_{}_{}", i, unique_id),
"email": format!("ratelimit_{}_{}@example.com", i, unique_id),
"password": "testpassword123"
});
let res = client
.post(&url)
.json(&payload)
.send()
.await
.expect("Request failed");
match res.status() {
StatusCode::TOO_MANY_REQUESTS => {
rate_limited_count += 1;
}
_ => {
other_count += 1;
}
}
}
assert!(
rate_limited_count > 0,
"Expected rate limiting after account creation attempts. Got {} other responses and {} rate limits.",
other_count,
rate_limited_count
);
}
#[tokio::test]
async fn test_valkey_connection() {
if std::env::var("VALKEY_URL").is_err() {
println!("VALKEY_URL not set, skipping Valkey connection test");
return;
}
let valkey_url = std::env::var("VALKEY_URL").unwrap();
let client = redis::Client::open(valkey_url.as_str()).expect("Failed to create Redis client");
let mut conn = client
.get_multiplexed_async_connection()
.await
.expect("Failed to connect to Valkey");
let pong: String = redis::cmd("PING")
.query_async(&mut conn)
.await
.expect("PING failed");
assert_eq!(pong, "PONG");
let _: () = redis::cmd("SET")
.arg("test_key")
.arg("test_value")
.arg("EX")
.arg(10)
.query_async(&mut conn)
.await
.expect("SET failed");
let value: String = redis::cmd("GET")
.arg("test_key")
.query_async(&mut conn)
.await
.expect("GET failed");
assert_eq!(value, "test_value");
let _: () = redis::cmd("DEL")
.arg("test_key")
.query_async(&mut conn)
.await
.expect("DEL failed");
}
#[tokio::test]
async fn test_distributed_rate_limiter_directly() {
if std::env::var("VALKEY_URL").is_err() {
println!("VALKEY_URL not set, skipping distributed rate limiter test");
return;
}
use bspds::cache::{DistributedRateLimiter, RedisRateLimiter};
let valkey_url = std::env::var("VALKEY_URL").unwrap();
let client = redis::Client::open(valkey_url.as_str()).expect("Failed to create Redis client");
let conn = client
.get_connection_manager()
.await
.expect("Failed to get connection manager");
let rate_limiter = RedisRateLimiter::new(conn);
let test_key = format!("test_rate_limit_{}", uuid::Uuid::new_v4());
let limit = 5;
let window_ms = 60_000;
for i in 0..limit {
let allowed = rate_limiter
.check_rate_limit(&test_key, limit, window_ms)
.await;
assert!(
allowed,
"Request {} should have been allowed (limit: {})",
i + 1,
limit
);
}
let allowed = rate_limiter
.check_rate_limit(&test_key, limit, window_ms)
.await;
assert!(
!allowed,
"Request {} should have been rate limited (limit: {})",
limit + 1,
limit
);
let allowed = rate_limiter
.check_rate_limit(&test_key, limit, window_ms)
.await;
assert!(!allowed, "Subsequent request should also be rate limited");
}