mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
ci: add migration test workflow for SQLite and PostgreSQL
Tests alembic upgrade on both databases: - Stamp baseline on existing schema - Upgrade to head - Idempotent re-upgrade - Fresh DB upgrade from scratch
This commit is contained in:
171
.github/workflows/test-migrations.yml
vendored
Normal file
171
.github/workflows/test-migrations.yml
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
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())
|
||||
"
|
||||
Reference in New Issue
Block a user