"""Tests for central Redis pool consolidation (B-RED-TEST). Verifies that get_redis() returns a singleton, get_cache() delegates to it, the connection works, parallel calls are safe, and reset works. """ from __future__ import annotations import asyncio import pytest import redis.asyncio as aioredis from app.core import auth from app.core.auth import close_redis, get_redis from app.core.cache import get_cache @pytest.fixture(autouse=True) def _reset_redis_singleton(): """Ensure a clean Redis singleton before and after each test.""" # Reset before test auth._redis_client = None yield # Reset after test auth._redis_client = None @pytest.mark.asyncio async def test_get_redis_returns_same_instance(): """get_redis() must return the same instance on repeated calls.""" r1 = get_redis() r2 = get_redis() assert r1 is r2, "get_redis() returned different instances" @pytest.mark.asyncio async def test_get_cache_returns_same_instance_as_get_redis(): """get_cache() must delegate to get_redis() and return the same instance.""" r = get_redis() c = get_cache() assert c is r, "get_cache() did not return the same instance as get_redis()" @pytest.mark.asyncio async def test_get_redis_connection_works(): """The Redis connection from get_redis() must support SET/GET.""" r = get_redis() await r.set("test:pool:key", "hello") val = await r.get("test:pool:key") assert val == "hello" await r.delete("test:pool:key") @pytest.mark.asyncio async def test_parallel_get_redis_returns_same_instance(): """Multiple parallel get_redis() calls must return the same instance.""" results = await asyncio.gather(*[asyncio.to_thread(get_redis) for _ in range(10)]) first = results[0] assert all(r is first for r in results), "Parallel get_redis() returned different instances" @pytest.mark.asyncio async def test_close_redis_resets_instance(): """close_redis() must reset the singleton so the next get_redis() creates a new one.""" r1 = get_redis() await close_redis() r2 = get_redis() assert r1 is not r2, "close_redis() did not reset the singleton" @pytest.mark.asyncio async def test_no_direct_aioredis_from_url_in_cache(): """cache.py must not use aioredis.from_url() directly (uses get_redis() instead).""" import inspect from app.core import cache source = inspect.getsource(cache) assert "from_url" not in source, "cache.py still contains aioredis.from_url()" @pytest.mark.asyncio async def test_no_direct_aioredis_from_url_in_monitoring(): """monitoring.py must not use aioredis.from_url() directly.""" import inspect from app.core import monitoring source = inspect.getsource(monitoring) assert "from_url" not in source, "monitoring.py still contains aioredis.from_url()" @pytest.mark.asyncio async def test_no_direct_aioredis_from_url_in_worker(): """worker.py must not use aioredis.from_url() directly.""" import inspect from app.core import worker source = inspect.getsource(worker) assert "from_url" not in source, "worker.py still contains aioredis.from_url()"