rate-guard 0.1.0

Thread-safe rate limiting library with multiple algorithms and Duration-based configuration
Documentation
//! Tests for documentation examples to ensure they compile and work correctly.
//!
//! This module contains tests that verify all documentation examples in README.md
//! and other documentation actually compile and behave as expected.

#[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() {
        // This is the main example from README.md Quick Start section
        let bucket = TokenBucketBuilder::builder()
            .capacity(100)
            .refill_amount(10)
            .refill_every(Duration::from_millis(100))
            .with_time(MockTimeSource::new())
            .with_precision::<Nanos>()
            .build()
            .unwrap();

        // Check if request is allowed
        match bucket.try_acquire(1) {
            Ok(()) => println!("Request allowed"),
            Err(_) => println!("Rate limited"),
        }

        // Check remaining capacity
        println!("Remaining: {}", bucket.capacity_remaining().unwrap());
        
        // Verify it works as expected
        assert!(bucket.try_acquire(50).is_ok());
        assert_eq!(bucket.capacity_remaining().unwrap(), 49);
    }

    #[test]
    fn test_readme_token_bucket_example() {
        // Token Bucket example from README.md
        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); // 50 + 20 refill
    }

    #[test]
    fn test_readme_fixed_window_counter_example() {
        // Fixed Window Counter example from README.md
        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() {
        // Sliding Window Counter example from README.md
        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() {
        // Approximate Sliding Window example from README.md
        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();

        // Verify all buckets work
        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() {
        // Example from lib.rs documentation
        let time_source = MockTimeSource::new();

        // Token Bucket - allows bursts
        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();

        // Fixed Window Counter
        let fixed_window = FixedWindowCounterBuilder::builder()
            .capacity(100)
            .window_duration(Duration::from_secs(60))
            .with_time(time_source.clone())
            .with_precision::<Millis>()
            .build()
            .unwrap();

        // Sliding Window Counter
        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();

        // Approximate Sliding Window
        let approx_window = ApproximateSlidingWindowBuilder::builder()
            .capacity(100)
            .window_duration(Duration::from_secs(60))
            .with_time(time_source)
            .with_precision::<Millis>()
            .build()
            .unwrap();

        // Verify all rate limiters work
        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();

        // Just verify it compiles and can be created
        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() {
        // Example from token_bucket.rs module documentation
        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() {
        // Example from fixed_window_counter.rs module documentation
        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() {
        // Example from sliding_window_counter.rs module documentation
        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() {
        // Example from approximate_sliding_window.rs module documentation
        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() {
        // Example from TokenBucketBuilder::builder() documentation
        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());
    }
}