use rate_guard::{Nanos, Millis, MockTimeSource};
use rate_guard::limits::{
TokenBucketBuilder, FixedWindowCounterBuilder,
SlidingWindowCounterBuilder, ApproximateSlidingWindowBuilder
};
use rate_guard::error::BuildError;
use std::time::Duration;
#[cfg(test)]
mod token_bucket_build_errors {
use super::*;
#[test]
fn test_missing_capacity() {
let result = TokenBucketBuilder::builder()
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("capacity"))));
}
#[test]
fn test_missing_refill_amount() {
let result = TokenBucketBuilder::builder()
.capacity(100)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("refill_amount"))));
}
#[test]
fn test_missing_refill_every() {
let result = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("refill_every"))));
}
#[test]
fn test_zero_capacity() {
let result = TokenBucketBuilder::builder()
.capacity(0)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "capacity", reason: "must be greater than 0" })
));
}
#[test]
fn test_zero_refill_amount() {
let result = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(0)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "refill_amount", reason: "must be greater than 0" })
));
}
#[test]
fn test_zero_refill_every() {
let result = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::ZERO)
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "refill_every", reason: "must be greater than zero" })
));
}
#[test]
fn test_refill_amount_exceeds_capacity() {
let result = TokenBucketBuilder::builder()
.capacity(10)
.refill_amount(20) .refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument {
field: "refill_amount",
reason: "should not exceed capacity for optimal rate limiting behavior"
})
));
}
#[test]
fn test_valid_configuration() {
let result = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build();
assert!(result.is_ok());
}
}
#[cfg(test)]
mod fixed_window_counter_build_errors {
use super::*;
#[test]
fn test_missing_capacity() {
let result = FixedWindowCounterBuilder::builder()
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("capacity"))));
}
#[test]
fn test_missing_window_duration() {
let result = FixedWindowCounterBuilder::builder()
.capacity(100)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("window_duration"))));
}
#[test]
fn test_zero_capacity() {
let result = FixedWindowCounterBuilder::builder()
.capacity(0)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "capacity", reason: "must be greater than 0" })
));
}
#[test]
fn test_zero_window_duration() {
let result = FixedWindowCounterBuilder::builder()
.capacity(100)
.window_duration(Duration::ZERO)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "window_duration", reason: "must be greater than zero" })
));
}
#[test]
fn test_valid_configuration() {
let result = FixedWindowCounterBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(result.is_ok());
}
}
#[cfg(test)]
mod sliding_window_counter_build_errors {
use super::*;
#[test]
fn test_missing_capacity() {
let result = SlidingWindowCounterBuilder::builder()
.bucket_duration(Duration::from_secs(10))
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("capacity"))));
}
#[test]
fn test_missing_bucket_duration() {
let result = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("bucket_duration"))));
}
#[test]
fn test_missing_bucket_count() {
let result = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::from_secs(10))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("bucket_count"))));
}
#[test]
fn test_zero_capacity() {
let result = SlidingWindowCounterBuilder::builder()
.capacity(0)
.bucket_duration(Duration::from_secs(10))
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "capacity", reason: "must be greater than 0" })
));
}
#[test]
fn test_zero_bucket_duration() {
let result = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::ZERO)
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "bucket_duration", reason: "must be greater than zero" })
));
}
#[test]
fn test_zero_bucket_count() {
let result = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::from_secs(10))
.bucket_count(0)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "bucket_count", reason: "must be greater than 0" })
));
}
#[test]
fn test_valid_configuration() {
let result = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::from_secs(10))
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(result.is_ok());
}
}
#[cfg(test)]
mod approximate_sliding_window_build_errors {
use super::*;
#[test]
fn test_missing_capacity() {
let result = ApproximateSlidingWindowBuilder::builder()
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("capacity"))));
}
#[test]
fn test_missing_window_duration() {
let result = ApproximateSlidingWindowBuilder::builder()
.capacity(100)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(result, Err(BuildError::MissingArgument("window_duration"))));
}
#[test]
fn test_zero_capacity() {
let result = ApproximateSlidingWindowBuilder::builder()
.capacity(0)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "capacity", reason: "must be greater than 0" })
));
}
#[test]
fn test_zero_window_duration() {
let result = ApproximateSlidingWindowBuilder::builder()
.capacity(100)
.window_duration(Duration::ZERO)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(matches!(
result,
Err(BuildError::InvalidArgument { field: "window_duration", reason: "must be greater than zero" })
));
}
#[test]
fn test_valid_configuration() {
let result = ApproximateSlidingWindowBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build();
assert!(result.is_ok());
}
}
#[cfg(test)]
mod build_error_display_tests {
use super::*;
#[test]
fn test_missing_argument_display() {
let error = BuildError::MissingArgument("capacity");
assert_eq!(error.to_string(), "Missing required argument: capacity");
}
#[test]
fn test_invalid_argument_display() {
let error = BuildError::InvalidArgument {
field: "capacity",
reason: "must be greater than 0"
};
assert_eq!(error.to_string(), "Invalid argument 'capacity': must be greater than 0");
}
}