strat-gameplay-webapp/backend/alembic/env.py
Cal Corum 9d0d29ef18 CLAUDE: Add Alembic migrations and database session injection
Database Infrastructure:
- Added Alembic migration system (alembic.ini, env.py)
- Migration 001: Initial schema
- Migration 004: Stat materialized views (enhanced)
- Migration 005: Composite indexes for performance
- operations.py: Session injection support for test isolation
- session.py: Enhanced session management

Application Updates:
- main.py: Integration with new database infrastructure
- health.py: Enhanced health checks with pool monitoring

Integration Tests:
- conftest.py: Session injection pattern for reliable tests
- test_operations.py: Database operations tests
- test_migrations.py: Migration verification tests

Session injection pattern enables:
- Production: Auto-commit per operation
- Testing: Shared session with automatic rollback
- Transactions: Multiple ops, single commit

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 12:09:09 -06:00

102 lines
2.7 KiB
Python

"""
Alembic migration environment configuration.
Uses synchronous psycopg2 driver for migrations while the application
uses asyncpg at runtime. This is the standard pattern for Alembic with
async SQLAlchemy applications.
"""
from logging.config import fileConfig
from sqlalchemy import create_engine, pool
from sqlalchemy.engine import Connection
from alembic import context
# Import Base and all models to ensure metadata is complete
from app.database.session import Base
from app.models.db_models import ( # noqa: F401
Game,
GameCardsetLink,
GameSession,
Lineup,
Play,
Roll,
RosterLink,
)
# Load settings for database URL
from app.config import get_settings
# Alembic Config object - provides access to the .ini file values
config = context.config
# Get database URL from settings and convert for sync driver (psycopg2)
settings = get_settings()
# Replace asyncpg with psycopg2 for synchronous migrations
sync_database_url = settings.database_url.replace("+asyncpg", "+psycopg2")
config.set_main_option("sqlalchemy.url", sync_database_url)
# Interpret the config file for Python logging
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# Target metadata for 'autogenerate' support
target_metadata = Base.metadata
def run_migrations_offline() -> None:
"""
Run migrations in 'offline' mode.
This configures the context with just a URL and not an Engine,
generating SQL scripts without connecting to the database.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
compare_type=True, # Detect column type changes
compare_server_default=True, # Detect server_default changes
)
with context.begin_transaction():
context.run_migrations()
def do_run_migrations(connection: Connection) -> None:
"""Execute migrations using the provided connection."""
context.configure(
connection=connection,
target_metadata=target_metadata,
compare_type=True,
compare_server_default=True,
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""
Run migrations in 'online' mode.
Creates a synchronous engine and associates a connection with the context.
"""
connectable = create_engine(
config.get_main_option("sqlalchemy.url"),
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
do_run_migrations(connection)
connectable.dispose()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()