103 lines
3.1 KiB
Python
103 lines
3.1 KiB
Python
|
|
"""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()"
|