Across all four backends:
- TestAppliedOrdering: insert rows out of order, verify Applied()
returns them sorted by name. Guards against the ORDER BY clause
being dropped or the query returning rows in arbitrary order.
- TestEndToEndCycle: Collect → Up → Applied → Down → Applied via
the sqlmigrate orchestrator with real migration files. Catches
wiring bugs between Migrator and orchestrator that the in-package
mockMigrator tests cannot.
- TestDMLRollback: multi-statement DML migration where the last
statement fails, verifies earlier INSERTs are rolled back. MySQL
note: DML-only because MySQL implicitly commits DDL.
Dialect-specific:
- mymigrate TestMultiStatementsRequired: strip multiStatements=true
from the DSN, verify ExecUp fails with a clear error mentioning
multiStatements (rather than silently running only the first
statement of a multi-statement migration).
- litemigrate TestForeignKeyEnforcement: verifies FK constraints
are enforced when the DSN includes _pragma=foreign_keys(1).
Test fixture fix: cleanup closures now use context.Background()
instead of the test context. t.Context() is canceled before
t.Cleanup runs, so DB cleanup silently failed. Previously the
_migrations cleanup appeared to work because the next test's
connect() re-ran DROP TABLE at setup, but domain tables (test_*)
leaked across runs. New tests also pre-clean at setup for
self-healing after interrupted runs.
Hosted MariaDB users (e.g. todo_test_*) typically have access to a single
database/schema and cannot CREATE/DROP DATABASE. Drop the per-test database
isolation pattern in favour of dropping _migrations directly on entry and
exit, matching msmigrate's approach. Tests must not run concurrently
against the same DSN.
Apply the same lazy-error pattern fix to all backends, plus regression
tests that catch the bug.
pgmigrate is the confirmed-broken case (pgx/v5's Conn.Query is lazy and
surfaces 42P01 at rows.Err() once the prepared statement cache is primed).
The defensive check at rows.Err() is also added to mymigrate and msmigrate
in case their drivers exhibit similar behavior in some configurations.
litemigrate is refactored to probe sqlite_master with errors.Is(sql.ErrNoRows)
instead of string-matching the error message — SQLite returns the generic
SQLITE_ERROR code for "no such table" so a typed-error approach isn't
possible at the driver layer; the probe lets us use idiomatic errors.Is.
Tests:
- litemigrate: in-memory SQLite, runs on every go test (no infra)
- pgmigrate: PG_TEST_URL env-gated; verified against real Postgres,
TestAppliedAfterDropTable reproduces the agent's exact error
message ("reading rows: ... 42P01") without the fix
- mymigrate: MYSQL_TEST_DSN env-gated
- msmigrate: MSSQL_TEST_URL env-gated; verified against real SQL Server
Each backend has four cases: missing table, populated table, empty table,
and table-dropped-after-cache-primed (the lazy-error scenario).
Same issue as pgmigrate: *sql.DB is a connection pool, so each call
may land on a different connection. Migrations need a pinned connection
for session state (SET search_path, temp tables, etc.) to persist
across sequential calls. *sql.Conn (from db.Conn(ctx)) pins one
underlying connection for its lifetime.