Files

107 lines
3.2 KiB
Python

import asyncio
import os
import sys
from collections.abc import AsyncIterator, Iterator
from pathlib import Path
import httpx2
import pytest
import pytest_asyncio
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from testcontainers.community.postgres import PostgresContainer
from testcontainers.core.container import DockerContainer
from testcontainers.core.wait_strategies import LogMessageWaitStrategy
from app.api import router
from app.auth import get_settings
from app.config import Settings
from app.db import Base, get_session
PROJECT_ROOT = Path(__file__).parents[2]
async def migrate_to_head(database_url: str) -> None:
process = await asyncio.create_subprocess_exec(
sys.executable,
"-m",
"alembic",
"upgrade",
"head",
cwd=PROJECT_ROOT,
env={**os.environ, "DATABASE_URL": database_url},
)
assert await process.wait() == 0
@pytest.fixture(scope="session")
def postgres_url() -> Iterator[str]:
with PostgresContainer("postgres:16-alpine", driver=None) as postgres:
url = postgres.get_connection_url(driver=None)
yield url.replace("postgresql://", "postgresql+asyncpg://", 1)
@pytest.fixture(scope="session")
def rabbitmq_url() -> Iterator[str]:
rabbitmq = (
DockerContainer("rabbitmq:3.13-alpine")
.with_exposed_ports(5672)
.with_env("RABBITMQ_DEFAULT_USER", "payments")
.with_env("RABBITMQ_DEFAULT_PASS", "payments")
.waiting_for(LogMessageWaitStrategy("Server startup complete").with_startup_timeout(60))
)
with rabbitmq:
host = rabbitmq.get_container_host_ip()
port = rabbitmq.get_exposed_port(5672)
yield f"amqp://payments:payments@{host}:{port}/"
@pytest_asyncio.fixture
async def database(
postgres_url: str,
) -> AsyncIterator[async_sessionmaker[AsyncSession]]:
engine = create_async_engine(postgres_url)
async with engine.begin() as connection:
await connection.run_sync(Base.metadata.drop_all)
await connection.exec_driver_sql("DROP TABLE IF EXISTS alembic_version")
await engine.dispose()
await migrate_to_head(postgres_url)
engine = create_async_engine(postgres_url)
factory = async_sessionmaker(engine, expire_on_commit=False)
try:
yield factory
finally:
await engine.dispose()
@pytest_asyncio.fixture
async def db_session(
database: async_sessionmaker[AsyncSession],
) -> AsyncIterator[AsyncSession]:
async with database() as session:
yield session
@pytest_asyncio.fixture
async def api_client(
database: async_sessionmaker[AsyncSession],
) -> AsyncIterator[httpx2.AsyncClient]:
app = FastAPI()
app.include_router(router)
async def override_session() -> AsyncIterator[AsyncSession]:
async with database() as session:
yield session
app.dependency_overrides[get_session] = override_session
app.dependency_overrides[get_settings] = lambda: Settings(api_key="test-key")
transport = httpx2.ASGITransport(app=app)
async with httpx2.AsyncClient(
transport=transport,
base_url="http://testserver",
) as client:
yield client