Add user authentication with login/signup/protected routes, boss pokemon detail fields and result team tracking, moves and abilities selector components and API, run ownership and visibility controls, and various UI improvements across encounters, run list, and journal pages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
107 lines
3.0 KiB
Python
107 lines
3.0 KiB
Python
import os
|
|
import time
|
|
|
|
import jwt
|
|
import pytest
|
|
from httpx import ASGITransport, AsyncClient
|
|
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
|
|
|
import app.models # noqa: F401 — ensures all models register with Base.metadata
|
|
from app.core.auth import AuthUser, get_current_user
|
|
from app.core.database import Base, get_session
|
|
from app.main import app
|
|
|
|
TEST_JWT_SECRET = "test-jwt-secret-for-testing-only"
|
|
|
|
TEST_DATABASE_URL = os.getenv(
|
|
"TEST_DATABASE_URL",
|
|
"postgresql+asyncpg://postgres:postgres@localhost:5433/nuzlocke_test",
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
async def engine():
|
|
"""Create the test engine and schema once for the entire session."""
|
|
eng = create_async_engine(TEST_DATABASE_URL, echo=False)
|
|
async with eng.begin() as conn:
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
yield eng
|
|
async with eng.begin() as conn:
|
|
await conn.run_sync(Base.metadata.drop_all)
|
|
await eng.dispose()
|
|
|
|
|
|
@pytest.fixture
|
|
async def db_session(engine):
|
|
"""
|
|
Provide a database session for a single test.
|
|
|
|
Overrides the FastAPI get_session dependency so endpoint handlers use the
|
|
same session. Truncates all tables after the test to isolate state.
|
|
"""
|
|
session_factory = async_sessionmaker(engine, expire_on_commit=False)
|
|
session = session_factory()
|
|
|
|
async def override_get_session():
|
|
yield session
|
|
|
|
app.dependency_overrides[get_session] = override_get_session
|
|
|
|
yield session
|
|
|
|
await session.close()
|
|
app.dependency_overrides.clear()
|
|
|
|
async with engine.begin() as conn:
|
|
for table in reversed(Base.metadata.sorted_tables):
|
|
await conn.execute(table.delete())
|
|
|
|
|
|
@pytest.fixture
|
|
async def client(db_session):
|
|
"""Async HTTP client wired to the FastAPI app with the test database session."""
|
|
async with AsyncClient(
|
|
transport=ASGITransport(app=app), base_url="http://test"
|
|
) as ac:
|
|
yield ac
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_auth_user():
|
|
"""Return a mock authenticated user for tests."""
|
|
return AuthUser(id="test-user-123", email="test@example.com", role="authenticated")
|
|
|
|
|
|
@pytest.fixture
|
|
def auth_override(mock_auth_user):
|
|
"""Override get_current_user to return a mock user."""
|
|
|
|
def _override():
|
|
return mock_auth_user
|
|
|
|
app.dependency_overrides[get_current_user] = _override
|
|
yield
|
|
app.dependency_overrides.pop(get_current_user, None)
|
|
|
|
|
|
@pytest.fixture
|
|
async def auth_client(db_session, auth_override):
|
|
"""Async HTTP client with mocked authentication."""
|
|
async with AsyncClient(
|
|
transport=ASGITransport(app=app), base_url="http://test"
|
|
) as ac:
|
|
yield ac
|
|
|
|
|
|
@pytest.fixture
|
|
def valid_token():
|
|
"""Generate a valid JWT token for testing."""
|
|
payload = {
|
|
"sub": "test-user-123",
|
|
"email": "test@example.com",
|
|
"role": "authenticated",
|
|
"aud": "authenticated",
|
|
"exp": int(time.time()) + 3600,
|
|
}
|
|
return jwt.encode(payload, TEST_JWT_SECRET, algorithm="HS256")
|