"""Tests for infra.throttler — Throttler, TokenBucketStrategy.""" import time import pytest from infra.throttler import Throttler, TokenBucketStrategy class TestTokenBucketStrategy: def test_initial_tokens_equal_capacity(self): strategy = TokenBucketStrategy(capacity=5, refill_rate=1.0) for _ in range(5): success, _ = strategy.try_acquire() assert success is True success, retry = strategy.try_acquire() assert success is False assert retry > 0 def test_single_token_blocks_immediately(self): strategy = TokenBucketStrategy(capacity=1, refill_rate=1.0) assert strategy.try_acquire() == (True, 0.0) success, _ = strategy.try_acquire() assert success is False def test_refills_over_time(self): strategy = TokenBucketStrategy(capacity=1, refill_rate=100.0) strategy.try_acquire() # drain time.sleep(0.02) # 20ms at 100/s = ~2 tokens success, _ = strategy.try_acquire() assert success is True def test_clear_resets_to_capacity(self): strategy = TokenBucketStrategy(capacity=3, refill_rate=1.0) for _ in range(3): strategy.try_acquire() strategy.clear() success, _ = strategy.try_acquire() assert success is True def test_invalid_capacity_raises(self): with pytest.raises(ValueError, match='capacity'): TokenBucketStrategy(capacity=0, refill_rate=1.0) def test_invalid_refill_rate_raises(self): with pytest.raises(ValueError, match='refill_rate'): TokenBucketStrategy(capacity=1, refill_rate=0) class TestThrottler: def test_acquire_blocks_until_available(self): strategy = TokenBucketStrategy(capacity=1, refill_rate=100.0) throttler = Throttler(strategy) throttler.acquire() # drain first token start = time.monotonic() throttler.acquire() # should block briefly elapsed = time.monotonic() - start assert elapsed < 0.1 # fast refill, should be near-instant def test_try_acquire_non_blocking(self): strategy = TokenBucketStrategy(capacity=1, refill_rate=0.1) throttler = Throttler(strategy) assert throttler.try_acquire() is True assert throttler.try_acquire() is False def test_clear_delegates(self): strategy = TokenBucketStrategy(capacity=1, refill_rate=0.1) throttler = Throttler(strategy) throttler.try_acquire() # drain throttler.clear() assert throttler.try_acquire() is True