#![cfg(feature = "clock")]
#![allow(clippy::unwrap_used)]
use std::sync::Arc;
use std::time::Duration;
use better_bucket::{Bucket, BucketConfig, Decision, TokenBucket};
use clock_lib::ManualClock;
#[derive(Debug, PartialEq, Eq)]
enum Admission {
Allow,
Deny { retry_after: Duration },
}
fn check(bucket: &dyn TokenBucket, cost: u32) -> Admission {
match bucket.acquire(cost) {
Decision::Allowed => Admission::Allow,
Decision::Denied { retry_after } => Admission::Deny { retry_after },
_ => Admission::Deny {
retry_after: Duration::MAX,
},
}
}
#[test]
fn test_consumer_checks_through_the_trait() {
let clock = Arc::new(ManualClock::new());
let bucket = Bucket::per_second(2).with_clock(Arc::clone(&clock));
assert_eq!(check(&bucket, 1), Admission::Allow);
assert_eq!(check(&bucket, 1), Admission::Allow);
assert_eq!(
check(&bucket, 1),
Admission::Deny {
retry_after: Duration::from_millis(500)
}
);
clock.advance(Duration::from_millis(500));
assert_eq!(check(&bucket, 1), Admission::Allow);
}
#[test]
fn test_consumer_holds_buckets_with_different_clocks_behind_the_trait() {
let system = Bucket::per_second(4);
let manual = Bucket::per_second(4).with_clock(Arc::new(ManualClock::new()));
let limiters: [&dyn TokenBucket; 2] = [&system, &manual];
for limiter in limiters {
assert_eq!(limiter.capacity(), 4);
assert!(limiter.try_acquire(4));
assert!(!limiter.try_acquire(1));
}
}
#[test]
fn test_consumer_per_key_buckets_share_one_clock() {
let clock = Arc::new(ManualClock::new());
let make = |rate: u32| Bucket::per_second(rate).with_clock(Arc::clone(&clock));
let alice = make(3);
let bob = make(1);
assert!(alice.try_acquire(3));
assert!(bob.try_acquire(1));
assert!(!alice.try_acquire(1));
assert!(!bob.try_acquire(1));
clock.advance(Duration::from_secs(1));
assert_eq!(alice.available(), 3);
assert_eq!(bob.available(), 1);
}
#[test]
fn test_consumer_configures_via_config_then_injects_clock() {
let clock = Arc::new(ManualClock::new());
let config = BucketConfig::new(100, 10, Duration::from_secs(1), 0).unwrap();
let bucket = Bucket::from_config(config).with_clock(Arc::clone(&clock));
assert_eq!(bucket.available(), 0); clock.advance(Duration::from_secs(1));
assert_eq!(bucket.available(), 10); }