011 fix redis
This commit is contained in:
+69
-2
@@ -1,8 +1,16 @@
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
import sys
|
||||
import types
|
||||
|
||||
import app.config as app_config
|
||||
import pytest
|
||||
from app.config import Settings
|
||||
from app.repositories.cache.redis_cache import PriceCache
|
||||
from app.repositories.cache.redis_cache import (
|
||||
PriceCache,
|
||||
PriceCacheRepositoryError,
|
||||
_default_redis_client_factory,
|
||||
)
|
||||
|
||||
|
||||
class InMemoryRedisClient:
|
||||
@@ -39,6 +47,28 @@ class InMemoryRedisClient:
|
||||
return self._storage[key][0]
|
||||
|
||||
|
||||
class FailingGetRedisClient:
|
||||
async def get(self, key: str) -> bytes | None: # noqa: ARG002
|
||||
raise RuntimeError("redis get failed")
|
||||
|
||||
async def set(self, key: str, value: str, ex: int | None = None) -> bool: # noqa: ARG002
|
||||
return True
|
||||
|
||||
async def delete(self, key: str) -> int: # noqa: ARG002
|
||||
return 1
|
||||
|
||||
|
||||
class FailingSetRedisClient:
|
||||
async def get(self, key: str) -> bytes | None: # noqa: ARG002
|
||||
return None
|
||||
|
||||
async def set(self, key: str, value: str, ex: int | None = None) -> bool: # noqa: ARG002
|
||||
raise RuntimeError("redis set failed")
|
||||
|
||||
async def delete(self, key: str) -> int: # noqa: ARG002
|
||||
return 1
|
||||
|
||||
|
||||
def test_cache_miss_returns_none() -> None:
|
||||
cache = PriceCache(InMemoryRedisClient(), ttl_seconds=120)
|
||||
|
||||
@@ -130,7 +160,7 @@ repository:
|
||||
""".strip(),
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setenv("G2S_CONFIG_FILE", str(config_file))
|
||||
monkeypatch.setattr(app_config, "_resolve_runtime_config_file", lambda: str(config_file))
|
||||
|
||||
settings = Settings()
|
||||
redis_client = InMemoryRedisClient()
|
||||
@@ -153,3 +183,40 @@ repository:
|
||||
assert seen_dsns == ["redis://redis.internal:6380/5"]
|
||||
assert redis_client.last_set == ("config-key", '{"ok":true}', 123)
|
||||
assert result == {"ok": True}
|
||||
|
||||
|
||||
def test_default_client_factory_uses_aioredis_from_url(monkeypatch) -> None:
|
||||
seen: dict[str, object] = {}
|
||||
expected_client = object()
|
||||
|
||||
def fake_from_url(redis_dsn: str, *, encoding: str, decode_responses: bool) -> object:
|
||||
seen["redis_dsn"] = redis_dsn
|
||||
seen["encoding"] = encoding
|
||||
seen["decode_responses"] = decode_responses
|
||||
return expected_client
|
||||
|
||||
fake_aioredis_module = types.SimpleNamespace(from_url=fake_from_url)
|
||||
monkeypatch.setitem(sys.modules, "aioredis", fake_aioredis_module)
|
||||
|
||||
actual_client = _default_redis_client_factory("redis://cache.internal:6381/9")
|
||||
|
||||
assert actual_client is expected_client
|
||||
assert seen == {
|
||||
"redis_dsn": "redis://cache.internal:6381/9",
|
||||
"encoding": "utf-8",
|
||||
"decode_responses": False,
|
||||
}
|
||||
|
||||
|
||||
def test_get_raises_deterministic_repository_error_on_redis_failure() -> None:
|
||||
cache = PriceCache(FailingGetRedisClient(), ttl_seconds=10)
|
||||
|
||||
with pytest.raises(PriceCacheRepositoryError, match="Redis cache read failed."):
|
||||
asyncio.run(cache.get("key"))
|
||||
|
||||
|
||||
def test_set_raises_deterministic_repository_error_on_redis_failure() -> None:
|
||||
cache = PriceCache(FailingSetRedisClient(), ttl_seconds=10)
|
||||
|
||||
with pytest.raises(PriceCacheRepositoryError, match="Redis cache write failed."):
|
||||
asyncio.run(cache.set("key", {"v": 1}))
|
||||
|
||||
Reference in New Issue
Block a user