87 lines
2.5 KiB
Python
87 lines
2.5 KiB
Python
"""
|
|
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()
|