123
This commit is contained in:
86
backend/tests/conftest.py
Normal file
86
backend/tests/conftest.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user