Skip to main content

Catch database migration rollback failures before they reach production

Project description

pytest-mrt

PyPI CI Coverage Python MIT License

A pytest plugin that catches database migration rollback failures before they reach production.


alembic downgrade -1 ran clean. No errors. Your monitoring went green.

But the users' phone numbers are gone. The column came back. The data didn't.


What it does

Most tools verify that migrations run without errors.
pytest-mrt verifies that your data survives a rollback.

It seeds real rows before each migration, rolls back, and checks nothing was lost. It also statically scans migration files for 27 known dangerous patterns — across both Alembic and Django migrations.

Install

pip install pytest-mrt

Setup (2 minutes)

1. Create conftest.py in your project root:

# conftest.py
import os
from pytest_mrt import MRTConfig


def pytest_configure(config):
    config._mrt_config = MRTConfig(
        alembic_ini="alembic.ini",                               # path to your alembic.ini
        db_url=os.environ.get("TEST_DATABASE_URL", "sqlite:///test.db"),  # test database
    )

2. Write a test:

# tests/test_migrations.py


def test_migrations_are_safe(mrt):
    mrt.assert_all_reversible()

3. Run:

pytest tests/test_migrations.py -s

mrt is a pytest fixture — just add it as a parameter and it works. No import needed in test files.

Static analysis (no database needed)

mrt check migrations/versions/
╭──────────┬──────────────────────────┬─────────┬──────┬────────────────────────────────────╮
│ Revision │ Pattern                  │ Sev     │ Line │ Message                            │
├──────────┼──────────────────────────┼─────────┼──────┼────────────────────────────────────┤
│ 004      │ DROP COLUMN in upgrade   │ error   │   12 │ Data permanently lost on rollback  │
│ 005      │ No-op downgrade          │ error   │    8 │ downgrade() does nothing           │
│ 006      │ INDEX without CONCURR.   │ warning │   19 │ Locks table during index build     │
╰──────────┴──────────────────────────┴─────────┴──────┴────────────────────────────────────╯
2 error(s), 1 warning(s)

What gets caught

Errors (will cause data loss or a broken rollback):

  • op.drop_column() in upgrade — data is gone even if downgrade re-adds the column
  • op.drop_table() in upgrade — all rows permanently lost
  • TRUNCATE in migration
  • def downgrade(): pass — rollback silently does nothing
  • No downgrade() function
  • rename_table / rename_column without reverse
  • DROP VIEW without recreating in downgrade
  • ALTER TYPE ... ADD VALUE (PostgreSQL ENUM) — can't roll back once rows use the new value
  • Add column + migrate data + drop original in one migration

Warnings (review before deploying):

  • NOT NULL without server_default
  • Column type change
  • Raw op.execute() / context.execute() without reverse
  • op.execute(sa.text(...)) — SQL inside sa.text() wrapper now fully analyzed
  • op.bulk_insert() without corresponding DELETE in downgrade
  • Bulk UPDATE without a reverse UPDATE in downgrade
  • ON DELETE CASCADE added
  • CREATE INDEX without CONCURRENTLY (PostgreSQL)
  • ADD COLUMN with DEFAULT on large tables
  • CREATE UNIQUE CONSTRAINT on existing data
  • DROP INDEX without recreating
  • DROP CONSTRAINT without recreating
  • ALTER SEQUENCE / setval
  • NOT NULL via raw SQL without reverse
  • NOT NULL without restoring nullable in downgrade

Databases

Static analysis Dynamic verification
PostgreSQL
SQLite
MySQL / MariaDB
pip install pytest-mrt[mysql]   # includes PyMySQL

Use mysql+pymysql://user:pass@host/dbname as your db_url.

CI/CD integration

Drop mrt check into any pipeline as a pre-deploy gate:

# GitHub Actions — blocks merge if unsafe migrations are detected
- name: Migration safety check
  run: mrt check alembic/versions/ --strict

Full examples for GitHub Actions, GitLab CI, Jenkins, and pre-commit hooks are in examples/ci-integration/.

Docker

Run tests locally against PostgreSQL or MySQL without installing anything:

docker compose run test-postgres
docker compose run test-mysql

See docker-compose.yml for the full configuration.

Changelog

See CHANGELOG.md for the full release history.

Documentation

Full docs at croc100.github.io/pytest-mrt

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_mrt-0.7.0.tar.gz (63.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_mrt-0.7.0-py3-none-any.whl (40.3 kB view details)

Uploaded Python 3

File details

Details for the file pytest_mrt-0.7.0.tar.gz.

File metadata

  • Download URL: pytest_mrt-0.7.0.tar.gz
  • Upload date:
  • Size: 63.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.17.0 {"ci":null,"cpu":"arm64","distro":{"name":"macOS","version":"26.4.1"},"implementation":{"name":"CPython","version":"3.14.3"},"installer":{"name":"hatch","version":"1.17.0"},"openssl_version":"OpenSSL 3.6.2 7 Apr 2026","python":"3.14.3","system":{"name":"Darwin","release":"25.4.0"}} HTTPX2/2.3.0

File hashes

Hashes for pytest_mrt-0.7.0.tar.gz
Algorithm Hash digest
SHA256 48d5933c5230d37171672f8372cb3dee34e49dc8144fecf034cf70d54d417db9
MD5 cd24788f9e8f2e58964281ba23a87e7d
BLAKE2b-256 fcf166c28b7ae299c943c83795167e902102e87bff038ca852b3a98770a29f52

See more details on using hashes here.

File details

Details for the file pytest_mrt-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: pytest_mrt-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 40.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.17.0 {"ci":null,"cpu":"arm64","distro":{"name":"macOS","version":"26.4.1"},"implementation":{"name":"CPython","version":"3.14.3"},"installer":{"name":"hatch","version":"1.17.0"},"openssl_version":"OpenSSL 3.6.2 7 Apr 2026","python":"3.14.3","system":{"name":"Darwin","release":"25.4.0"}} HTTPX2/2.3.0

File hashes

Hashes for pytest_mrt-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f1a922864df476adc22c5c526d62b8acb6d81ea8e15397f6e2fc3465ed794a57
MD5 79e5044464872601e5cd90811a14073c
BLAKE2b-256 ec74f2d8c693edba9009f071f37b940fa9a6f70298039f40ff7ffbd2e4880ab8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page