mirror of
https://github.com/therootcompany/golib.git
synced 2026-04-24 12:48:00 +00:00
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.
146 lines
4.0 KiB
Go
146 lines
4.0 KiB
Go
package mymigrate_test
|
|
|
|
import (
|
|
"database/sql"
|
|
"os"
|
|
"testing"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
|
|
"github.com/therootcompany/golib/database/sqlmigrate/mymigrate"
|
|
)
|
|
|
|
// connect opens a *sql.Conn from MYSQL_TEST_DSN, skips the test if the
|
|
// env var is unset, and ensures _migrations does not exist on entry,
|
|
// with cleanup on exit.
|
|
//
|
|
// Note: tests run against the database in the DSN (no per-test database)
|
|
// because hosted MariaDB users typically have access to a single schema
|
|
// only. Tests must not be run concurrently against the same DSN.
|
|
func connect(t *testing.T) *sql.Conn {
|
|
t.Helper()
|
|
dsn := os.Getenv("MYSQL_TEST_DSN")
|
|
if dsn == "" {
|
|
t.Skip("MYSQL_TEST_DSN not set")
|
|
}
|
|
|
|
ctx := t.Context()
|
|
db, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = db.Close() })
|
|
|
|
conn, err := db.Conn(ctx)
|
|
if err != nil {
|
|
t.Fatalf("conn: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = conn.Close() })
|
|
|
|
if _, err := conn.ExecContext(ctx, "DROP TABLE IF EXISTS _migrations"); err != nil {
|
|
t.Fatalf("pre-cleanup _migrations: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_, _ = conn.ExecContext(ctx, "DROP TABLE IF EXISTS _migrations")
|
|
})
|
|
|
|
return conn
|
|
}
|
|
|
|
// TestAppliedNoMigrationsTable verifies Applied returns (nil, nil) when
|
|
// the _migrations table does not exist (MySQL error 1146). Defensive
|
|
// regression test against drivers that may surface the error lazily.
|
|
func TestAppliedNoMigrationsTable(t *testing.T) {
|
|
conn := connect(t)
|
|
|
|
m := mymigrate.New(conn)
|
|
applied, err := m.Applied(t.Context())
|
|
if err != nil {
|
|
t.Fatalf("Applied() error = %v, want nil", err)
|
|
}
|
|
if len(applied) != 0 {
|
|
t.Errorf("Applied() len = %d, want 0", len(applied))
|
|
}
|
|
}
|
|
|
|
// TestAppliedWithMigrationsTable verifies Applied reads existing rows.
|
|
func TestAppliedWithMigrationsTable(t *testing.T) {
|
|
conn := connect(t)
|
|
ctx := t.Context()
|
|
|
|
if _, err := conn.ExecContext(ctx, `
|
|
CREATE TABLE _migrations (id VARCHAR(16), name VARCHAR(255))
|
|
`); err != nil {
|
|
t.Fatalf("create table: %v", err)
|
|
}
|
|
if _, err := conn.ExecContext(ctx,
|
|
`INSERT INTO _migrations (id, name) VALUES ('abc12345', '0001_init'), ('def67890', '0002_users')`,
|
|
); err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
m := mymigrate.New(conn)
|
|
applied, err := m.Applied(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Applied() error = %v", err)
|
|
}
|
|
if len(applied) != 2 {
|
|
t.Fatalf("Applied() len = %d, want 2", len(applied))
|
|
}
|
|
if applied[0].Name != "0001_init" || applied[0].ID != "abc12345" {
|
|
t.Errorf("applied[0] = %+v, want {abc12345 0001_init}", applied[0])
|
|
}
|
|
if applied[1].Name != "0002_users" || applied[1].ID != "def67890" {
|
|
t.Errorf("applied[1] = %+v, want {def67890 0002_users}", applied[1])
|
|
}
|
|
}
|
|
|
|
// TestAppliedEmptyMigrationsTable verifies Applied returns an empty
|
|
// slice (not an error) when _migrations exists but has no rows.
|
|
func TestAppliedEmptyMigrationsTable(t *testing.T) {
|
|
conn := connect(t)
|
|
ctx := t.Context()
|
|
|
|
if _, err := conn.ExecContext(ctx, `CREATE TABLE _migrations (id VARCHAR(16), name VARCHAR(255))`); err != nil {
|
|
t.Fatalf("create table: %v", err)
|
|
}
|
|
|
|
m := mymigrate.New(conn)
|
|
applied, err := m.Applied(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Applied() error = %v", err)
|
|
}
|
|
if len(applied) != 0 {
|
|
t.Errorf("Applied() len = %d, want 0", len(applied))
|
|
}
|
|
}
|
|
|
|
// TestAppliedAfterDropTable verifies Applied still returns (nil, nil) after
|
|
// the _migrations table is dropped — exercises any prepared-statement
|
|
// caching the driver may do.
|
|
func TestAppliedAfterDropTable(t *testing.T) {
|
|
conn := connect(t)
|
|
ctx := t.Context()
|
|
|
|
if _, err := conn.ExecContext(ctx, `CREATE TABLE _migrations (id VARCHAR(16), name VARCHAR(255))`); err != nil {
|
|
t.Fatalf("create: %v", err)
|
|
}
|
|
|
|
m := mymigrate.New(conn)
|
|
if _, err := m.Applied(ctx); err != nil {
|
|
t.Fatalf("first Applied: %v", err)
|
|
}
|
|
|
|
if _, err := conn.ExecContext(ctx, `DROP TABLE _migrations`); err != nil {
|
|
t.Fatalf("drop: %v", err)
|
|
}
|
|
|
|
applied, err := m.Applied(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Applied() after DROP TABLE error = %v, want nil", err)
|
|
}
|
|
if len(applied) != 0 {
|
|
t.Errorf("Applied() len = %d, want 0", len(applied))
|
|
}
|
|
}
|