nanochat/services/auth/src/tests/conftest.py
Manmohan Sharma 4b4aca642a
feat(auth): OAuth2 + JWT auth service with Alembic migrations (#5 #7)
- Alembic async migrations: users, conversations, messages, is_favorited
- FastAPI auth service: Google + GitHub OAuth, RS256 JWT, refresh cookie
- /auth/me, /auth/refresh, /auth/validate (service-to-service)
- rate limiting 10/min on OAuth routes, CORS locked to FRONTEND_URL

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 11:47:00 -07:00

85 lines
2.8 KiB
Python

"""Shared pytest fixtures: RSA keys, in-memory DB, FastAPI test client."""
from __future__ import annotations
import os
import uuid
from collections.abc import AsyncIterator
import pytest
import pytest_asyncio
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
def _make_rsa_pair() -> tuple[str, str]:
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
private = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
).decode()
public = key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
).decode()
return private, public
@pytest.fixture(scope="session", autouse=True)
def _test_environment():
private, public = _make_rsa_pair()
os.environ["JWT_PRIVATE_KEY"] = private
os.environ["JWT_PUBLIC_KEY"] = public
os.environ["INTERNAL_API_KEY"] = "test-internal-key"
os.environ["FRONTEND_URL"] = "http://localhost:3000"
os.environ["AUTH_BASE_URL"] = "http://localhost:8001"
os.environ["SESSION_SECRET"] = "test-session-secret"
os.environ["DATABASE_URL"] = "sqlite+aiosqlite:///:memory:"
os.environ["GOOGLE_CLIENT_ID"] = "g-id"
os.environ["GOOGLE_CLIENT_SECRET"] = "g-secret"
os.environ["GITHUB_CLIENT_ID"] = "gh-id"
os.environ["GITHUB_CLIENT_SECRET"] = "gh-secret"
from src.config import get_settings
get_settings.cache_clear()
yield
@pytest_asyncio.fixture
async def session_factory() -> AsyncIterator[async_sessionmaker[AsyncSession]]:
from src.database import Base, override_session_factory
from src.models.user import User # noqa: F401 — register on Base
engine = create_async_engine(
f"sqlite+aiosqlite:///file:test_{uuid.uuid4().hex}?mode=memory&cache=shared&uri=true",
connect_args={"uri": True},
)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
factory = async_sessionmaker(engine, expire_on_commit=False)
override_session_factory(factory)
try:
yield factory
finally:
await engine.dispose()
@pytest_asyncio.fixture
async def db_session(session_factory) -> AsyncIterator[AsyncSession]:
async with session_factory() as session:
yield session
@pytest_asyncio.fixture
async def client(session_factory) -> AsyncIterator[AsyncClient]:
from src.main import create_app
app = create_app()
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://testserver") as ac:
yield ac