#[cfg(test)]
mod readme_examples_test {
use rate_guard::{Nanos, Millis, Secs, MockTimeSource, RateLimit};
use rate_guard::limits::{
TokenBucketBuilder, FixedWindowCounterBuilder,
SlidingWindowCounterBuilder, ApproximateSlidingWindowBuilder
};
use std::time::Duration;
#[test]
fn test_readme_quick_start_example() {
let bucket = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build()
.unwrap();
match bucket.try_acquire(1) {
Ok(()) => println!("Request allowed"),
Err(_) => println!("Rate limited"),
}
println!("Remaining: {}", bucket.capacity_remaining().unwrap());
assert!(bucket.try_acquire(50).is_ok());
assert_eq!(bucket.capacity_remaining().unwrap(), 49);
}
#[test]
fn test_readme_token_bucket_example() {
let time_source = MockTimeSource::new();
let bucket = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(time_source.clone())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(bucket.try_acquire(50).is_ok());
time_source.advance(Duration::from_millis(200));
assert_eq!(bucket.capacity_remaining().unwrap(), 70); }
#[test]
fn test_readme_fixed_window_counter_example() {
let counter = FixedWindowCounterBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Secs>()
.build()
.unwrap();
assert!(counter.try_acquire(50).is_ok());
}
#[test]
fn test_readme_sliding_window_counter_example() {
let counter = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::from_secs(10))
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(counter.try_acquire(25).is_ok());
}
#[test]
fn test_readme_approximate_sliding_window_example() {
let window = ApproximateSlidingWindowBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(window.try_acquire(30).is_ok());
}
#[test]
fn test_readme_verbose_error_handling_example() {
use rate_guard::error::RateLimitError;
let bucket = TokenBucketBuilder::builder()
.capacity(10)
.refill_amount(1)
.refill_every(Duration::from_secs(1))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
bucket.try_acquire(10).unwrap();
match bucket.try_acquire_verbose(5) {
Ok(()) => println!("Request allowed"),
Err(RateLimitError::InsufficientCapacity { acquiring, available, retry_after }) => {
println!("Need {} tokens, only {} available, retry after {:?}",
acquiring, available, retry_after);
assert_eq!(acquiring, 5);
assert_eq!(available, 0);
}
Err(e) => panic!("Unexpected error: {}", e),
}
}
#[test]
fn test_readme_testing_with_mock_time_example() {
let time_source = MockTimeSource::new();
let bucket = TokenBucketBuilder::builder()
.capacity(50)
.refill_amount(5)
.refill_every(Duration::from_millis(200))
.with_time(time_source.clone())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(bucket.try_acquire(50).is_ok());
assert_eq!(bucket.capacity_remaining().unwrap(), 0);
time_source.advance(Duration::from_millis(200));
assert_eq!(bucket.capacity_remaining().unwrap(), 5);
}
#[test]
fn test_readme_precision_types_example() {
use rate_guard::{Nanos, Millis, Secs, MockTimeSource};
let nano_bucket = TokenBucketBuilder::builder()
.capacity(100).refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build().unwrap();
let milli_bucket = TokenBucketBuilder::builder()
.capacity(100).refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build().unwrap();
let sec_bucket = TokenBucketBuilder::builder()
.capacity(100).refill_amount(10)
.refill_every(Duration::from_secs(1))
.with_time(MockTimeSource::new())
.with_precision::<Secs>()
.build().unwrap();
assert!(nano_bucket.try_acquire(10).is_ok());
assert!(milli_bucket.try_acquire(10).is_ok());
assert!(sec_bucket.try_acquire(10).is_ok());
}
#[test]
fn test_lib_rs_all_rate_limiter_types_example() {
let time_source = MockTimeSource::new();
let token_bucket = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(time_source.clone())
.with_precision::<Millis>()
.build()
.unwrap();
let fixed_window = FixedWindowCounterBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(time_source.clone())
.with_precision::<Millis>()
.build()
.unwrap();
let sliding_window = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::from_secs(10))
.bucket_count(6)
.with_time(time_source.clone())
.with_precision::<Millis>()
.build()
.unwrap();
let approx_window = ApproximateSlidingWindowBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(time_source)
.with_precision::<Millis>()
.build()
.unwrap();
assert!(token_bucket.try_acquire(10).is_ok());
assert!(fixed_window.try_acquire(10).is_ok());
assert!(sliding_window.try_acquire(10).is_ok());
assert!(approx_window.try_acquire(10).is_ok());
}
#[test]
#[cfg(feature = "std-time")]
fn test_readme_production_usage_with_real_time() {
use rate_guard::{Nanos, StdTimeSource, RateLimit};
let limiter = TokenBucketBuilder::builder()
.capacity(1000)
.refill_amount(100)
.refill_every(Duration::from_millis(100))
.with_time(StdTimeSource::new())
.with_precision::<Nanos>()
.build()
.unwrap();
assert!(limiter.try_acquire(10).is_ok());
}
}
#[cfg(test)]
mod individual_module_doc_examples {
use rate_guard::{Nanos, Millis, MockTimeSource, RateLimit};
use rate_guard::limits::{
TokenBucketBuilder, FixedWindowCounterBuilder,
SlidingWindowCounterBuilder, ApproximateSlidingWindowBuilder
};
use std::time::Duration;
#[test]
fn test_token_bucket_module_example() {
let bucket = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build()
.unwrap();
assert!(bucket.try_acquire(50).is_ok());
}
#[test]
fn test_fixed_window_counter_module_example() {
let counter = FixedWindowCounterBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(counter.try_acquire(50).is_ok());
}
#[test]
fn test_sliding_window_counter_module_example() {
let counter = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::from_secs(10))
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(counter.try_acquire(50).is_ok());
}
#[test]
fn test_approximate_sliding_window_module_example() {
let window = ApproximateSlidingWindowBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(window.try_acquire(50).is_ok());
}
}
#[cfg(test)]
mod builder_method_doc_examples {
use rate_guard::{Nanos, MockTimeSource, RateLimit};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;
#[test]
fn test_token_bucket_builder_example() {
let bucket = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build()
.unwrap();
assert!(bucket.try_acquire(50).is_ok());
}
}