name: Test Migrations on: push: branches: - main - master - dev paths: - 'src/langbot/pkg/persistence/**' - 'src/langbot/pkg/entity/persistence/**' pull_request: types: [opened, synchronize, reopened, ready_for_review] paths: - 'src/langbot/pkg/persistence/**' - 'src/langbot/pkg/entity/persistence/**' jobs: test-migrations-sqlite: name: Migrations (SQLite) runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install uv uses: astral-sh/setup-uv@v4 - name: Install dependencies run: uv sync --dev - name: Test Alembic upgrade (SQLite) run: | uv run python -c " import asyncio from sqlalchemy.ext.asyncio import create_async_engine from langbot.pkg.entity.persistence.base import Base from langbot.pkg.persistence.alembic_runner import run_alembic_upgrade, run_alembic_stamp, get_alembic_current async def main(): engine = create_async_engine('sqlite+aiosqlite:///test_migrations.db') # Create all tables (simulates existing DB) async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) # Stamp baseline await run_alembic_stamp(engine, '0001_baseline') rev = await get_alembic_current(engine) assert rev == '0001_baseline', f'Expected 0001_baseline, got {rev}' print(f'Stamped: {rev}') # Upgrade to head await run_alembic_upgrade(engine, 'head') rev = await get_alembic_current(engine) print(f'After upgrade: {rev}') assert rev is not None, 'Expected a revision after upgrade' # Verify idempotent await run_alembic_upgrade(engine, 'head') rev2 = await get_alembic_current(engine) assert rev2 == rev, f'Expected {rev}, got {rev2}' print(f'Idempotent check passed: {rev2}') # Fresh DB: upgrade from scratch engine2 = create_async_engine('sqlite+aiosqlite:///test_migrations_fresh.db') async with engine2.begin() as conn: await conn.run_sync(Base.metadata.create_all) await run_alembic_upgrade(engine2, 'head') rev3 = await get_alembic_current(engine2) print(f'Fresh DB upgrade: {rev3}') assert rev3 is not None print('All SQLite migration tests passed!') asyncio.run(main()) " test-migrations-postgres: name: Migrations (PostgreSQL) runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_USER: langbot POSTGRES_PASSWORD: langbot POSTGRES_DB: langbot_test ports: - 5432:5432 options: >- --health-cmd="pg_isready -U langbot" --health-interval=5s --health-timeout=5s --health-retries=5 steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install uv uses: astral-sh/setup-uv@v4 - name: Install dependencies run: uv sync --dev - name: Test Alembic upgrade (PostgreSQL) run: | uv run python -c " import asyncio from sqlalchemy.ext.asyncio import create_async_engine from langbot.pkg.entity.persistence.base import Base from langbot.pkg.persistence.alembic_runner import run_alembic_upgrade, run_alembic_stamp, get_alembic_current DB_URL = 'postgresql+asyncpg://langbot:langbot@localhost:5432/langbot_test' async def main(): engine = create_async_engine(DB_URL) # Create all tables async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) # Stamp baseline await run_alembic_stamp(engine, '0001_baseline') rev = await get_alembic_current(engine) assert rev == '0001_baseline', f'Expected 0001_baseline, got {rev}' print(f'Stamped: {rev}') # Upgrade to head await run_alembic_upgrade(engine, 'head') rev = await get_alembic_current(engine) print(f'After upgrade: {rev}') assert rev is not None # Verify idempotent await run_alembic_upgrade(engine, 'head') rev2 = await get_alembic_current(engine) assert rev2 == rev, f'Expected {rev}, got {rev2}' print(f'Idempotent check passed: {rev2}') # Fresh DB: drop all and upgrade from scratch engine2 = create_async_engine(DB_URL.replace('langbot_test', 'langbot_fresh')) # Create fresh database from sqlalchemy import text async with engine.connect() as conn: await conn.execute(text('COMMIT')) await conn.execute(text('CREATE DATABASE langbot_fresh')) async with engine2.begin() as conn: await conn.run_sync(Base.metadata.create_all) await run_alembic_upgrade(engine2, 'head') rev3 = await get_alembic_current(engine2) print(f'Fresh DB upgrade: {rev3}') assert rev3 is not None print('All PostgreSQL migration tests passed!') asyncio.run(main()) "