fix: cover api edge cases
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user