""" Shared test fixtures for Weibo-HotSign backend tests. Uses SQLite in-memory for database tests and a simple dict-based fake Redis for refresh-token tests, so no external services are needed. """ import asyncio import sys import os from typing import AsyncGenerator import pytest import pytest_asyncio from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker # Ensure backend/ is on sys.path so `shared` and `app` imports work sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) # --------------- override shared settings BEFORE any app import --------------- os.environ["DATABASE_URL"] = "sqlite+aiosqlite://" os.environ["REDIS_URL"] = "redis://localhost:6379/0" os.environ["JWT_SECRET_KEY"] = "test-secret-key" os.environ["COOKIE_ENCRYPTION_KEY"] = "test-cookie-key" # Create the test engine BEFORE importing shared.models so we can swap it in TEST_ENGINE = create_async_engine("sqlite+aiosqlite://", echo=False) TestSessionLocal = sessionmaker(TEST_ENGINE, class_=AsyncSession, expire_on_commit=False) # Now patch shared.models.base module-level objects before they get used import shared.models.base as _base_mod # noqa: E402 _base_mod.engine = TEST_ENGINE _base_mod.AsyncSessionLocal = TestSessionLocal from shared.models.base import Base # noqa: E402 from shared.models import User # noqa: E402 @pytest.fixture(scope="session") def event_loop(): """Create a single event loop for the whole test session.""" loop = asyncio.new_event_loop() yield loop loop.close() @pytest_asyncio.fixture(autouse=True) async def setup_db(): """Create all tables before each test, drop after.""" async with TEST_ENGINE.begin() as conn: await conn.run_sync(Base.metadata.create_all) yield async with TEST_ENGINE.begin() as conn: await conn.run_sync(Base.metadata.drop_all) @pytest_asyncio.fixture async def db_session() -> AsyncGenerator[AsyncSession, None]: """Yield a fresh async DB session.""" async with TestSessionLocal() as session: yield session # --------------- Fake Redis for refresh-token tests --------------- class FakeRedis: """Minimal async Redis stand-in backed by a plain dict.""" def __init__(self): self._store: dict[str, str] = {} async def setex(self, key: str, ttl: int, value: str): self._store[key] = value async def get(self, key: str): return self._store.get(key) async def delete(self, key: str): self._store.pop(key, None) @pytest.fixture def fake_redis(): return FakeRedis()