Files
nuzlocke-tracker/backend/tests/conftest.py
Julian Tabel c17e776408
Some checks failed
CI / backend-tests (pull_request) Successful in 26s
CI / frontend-tests (pull_request) Failing after 29s
fix(tests): use admin_client for admin-protected endpoints
After adding require_admin to admin endpoints, tests using
unauthenticated client or auth_client got 401/403. Also fix mock user
ID to be a valid UUID (was "test-user-123", now a proper UUID4).

- Add admin_override and admin_client fixtures to conftest
- Update test_pokemon.py, test_games.py, test_genlocke_boss.py to use
  admin_client for write operations on admin endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 11:39:10 +01:00

135 lines
3.8 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, require_admin
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.drop_all)
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="00000000-0000-4000-a000-000000000001",
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 admin_override(mock_auth_user):
"""Override require_admin and get_current_user to return a mock user."""
def _override():
return mock_auth_user
app.dependency_overrides[require_admin] = _override
app.dependency_overrides[get_current_user] = _override
yield
app.dependency_overrides.pop(require_admin, None)
app.dependency_overrides.pop(get_current_user, None)
@pytest.fixture
async def admin_client(db_session, admin_override):
"""Async HTTP client with mocked admin 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": "00000000-0000-4000-a000-000000000001",
"email": "test@example.com",
"role": "authenticated",
"aud": "authenticated",
"exp": int(time.time()) + 3600,
}
return jwt.encode(payload, TEST_JWT_SECRET, algorithm="HS256")