mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-04-28 11:30:03 +00:00
159 lines
5.4 KiB
Python
159 lines
5.4 KiB
Python
import pytest
|
|
import pytest_asyncio
|
|
import asyncio
|
|
import time
|
|
import os
|
|
import logging
|
|
|
|
from providers.rate_limit import GlobalRateLimiter
|
|
|
|
# Configure logging for tests
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TestProviderRateLimiter:
|
|
"""Tests for providers.rate_limit.GlobalRateLimiter."""
|
|
|
|
@pytest_asyncio.fixture(autouse=True)
|
|
async def reset_limiter(self):
|
|
"""Reset singleton before each test."""
|
|
GlobalRateLimiter.reset_instance()
|
|
yield
|
|
GlobalRateLimiter.reset_instance()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proactive_throttling(self):
|
|
"""
|
|
Test proactive throttling using aiolimiter.
|
|
Logic ported from verify_provider_limiter.py
|
|
"""
|
|
# Re-init with tight limits: 1 request per 0.25 second
|
|
GlobalRateLimiter.reset_instance()
|
|
limiter = GlobalRateLimiter.get_instance(rate_limit=1, rate_window=0.25)
|
|
|
|
start_time = time.time()
|
|
|
|
async def call_limiter():
|
|
await limiter.wait_if_blocked()
|
|
return time.time()
|
|
|
|
# 5 requests.
|
|
# R0 -> 0s
|
|
# R1 -> 0.25s
|
|
# R2 -> 0.50s
|
|
# R3 -> 0.75s
|
|
# R4 -> 1.00s
|
|
results = []
|
|
for _ in range(5):
|
|
results.append(await call_limiter())
|
|
|
|
total_time = time.time() - start_time
|
|
|
|
assert len(results) == 5
|
|
# Should take at least ~1.0s
|
|
assert total_time >= 0.9, f"Throttling failed, took too fast: {total_time:.2f}s"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reactive_blocking(self):
|
|
"""
|
|
Test reactive blocking when set_blocked is called.
|
|
Logic ported from verify_provider_limiter.py
|
|
"""
|
|
GlobalRateLimiter.reset_instance()
|
|
limiter = GlobalRateLimiter.get_instance()
|
|
|
|
start_time = time.time()
|
|
|
|
# Manually block for 1.5s
|
|
block_time = 1.5
|
|
limiter.set_blocked(block_time)
|
|
|
|
assert limiter.is_blocked()
|
|
|
|
async def call_limiter():
|
|
return await limiter.wait_if_blocked()
|
|
|
|
# Run 2 calls concurrently
|
|
# They should both wait for the block time
|
|
results = await asyncio.gather(call_limiter(), call_limiter())
|
|
|
|
total_time = time.time() - start_time
|
|
|
|
# Both should report having waited reactively
|
|
assert all(results) is True
|
|
assert total_time >= block_time - 0.1, (
|
|
f"Reactive block failed, took {total_time:.2f}s"
|
|
)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_set_blocked_zero_immediately_unblocks(self):
|
|
"""set_blocked(0) should not actually block."""
|
|
limiter = GlobalRateLimiter.get_instance(rate_limit=100, rate_window=60)
|
|
limiter.set_blocked(0)
|
|
|
|
# Should not be blocked since 0 seconds from now is already past
|
|
await asyncio.sleep(0.01)
|
|
assert limiter.is_blocked() is False
|
|
assert limiter.remaining_wait() == 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remaining_wait_when_not_blocked(self):
|
|
"""remaining_wait() should return 0 when not blocked."""
|
|
limiter = GlobalRateLimiter.get_instance(rate_limit=100, rate_window=60)
|
|
assert limiter.remaining_wait() == 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remaining_wait_decreases(self):
|
|
"""remaining_wait() should decrease over time."""
|
|
limiter = GlobalRateLimiter.get_instance(rate_limit=100, rate_window=60)
|
|
limiter.set_blocked(2.0)
|
|
|
|
wait1 = limiter.remaining_wait()
|
|
assert wait1 > 1.5
|
|
|
|
await asyncio.sleep(0.5)
|
|
wait2 = limiter.remaining_wait()
|
|
assert wait2 < wait1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_is_blocked_false_initially(self):
|
|
"""is_blocked() should be False for a fresh limiter."""
|
|
limiter = GlobalRateLimiter.get_instance(rate_limit=100, rate_window=60)
|
|
assert limiter.is_blocked() is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_high_rate_limit_no_throttling(self):
|
|
"""Very high rate limit should not cause throttling."""
|
|
GlobalRateLimiter.reset_instance()
|
|
limiter = GlobalRateLimiter.get_instance(rate_limit=10000, rate_window=60)
|
|
|
|
start = time.time()
|
|
for _ in range(20):
|
|
await limiter.wait_if_blocked()
|
|
duration = time.time() - start
|
|
|
|
# 20 requests with 10000 limit should be near-instant
|
|
assert duration < 1.0, f"High rate limit caused throttling: {duration:.2f}s"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_singleton_pattern(self):
|
|
"""get_instance should return the same object."""
|
|
limiter1 = GlobalRateLimiter.get_instance(rate_limit=10, rate_window=1)
|
|
limiter2 = GlobalRateLimiter.get_instance()
|
|
assert limiter1 is limiter2
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reset_instance(self):
|
|
"""reset_instance should allow creating a new instance."""
|
|
limiter1 = GlobalRateLimiter.get_instance(rate_limit=10, rate_window=1)
|
|
GlobalRateLimiter.reset_instance()
|
|
limiter2 = GlobalRateLimiter.get_instance(rate_limit=20, rate_window=2)
|
|
assert limiter1 is not limiter2
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_wait_if_blocked_returns_false_when_not_blocked(self):
|
|
"""wait_if_blocked should return False when not reactively blocked."""
|
|
limiter = GlobalRateLimiter.get_instance(rate_limit=100, rate_window=60)
|
|
result = await limiter.wait_if_blocked()
|
|
assert result is False
|