mirror of https://github.com/pressly/goose.git
286 lines
11 KiB
Go
286 lines
11 KiB
Go
package goose
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNewGoMigration(t *testing.T) {
|
|
t.Run("valid_both_nil", func(t *testing.T) {
|
|
m := NewGoMigration(1, nil, nil)
|
|
// roundtrip
|
|
require.EqualValues(t, 1, m.Version)
|
|
require.Equal(t, TypeGo, m.Type)
|
|
require.True(t, m.Registered)
|
|
require.EqualValues(t, -1, m.Next)
|
|
require.EqualValues(t, -1, m.Previous)
|
|
require.Empty(t, m.Source)
|
|
require.Nil(t, m.UpFnNoTxContext)
|
|
require.Nil(t, m.DownFnNoTxContext)
|
|
require.Nil(t, m.UpFnContext)
|
|
require.Nil(t, m.DownFnContext)
|
|
require.Nil(t, m.UpFn)
|
|
require.Nil(t, m.DownFn)
|
|
require.Nil(t, m.UpFnNoTx)
|
|
require.Nil(t, m.DownFnNoTx)
|
|
require.NotNil(t, m.goUp)
|
|
require.NotNil(t, m.goDown)
|
|
require.Equal(t, TransactionEnabled, m.goUp.Mode)
|
|
require.Equal(t, TransactionEnabled, m.goDown.Mode)
|
|
})
|
|
t.Run("all_set", func(t *testing.T) {
|
|
// This will eventually be an error when registering migrations.
|
|
m := NewGoMigration(
|
|
1,
|
|
&GoFunc{RunTx: func(context.Context, *sql.Tx) error { return nil }, RunDB: func(context.Context, *sql.DB) error { return nil }},
|
|
&GoFunc{RunTx: func(context.Context, *sql.Tx) error { return nil }, RunDB: func(context.Context, *sql.DB) error { return nil }},
|
|
)
|
|
// check only functions
|
|
require.NotNil(t, m.UpFn)
|
|
require.NotNil(t, m.UpFnContext)
|
|
require.NotNil(t, m.UpFnNoTx)
|
|
require.NotNil(t, m.UpFnNoTxContext)
|
|
require.NotNil(t, m.DownFn)
|
|
require.NotNil(t, m.DownFnContext)
|
|
require.NotNil(t, m.DownFnNoTx)
|
|
require.NotNil(t, m.DownFnNoTxContext)
|
|
})
|
|
}
|
|
|
|
func TestTransactionMode(t *testing.T) {
|
|
t.Cleanup(ResetGlobalMigrations)
|
|
|
|
runDB := func(context.Context, *sql.DB) error { return nil }
|
|
runTx := func(context.Context, *sql.Tx) error { return nil }
|
|
|
|
err := SetGlobalMigrations(
|
|
NewGoMigration(1, &GoFunc{RunTx: runTx, RunDB: runDB}, nil), // cannot specify both
|
|
)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "up function: must specify exactly one of RunTx or RunDB")
|
|
err = SetGlobalMigrations(
|
|
NewGoMigration(1, nil, &GoFunc{RunTx: runTx, RunDB: runDB}), // cannot specify both
|
|
)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "down function: must specify exactly one of RunTx or RunDB")
|
|
err = SetGlobalMigrations(
|
|
NewGoMigration(1, &GoFunc{RunTx: runTx, Mode: TransactionDisabled}, nil), // invalid explicit mode tx
|
|
)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "up function: transaction mode must be enabled or unspecified when RunTx is set")
|
|
err = SetGlobalMigrations(
|
|
NewGoMigration(1, nil, &GoFunc{RunTx: runTx, Mode: TransactionDisabled}), // invalid explicit mode tx
|
|
)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "down function: transaction mode must be enabled or unspecified when RunTx is set")
|
|
err = SetGlobalMigrations(
|
|
NewGoMigration(1, &GoFunc{RunDB: runDB, Mode: TransactionEnabled}, nil), // invalid explicit mode no-tx
|
|
)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "up function: transaction mode must be disabled or unspecified when RunDB is set")
|
|
err = SetGlobalMigrations(
|
|
NewGoMigration(1, nil, &GoFunc{RunDB: runDB, Mode: TransactionEnabled}), // invalid explicit mode no-tx
|
|
)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "down function: transaction mode must be disabled or unspecified when RunDB is set")
|
|
|
|
t.Run("default_mode", func(t *testing.T) {
|
|
t.Cleanup(ResetGlobalMigrations)
|
|
|
|
m := NewGoMigration(1, nil, nil)
|
|
err = SetGlobalMigrations(m)
|
|
require.NoError(t, err)
|
|
require.Len(t, registeredGoMigrations, 1)
|
|
registered := registeredGoMigrations[1]
|
|
require.NotNil(t, registered.goUp)
|
|
require.NotNil(t, registered.goDown)
|
|
require.Equal(t, TransactionEnabled, registered.goUp.Mode)
|
|
require.Equal(t, TransactionEnabled, registered.goDown.Mode)
|
|
|
|
migration2 := NewGoMigration(2, nil, nil)
|
|
// reset so we can check the default is set
|
|
migration2.goUp.Mode, migration2.goDown.Mode = 0, 0
|
|
err = SetGlobalMigrations(migration2)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid go migration: up function: invalid mode: 0")
|
|
|
|
migration3 := NewGoMigration(3, nil, nil)
|
|
// reset so we can check the default is set
|
|
migration3.goDown.Mode = 0
|
|
err = SetGlobalMigrations(migration3)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid go migration: down function: invalid mode: 0")
|
|
})
|
|
t.Run("unknown_mode", func(t *testing.T) {
|
|
m := NewGoMigration(1, nil, nil)
|
|
m.goUp.Mode, m.goDown.Mode = 3, 3 // reset to default
|
|
err := SetGlobalMigrations(m)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid mode: 3")
|
|
})
|
|
}
|
|
|
|
func TestLegacyFunctions(t *testing.T) {
|
|
t.Cleanup(ResetGlobalMigrations)
|
|
|
|
runDB := func(context.Context, *sql.DB) error { return nil }
|
|
runTx := func(context.Context, *sql.Tx) error { return nil }
|
|
|
|
assertMigration := func(t *testing.T, m *Migration, version int64) {
|
|
t.Helper()
|
|
require.Equal(t, version, m.Version)
|
|
require.Equal(t, TypeGo, m.Type)
|
|
require.True(t, m.Registered)
|
|
require.EqualValues(t, -1, m.Next)
|
|
require.EqualValues(t, -1, m.Previous)
|
|
require.Empty(t, m.Source)
|
|
}
|
|
|
|
t.Run("all_tx", func(t *testing.T) {
|
|
t.Cleanup(ResetGlobalMigrations)
|
|
err := SetGlobalMigrations(
|
|
NewGoMigration(1, &GoFunc{RunTx: runTx}, &GoFunc{RunTx: runTx}),
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, registeredGoMigrations, 1)
|
|
m := registeredGoMigrations[1]
|
|
assertMigration(t, m, 1)
|
|
// Legacy functions.
|
|
require.Nil(t, m.UpFnNoTxContext)
|
|
require.Nil(t, m.DownFnNoTxContext)
|
|
// Context-aware functions.
|
|
require.NotNil(t, m.goUp)
|
|
require.NotNil(t, m.UpFnContext)
|
|
require.NotNil(t, m.goDown)
|
|
require.NotNil(t, m.DownFnContext)
|
|
// Always nil
|
|
require.NotNil(t, m.UpFn)
|
|
require.NotNil(t, m.DownFn)
|
|
require.Nil(t, m.UpFnNoTx)
|
|
require.Nil(t, m.DownFnNoTx)
|
|
})
|
|
t.Run("all_db", func(t *testing.T) {
|
|
t.Cleanup(ResetGlobalMigrations)
|
|
err := SetGlobalMigrations(
|
|
NewGoMigration(2, &GoFunc{RunDB: runDB}, &GoFunc{RunDB: runDB}),
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, registeredGoMigrations, 1)
|
|
m := registeredGoMigrations[2]
|
|
assertMigration(t, m, 2)
|
|
// Legacy functions.
|
|
require.NotNil(t, m.UpFnNoTxContext)
|
|
require.NotNil(t, m.goUp)
|
|
require.NotNil(t, m.DownFnNoTxContext)
|
|
require.NotNil(t, m.goDown)
|
|
// Context-aware functions.
|
|
require.Nil(t, m.UpFnContext)
|
|
require.Nil(t, m.DownFnContext)
|
|
// Always nil
|
|
require.Nil(t, m.UpFn)
|
|
require.Nil(t, m.DownFn)
|
|
require.NotNil(t, m.UpFnNoTx)
|
|
require.NotNil(t, m.DownFnNoTx)
|
|
})
|
|
}
|
|
|
|
func TestGlobalRegister(t *testing.T) {
|
|
t.Cleanup(ResetGlobalMigrations)
|
|
|
|
// runDB := func(context.Context, *sql.DB) error { return nil }
|
|
runTx := func(context.Context, *sql.Tx) error { return nil }
|
|
|
|
// Success.
|
|
err := SetGlobalMigrations([]*Migration{}...)
|
|
require.NoError(t, err)
|
|
err = SetGlobalMigrations(
|
|
NewGoMigration(1, &GoFunc{RunTx: runTx}, nil),
|
|
)
|
|
require.NoError(t, err)
|
|
// Try to register the same migration again.
|
|
err = SetGlobalMigrations(
|
|
NewGoMigration(1, &GoFunc{RunTx: runTx}, nil),
|
|
)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "go migration with version 1 already registered")
|
|
err = SetGlobalMigrations(&Migration{Registered: true, Version: 2, Type: TypeGo})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "must use NewGoMigration to construct migrations")
|
|
}
|
|
|
|
func TestCheckMigration(t *testing.T) {
|
|
// Success.
|
|
err := checkGoMigration(NewGoMigration(1, nil, nil))
|
|
require.NoError(t, err)
|
|
// Failures.
|
|
err = checkGoMigration(&Migration{})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "must use NewGoMigration to construct migrations")
|
|
err = checkGoMigration(&Migration{construct: true})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "must be registered")
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), `type must be "go"`)
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "version must be greater than zero")
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, goUp: &GoFunc{}, goDown: &GoFunc{}})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "up function: invalid mode: 0")
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, goUp: &GoFunc{Mode: TransactionEnabled}, goDown: &GoFunc{}})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "down function: invalid mode: 0")
|
|
// Success.
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, goUp: &GoFunc{Mode: TransactionEnabled}, goDown: &GoFunc{Mode: TransactionEnabled}})
|
|
require.NoError(t, err)
|
|
// Failures.
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, Source: "foo"})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), `source must have .go extension: "foo"`)
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1, Source: "foo.go"})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), `no filename separator '_' found`)
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 2, Source: "00001_foo.sql"})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), `source must have .go extension: "00001_foo.sql"`)
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 2, Source: "00001_foo.go"})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), `version:2 does not match numeric component in source "00001_foo.go"`)
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
|
|
UpFnContext: func(context.Context, *sql.Tx) error { return nil },
|
|
UpFnNoTxContext: func(context.Context, *sql.DB) error { return nil },
|
|
goUp: &GoFunc{Mode: TransactionEnabled},
|
|
goDown: &GoFunc{Mode: TransactionEnabled},
|
|
})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "must specify exactly one of UpFnContext or UpFnNoTxContext")
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
|
|
DownFnContext: func(context.Context, *sql.Tx) error { return nil },
|
|
DownFnNoTxContext: func(context.Context, *sql.DB) error { return nil },
|
|
goUp: &GoFunc{Mode: TransactionEnabled},
|
|
goDown: &GoFunc{Mode: TransactionEnabled},
|
|
})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "must specify exactly one of DownFnContext or DownFnNoTxContext")
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
|
|
UpFn: func(*sql.Tx) error { return nil },
|
|
UpFnNoTx: func(*sql.DB) error { return nil },
|
|
goUp: &GoFunc{Mode: TransactionEnabled},
|
|
goDown: &GoFunc{Mode: TransactionEnabled},
|
|
})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "must specify exactly one of UpFn or UpFnNoTx")
|
|
err = checkGoMigration(&Migration{construct: true, Registered: true, Type: TypeGo, Version: 1,
|
|
DownFn: func(*sql.Tx) error { return nil },
|
|
DownFnNoTx: func(*sql.DB) error { return nil },
|
|
goUp: &GoFunc{Mode: TransactionEnabled},
|
|
goDown: &GoFunc{Mode: TransactionEnabled},
|
|
})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "must specify exactly one of DownFn or DownFnNoTx")
|
|
}
|