mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-28 05:04:14 +00:00
199 lines
6.1 KiB
Rust
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");
|
|
}
|