|
|
|
|
|
""" |
|
|
Testes para o RateLimiter (rate_limiter.py). |
|
|
""" |
|
|
|
|
|
import pytest |
|
|
import time |
|
|
from unittest.mock import patch |
|
|
|
|
|
from superezio_enterprise.rate_limiter import RateLimiter |
|
|
from superezio_enterprise.config import EnterpriseConfig |
|
|
|
|
|
@pytest.fixture |
|
|
def mock_config(): |
|
|
"""Fornece uma configuração mockada para isolar os testes do limitador.""" |
|
|
|
|
|
return EnterpriseConfig(rate_limit_per_minute=5) |
|
|
|
|
|
@pytest.fixture |
|
|
def limiter_instance(mock_config): |
|
|
"""Fornece uma instância limpa do RateLimiter para cada teste.""" |
|
|
|
|
|
with patch('superezio_enterprise.rate_limiter.CONFIG', mock_config): |
|
|
instance = RateLimiter() |
|
|
|
|
|
instance.rate = float(mock_config.rate_limit_per_minute) |
|
|
instance.per_seconds = 60.0 |
|
|
instance._tokens = instance.rate |
|
|
instance._last_update = time.monotonic() |
|
|
instance.tokens_per_second = instance.rate / instance.per_seconds |
|
|
yield instance |
|
|
|
|
|
def test_rate_limiter_singleton(limiter_instance): |
|
|
"""Testa se o RateLimiter segue o padrão singleton.""" |
|
|
instance1 = limiter_instance |
|
|
instance2 = RateLimiter() |
|
|
assert instance1 is instance2 |
|
|
|
|
|
def test_initial_tokens(limiter_instance): |
|
|
"""Testa se o número inicial de tokens está correto.""" |
|
|
assert limiter_instance._tokens == limiter_instance.rate |
|
|
|
|
|
def test_acquire_success(limiter_instance): |
|
|
"""Testa se é possível adquirir tokens quando há suficientes.""" |
|
|
assert limiter_instance.acquire(1) is True |
|
|
assert limiter_instance.acquire(2) is True |
|
|
|
|
|
assert limiter_instance._tokens == pytest.approx(2) |
|
|
|
|
|
def test_acquire_failure_due_to_exhaustion(limiter_instance): |
|
|
"""Testa se a aquisição falha quando não há tokens suficientes.""" |
|
|
|
|
|
assert limiter_instance.acquire(5) is True |
|
|
assert limiter_instance._tokens == pytest.approx(0) |
|
|
|
|
|
|
|
|
assert limiter_instance.acquire(1) is False |
|
|
|
|
|
def test_token_refill(limiter_instance): |
|
|
"""Testa se os tokens são reabastecidos ao longo do tempo.""" |
|
|
|
|
|
assert limiter_instance.acquire(5) is True |
|
|
assert limiter_instance.acquire(1) is False |
|
|
|
|
|
|
|
|
|
|
|
time.sleep(12) |
|
|
|
|
|
|
|
|
assert limiter_instance.acquire(1) is True |
|
|
assert limiter_instance._tokens == pytest.approx(0, abs=0.1) |
|
|
|
|
|
def test_acquire_burst(limiter_instance): |
|
|
"""Testa se o balde de tokens permite rajadas (bursts).""" |
|
|
|
|
|
assert limiter_instance.acquire(5) is True |
|
|
assert limiter_instance._tokens == pytest.approx(0) |
|
|
|
|
|
def test_refill_does_not_exceed_capacity(limiter_instance): |
|
|
"""Testa se o reabastecimento não ultrapassa a capacidade máxima.""" |
|
|
|
|
|
limiter_instance.acquire(2) |
|
|
assert limiter_instance._tokens == pytest.approx(3) |
|
|
|
|
|
|
|
|
time.sleep(36) |
|
|
|
|
|
|
|
|
limiter_instance.acquire(0) |
|
|
assert limiter_instance._tokens <= limiter_instance.rate |
|
|
assert limiter_instance._tokens == pytest.approx(5) |
|
|
|