fix: cover api edge cases

This commit is contained in:
2026-06-13 10:44:47 +02:00
parent aa3b678cc5
commit ce959b59bf
15 changed files with 400 additions and 36 deletions

View File

@@ -1,5 +1,8 @@
use std::{sync::Mutex, time::Instant};
// This challenge uses a simple in-memory global limiter. In production,
// prefer API gateway rate limiting so limits are shared across instances and
// identity-aware policies can be keyed by API key, user, or client IP.
#[derive(Debug)]
pub struct RateLimiter {
bucket: Mutex<TokenBucket>,
@@ -7,7 +10,7 @@ pub struct RateLimiter {
impl RateLimiter {
pub fn new(refill_per_second: f64, burst: u32) -> Result<Self, RateLimitError> {
if !refill_per_second.is_sign_positive() || burst == 0 {
if !refill_per_second.is_finite() || refill_per_second <= 0.0 || burst == 0 {
return Err(RateLimitError::InvalidConfiguration);
}
@@ -69,6 +72,13 @@ mod tests {
use super::RateLimiter;
use std::time::Duration;
#[test]
fn rejects_non_positive_or_non_finite_configuration() {
assert!(RateLimiter::new(0.0, 1).is_err());
assert!(RateLimiter::new(f64::INFINITY, 1).is_err());
assert!(RateLimiter::new(1.0, 0).is_err());
}
#[test]
fn allows_requests_up_to_the_burst_capacity() {
let limiter = RateLimiter::new(100.0, 2).expect("rate limiter should build");
@@ -84,6 +94,8 @@ mod tests {
assert!(limiter.try_acquire());
assert!(!limiter.try_acquire());
// A production-grade limiter would inject a clock; a short sleep keeps
// this challenge implementation simple without coupling tests to internals.
std::thread::sleep(Duration::from_millis(20));
assert!(limiter.try_acquire());
}