2026-03-18 17:56:48 +08:00
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
2026-03-14 11:59:07 +08:00
|
|
|
from src.clients import redis_client as redis_client_module
|
|
|
|
|
from src.clients.redis_client import RedisClientManager, RedisState
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _seed_open_circuit(manager: RedisClientManager) -> None:
|
|
|
|
|
manager._circuit_open_until = 9999999999.0
|
|
|
|
|
manager._consecutive_failures = 5
|
|
|
|
|
manager._last_error = "boom"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_reset_redis_circuit_breaker_resets_both_clients() -> None:
|
|
|
|
|
old_global = redis_client_module._redis_manager
|
|
|
|
|
old_usage = redis_client_module._usage_queue_redis_manager
|
|
|
|
|
try:
|
|
|
|
|
global_manager = RedisClientManager(client_name="global")
|
|
|
|
|
usage_manager = RedisClientManager(client_name="usage")
|
|
|
|
|
_seed_open_circuit(global_manager)
|
|
|
|
|
_seed_open_circuit(usage_manager)
|
|
|
|
|
|
|
|
|
|
redis_client_module._redis_manager = global_manager
|
|
|
|
|
redis_client_module._usage_queue_redis_manager = usage_manager
|
|
|
|
|
|
|
|
|
|
assert redis_client_module.reset_redis_circuit_breaker() is True
|
|
|
|
|
assert global_manager.get_state() == RedisState.NOT_INITIALIZED
|
|
|
|
|
assert usage_manager.get_state() == RedisState.NOT_INITIALIZED
|
|
|
|
|
finally:
|
|
|
|
|
redis_client_module._redis_manager = old_global
|
|
|
|
|
redis_client_module._usage_queue_redis_manager = old_usage
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_reset_redis_circuit_breaker_returns_false_when_uninitialized() -> None:
|
|
|
|
|
old_global = redis_client_module._redis_manager
|
|
|
|
|
old_usage = redis_client_module._usage_queue_redis_manager
|
|
|
|
|
try:
|
|
|
|
|
redis_client_module._redis_manager = None
|
|
|
|
|
redis_client_module._usage_queue_redis_manager = None
|
|
|
|
|
|
|
|
|
|
assert redis_client_module.reset_redis_circuit_breaker() is False
|
|
|
|
|
finally:
|
|
|
|
|
redis_client_module._redis_manager = old_global
|
|
|
|
|
redis_client_module._usage_queue_redis_manager = old_usage
|
2026-03-18 17:56:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_get_redis_client_isolated_per_event_loop(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
|
|
old_global = redis_client_module._redis_manager
|
|
|
|
|
try:
|
|
|
|
|
redis_client_module._redis_manager = RedisClientManager(client_name="global")
|
|
|
|
|
|
|
|
|
|
class DummyRedis:
|
|
|
|
|
def __init__(self, name: str) -> None:
|
|
|
|
|
self.name = name
|
|
|
|
|
self.closed = False
|
|
|
|
|
|
|
|
|
|
async def ping(self) -> bool:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
async def close(self) -> None:
|
|
|
|
|
self.closed = True
|
|
|
|
|
|
|
|
|
|
created_clients: list[DummyRedis] = []
|
|
|
|
|
|
|
|
|
|
async def fake_from_url(*args: object, **kwargs: object) -> DummyRedis:
|
|
|
|
|
client = DummyRedis(f"client-{len(created_clients)}")
|
|
|
|
|
created_clients.append(client)
|
|
|
|
|
return client
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(redis_client_module.aioredis, "from_url", fake_from_url)
|
|
|
|
|
|
|
|
|
|
main_client = await redis_client_module.get_redis_client()
|
|
|
|
|
|
|
|
|
|
def _get_client_from_thread() -> object:
|
|
|
|
|
return asyncio.run(redis_client_module.get_redis_client())
|
|
|
|
|
|
|
|
|
|
thread_client = await asyncio.to_thread(_get_client_from_thread)
|
|
|
|
|
|
|
|
|
|
assert main_client is created_clients[0]
|
|
|
|
|
assert thread_client is created_clients[1]
|
|
|
|
|
assert thread_client is not main_client
|
|
|
|
|
assert redis_client_module.get_redis_client_sync() is main_client
|
|
|
|
|
|
|
|
|
|
await redis_client_module.close_redis_client()
|
|
|
|
|
|
|
|
|
|
assert created_clients[0].closed is True
|
|
|
|
|
assert created_clients[1].closed is True
|
|
|
|
|
finally:
|
|
|
|
|
redis_client_module._redis_manager = old_global
|