import logging import os import fnmatch from typing import Any, List, Tuple import redis from app.core.config import settings logger = logging.getLogger(__name__) class InMemoryRedis: """Minimal in-memory Redis replacement for tests.""" def __init__(self): self.store = {} def get(self, key): return self.store.get(key) def set(self, key, value): self.store[key] = value return True def setex(self, key, _seconds, value): self.store[key] = value return True def delete(self, key): if key in self.store: del self.store[key] return 1 return 0 def scan_iter(self, match=None): if match is None: yield from self.store.keys() return for key in list(self.store.keys()): if fnmatch.fnmatch(key, match): yield key def publish(self, _channel, _message): return 1 def ping(self): return True if os.getenv("TESTING") == "true": redis_client = InMemoryRedis() else: redis_client = redis.Redis( host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DB, decode_responses=True, ) def get_redis(): """Dependency for getting Redis client.""" return redis_client def get_redis_sync(): """Get Redis client synchronously (non-dependency use).""" return redis_client class RedisManager: """Lightweight Redis helper with publish fallback for reliability tests.""" def __init__(self, client=None): self._client = client self._message_queue: List[Tuple[str, Any]] = [] def get_client(self): return self._client or redis_client def _publish_direct(self, channel: str, message: Any): client = self.get_client() return client.publish(channel, message) def queue_message(self, channel: str, message: Any) -> None: self._message_queue.append((channel, message)) def publish_with_fallback(self, channel: str, message: Any): try: return self._publish_direct(channel, message) except Exception as exc: self.queue_message(channel, message) logger.warning("Redis publish failed, queued message: %s", exc) return None