""" Pytest fixtures for lewooogo-data tests ======================================== 提供測試所需的 Mock 與 Fixtures """ import pytest from unittest.mock import AsyncMock, MagicMock, patch from pydantic import BaseModel # ============================================================================= # 測試用 Pydantic Model # ============================================================================= class SampleModel(BaseModel): """測試用的 Pydantic Model""" id: str name: str value: int = 0 # ============================================================================= # Redis Mock Fixtures # ============================================================================= @pytest.fixture def mock_redis(): """ Mock Redis client 模擬 redis.asyncio 的行為 """ mock = AsyncMock() # 內部儲存 storage = {} ttls = {} async def mock_get(key): return storage.get(key) async def mock_setex(key, ttl, value): storage[key] = value ttls[key] = ttl return True async def mock_delete(key): if key in storage: del storage[key] if key in ttls: del ttls[key] return 1 return 0 async def mock_exists(key): return 1 if key in storage else 0 async def mock_ttl(key): if key not in storage: return -2 return ttls.get(key, -1) async def mock_expire(key, ttl): if key in storage: ttls[key] = ttl return True return False async def mock_ping(): return True mock.get = mock_get mock.setex = mock_setex mock.delete = mock_delete mock.exists = mock_exists mock.ttl = mock_ttl mock.expire = mock_expire mock.ping = mock_ping mock.close = AsyncMock() # 暴露 storage 供測試驗證 mock._storage = storage mock._ttls = ttls return mock @pytest.fixture def patch_redis_pool(mock_redis): """ Patch Redis 連線池 將全域 _redis_pool 替換為 mock """ with patch( "lewooogo_data.providers.redis_memory._redis_pool", mock_redis ): with patch( "lewooogo_data.providers.redis_memory.get_redis_pool", return_value=mock_redis ): yield mock_redis # ============================================================================= # PostgreSQL Mock Fixtures # ============================================================================= @pytest.fixture def mock_session(): """ Mock SQLAlchemy AsyncSession """ session = AsyncMock() # 內部儲存 storage = {} async def mock_get(model_class, key): return storage.get(key) session.get = mock_get session.add = MagicMock() session.delete = AsyncMock() session.commit = AsyncMock() # 暴露 storage 供測試驗證 session._storage = storage return session @pytest.fixture def mock_session_factory(mock_session): """ Mock Session Factory 使用 context manager 模式 """ class MockSessionFactory: def __init__(self, session): self._session = session def __call__(self): return self async def __aenter__(self): return self._session async def __aexit__(self, *args): pass return MockSessionFactory(mock_session) @pytest.fixture def patch_pg_session(mock_session_factory): """ Patch PostgreSQL session factory """ with patch( "lewooogo_data.providers.pg_memory._session_factory", mock_session_factory ): with patch( "lewooogo_data.providers.pg_memory.get_session_factory", return_value=mock_session_factory ): yield mock_session_factory