mirror of https://github.com/status-im/migrate.git
1417 lines
36 KiB
Go
1417 lines
36 KiB
Go
package migrate
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
import (
|
|
dStub "github.com/golang-migrate/migrate/v4/database/stub"
|
|
"github.com/golang-migrate/migrate/v4/source"
|
|
sStub "github.com/golang-migrate/migrate/v4/source/stub"
|
|
)
|
|
|
|
// sourceStubMigrations hold the following migrations:
|
|
// u = up migration, d = down migration, n = version
|
|
// | 1 | - | 3 | 4 | 5 | - | 7 |
|
|
// | u d | - | u | u d | d | - | u d |
|
|
var sourceStubMigrations *source.Migrations
|
|
|
|
const (
|
|
srcDrvNameStub = "stub"
|
|
dbDrvNameStub = "stub"
|
|
)
|
|
|
|
func init() {
|
|
sourceStubMigrations = source.NewMigrations()
|
|
sourceStubMigrations.Append(&source.Migration{Version: 1, Direction: source.Up, Identifier: "CREATE 1"})
|
|
sourceStubMigrations.Append(&source.Migration{Version: 1, Direction: source.Down, Identifier: "DROP 1"})
|
|
sourceStubMigrations.Append(&source.Migration{Version: 3, Direction: source.Up, Identifier: "CREATE 3"})
|
|
sourceStubMigrations.Append(&source.Migration{Version: 4, Direction: source.Up, Identifier: "CREATE 4"})
|
|
sourceStubMigrations.Append(&source.Migration{Version: 4, Direction: source.Down, Identifier: "DROP 4"})
|
|
sourceStubMigrations.Append(&source.Migration{Version: 5, Direction: source.Down, Identifier: "DROP 5"})
|
|
sourceStubMigrations.Append(&source.Migration{Version: 7, Direction: source.Up, Identifier: "CREATE 7"})
|
|
sourceStubMigrations.Append(&source.Migration{Version: 7, Direction: source.Down, Identifier: "DROP 7"})
|
|
}
|
|
|
|
type DummyInstance struct{ Name string }
|
|
|
|
func TestNew(t *testing.T) {
|
|
m, err := New("stub://", "stub://")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if m.sourceName != srcDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.sourceName)
|
|
}
|
|
if m.sourceDrv == nil {
|
|
t.Error("expected sourceDrv not to be nil")
|
|
}
|
|
|
|
if m.databaseName != dbDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.databaseName)
|
|
}
|
|
if m.databaseDrv == nil {
|
|
t.Error("expected databaseDrv not to be nil")
|
|
}
|
|
}
|
|
|
|
func ExampleNew() {
|
|
// Read migrations from /home/mattes/migrations and connect to a local postgres database.
|
|
m, err := New("file:///home/mattes/migrations", "postgres://mattes:secret@localhost:5432/database?sslmode=disable")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Migrate all the way up ...
|
|
if err := m.Up(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestNewWithDatabaseInstance(t *testing.T) {
|
|
dummyDb := &DummyInstance{"database"}
|
|
dbInst, err := dStub.WithInstance(dummyDb, &dStub.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
m, err := NewWithDatabaseInstance("stub://", dbDrvNameStub, dbInst)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if m.sourceName != srcDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.sourceName)
|
|
}
|
|
if m.sourceDrv == nil {
|
|
t.Error("expected sourceDrv not to be nil")
|
|
}
|
|
|
|
if m.databaseName != dbDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.databaseName)
|
|
}
|
|
if m.databaseDrv == nil {
|
|
t.Error("expected databaseDrv not to be nil")
|
|
}
|
|
}
|
|
|
|
func ExampleNewWithDatabaseInstance() {
|
|
// Create and use an existing database instance.
|
|
db, err := sql.Open("postgres", "postgres://mattes:secret@localhost:5432/database?sslmode=disable")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer func() {
|
|
if err := db.Close(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
// Create driver instance from db.
|
|
// Check each driver if it supports the WithInstance function.
|
|
// `import "github.com/golang-migrate/migrate/v4/database/postgres"`
|
|
instance, err := dStub.WithInstance(db, &dStub.Config{})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Read migrations from /home/mattes/migrations and connect to a local postgres database.
|
|
m, err := NewWithDatabaseInstance("file:///home/mattes/migrations", "postgres", instance)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Migrate all the way up ...
|
|
if err := m.Up(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestNewWithSourceInstance(t *testing.T) {
|
|
dummySource := &DummyInstance{"source"}
|
|
sInst, err := sStub.WithInstance(dummySource, &sStub.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
m, err := NewWithSourceInstance(srcDrvNameStub, sInst, "stub://")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if m.sourceName != srcDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.sourceName)
|
|
}
|
|
if m.sourceDrv == nil {
|
|
t.Error("expected sourceDrv not to be nil")
|
|
}
|
|
|
|
if m.databaseName != dbDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.databaseName)
|
|
}
|
|
if m.databaseDrv == nil {
|
|
t.Error("expected databaseDrv not to be nil")
|
|
}
|
|
}
|
|
|
|
func ExampleNewWithSourceInstance() {
|
|
di := &DummyInstance{"think any client required for a source here"}
|
|
|
|
// Create driver instance from DummyInstance di.
|
|
// Check each driver if it support the WithInstance function.
|
|
// `import "github.com/golang-migrate/migrate/v4/source/stub"`
|
|
instance, err := sStub.WithInstance(di, &sStub.Config{})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Read migrations from Stub and connect to a local postgres database.
|
|
m, err := NewWithSourceInstance(srcDrvNameStub, instance, "postgres://mattes:secret@localhost:5432/database?sslmode=disable")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Migrate all the way up ...
|
|
if err := m.Up(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestNewWithInstance(t *testing.T) {
|
|
dummyDb := &DummyInstance{"database"}
|
|
dbInst, err := dStub.WithInstance(dummyDb, &dStub.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dummySource := &DummyInstance{"source"}
|
|
sInst, err := sStub.WithInstance(dummySource, &sStub.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
m, err := NewWithInstance(srcDrvNameStub, sInst, dbDrvNameStub, dbInst)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if m.sourceName != srcDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.sourceName)
|
|
}
|
|
if m.sourceDrv == nil {
|
|
t.Error("expected sourceDrv not to be nil")
|
|
}
|
|
|
|
if m.databaseName != dbDrvNameStub {
|
|
t.Errorf("expected stub, got %v", m.databaseName)
|
|
}
|
|
if m.databaseDrv == nil {
|
|
t.Error("expected databaseDrv not to be nil")
|
|
}
|
|
}
|
|
|
|
func ExampleNewWithInstance() {
|
|
// See NewWithDatabaseInstance and NewWithSourceInstance for an example.
|
|
}
|
|
|
|
func TestClose(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
sourceErr, databaseErr := m.Close()
|
|
if sourceErr != nil {
|
|
t.Error(sourceErr)
|
|
}
|
|
if databaseErr != nil {
|
|
t.Error(databaseErr)
|
|
}
|
|
}
|
|
|
|
func TestMigrate(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
|
|
tt := []struct {
|
|
version uint
|
|
expectErr error
|
|
expectVersion uint
|
|
expectSeq migrationSequence
|
|
}{
|
|
// migrate all the way Up in single steps
|
|
{
|
|
version: 0,
|
|
expectErr: os.ErrNotExist,
|
|
},
|
|
{
|
|
version: 1,
|
|
expectVersion: 1,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
},
|
|
},
|
|
{
|
|
version: 2,
|
|
expectErr: os.ErrNotExist,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
},
|
|
},
|
|
{
|
|
version: 3,
|
|
expectVersion: 3,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
},
|
|
},
|
|
{
|
|
version: 4,
|
|
expectVersion: 4,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
},
|
|
},
|
|
{
|
|
version: 5,
|
|
expectVersion: 5,
|
|
expectSeq: migrationSequence{ // 5 has no up migration
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
},
|
|
},
|
|
{
|
|
version: 6,
|
|
expectErr: os.ErrNotExist,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
},
|
|
},
|
|
{
|
|
version: 7,
|
|
expectVersion: 7,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
},
|
|
},
|
|
{
|
|
version: 8,
|
|
expectErr: os.ErrNotExist,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
},
|
|
},
|
|
|
|
// migrate all the way Down in single steps
|
|
{
|
|
version: 6,
|
|
expectErr: os.ErrNotExist,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
},
|
|
},
|
|
{
|
|
version: 5,
|
|
expectVersion: 5,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
},
|
|
},
|
|
{
|
|
version: 4,
|
|
expectVersion: 4,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
},
|
|
},
|
|
{
|
|
version: 3,
|
|
expectVersion: 3,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
{
|
|
version: 2,
|
|
expectErr: os.ErrNotExist,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
{
|
|
version: 1,
|
|
expectVersion: 1,
|
|
expectSeq: migrationSequence{ // 3 has no down migration
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
{
|
|
version: 0,
|
|
expectErr: os.ErrNotExist,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
|
|
// migrate all the way Up in one step
|
|
{
|
|
version: 7,
|
|
expectVersion: 7,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
},
|
|
},
|
|
|
|
// migrate all the way Down in one step
|
|
{
|
|
version: 1,
|
|
expectVersion: 1,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
|
|
// can't migrate the same version twice
|
|
{
|
|
version: 1,
|
|
expectErr: ErrNoChange,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, v := range tt {
|
|
err := m.Migrate(v.version)
|
|
if (v.expectErr == os.ErrNotExist && !os.IsNotExist(err)) ||
|
|
(v.expectErr != os.ErrNotExist && err != v.expectErr) {
|
|
t.Errorf("expected err %v, got %v, in %v", v.expectErr, err, i)
|
|
|
|
} else if err == nil {
|
|
version, _, err := m.Version()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if version != v.expectVersion {
|
|
t.Errorf("expected version %v, got %v, in %v", v.expectVersion, version, i)
|
|
}
|
|
}
|
|
equalDbSeq(t, i, v.expectSeq, dbDrv)
|
|
}
|
|
}
|
|
|
|
func TestMigrateDirty(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
if err := dbDrv.SetVersion(0, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err := m.Migrate(1)
|
|
if _, ok := err.(ErrDirty); !ok {
|
|
t.Fatalf("expected ErrDirty, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSteps(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
|
|
tt := []struct {
|
|
steps int
|
|
expectErr error
|
|
expectVersion int
|
|
expectSeq migrationSequence
|
|
}{
|
|
// step must be != 0
|
|
{
|
|
steps: 0,
|
|
expectErr: ErrNoChange,
|
|
},
|
|
|
|
// can't go Down if ErrNilVersion
|
|
{
|
|
steps: -1,
|
|
expectErr: os.ErrNotExist,
|
|
},
|
|
|
|
// migrate all the way Up
|
|
{
|
|
steps: 1,
|
|
expectVersion: 1,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1")},
|
|
},
|
|
{
|
|
steps: 1,
|
|
expectVersion: 3,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
},
|
|
},
|
|
{
|
|
steps: 1,
|
|
expectVersion: 4,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
},
|
|
},
|
|
{
|
|
steps: 1,
|
|
expectVersion: 5,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
},
|
|
},
|
|
{
|
|
steps: 1,
|
|
expectVersion: 7,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
},
|
|
},
|
|
{
|
|
steps: 1,
|
|
expectErr: os.ErrNotExist,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
},
|
|
},
|
|
|
|
// migrate all the way Down
|
|
{
|
|
steps: -1,
|
|
expectVersion: 5,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
},
|
|
},
|
|
{
|
|
steps: -1,
|
|
expectVersion: 4,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
},
|
|
},
|
|
{
|
|
steps: -1,
|
|
expectVersion: 3,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
{
|
|
steps: -1,
|
|
expectVersion: 1,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
{
|
|
steps: -1,
|
|
expectVersion: -1,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
},
|
|
},
|
|
|
|
// migrate Up in bigger step
|
|
{
|
|
steps: 4,
|
|
expectVersion: 5,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
},
|
|
},
|
|
|
|
// apply one migration, then reaches out of boundary
|
|
{
|
|
steps: 2,
|
|
expectErr: ErrShortLimit{1},
|
|
expectVersion: 7,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
},
|
|
},
|
|
|
|
// migrate Down in bigger step
|
|
{
|
|
steps: -4,
|
|
expectVersion: 1,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
},
|
|
},
|
|
|
|
// apply one migration, then reaches out of boundary
|
|
{
|
|
steps: -2,
|
|
expectErr: ErrShortLimit{1},
|
|
expectVersion: -1,
|
|
expectSeq: migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, v := range tt {
|
|
err := m.Steps(v.steps)
|
|
if (v.expectErr == os.ErrNotExist && !os.IsNotExist(err)) ||
|
|
(v.expectErr != os.ErrNotExist && err != v.expectErr) {
|
|
t.Errorf("expected err %v, got %v, in %v", v.expectErr, err, i)
|
|
|
|
} else if err == nil {
|
|
version, _, err := m.Version()
|
|
if err != ErrNilVersion && err != nil {
|
|
t.Error(err)
|
|
}
|
|
if v.expectVersion == -1 && err != ErrNilVersion {
|
|
t.Errorf("expected ErrNilVersion, got %v, in %v", version, i)
|
|
|
|
} else if v.expectVersion >= 0 && version != uint(v.expectVersion) {
|
|
t.Errorf("expected version %v, got %v, in %v", v.expectVersion, version, i)
|
|
}
|
|
}
|
|
equalDbSeq(t, i, v.expectSeq, dbDrv)
|
|
}
|
|
}
|
|
|
|
func TestStepsDirty(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
if err := dbDrv.SetVersion(0, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err := m.Steps(1)
|
|
if _, ok := err.(ErrDirty); !ok {
|
|
t.Fatalf("expected ErrDirty, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestUpAndDown(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
|
|
// go Up first
|
|
if err := m.Up(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expectedSequence := migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
}
|
|
equalDbSeq(t, 0, expectedSequence, dbDrv)
|
|
|
|
// go Down
|
|
if err := m.Down(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expectedSequence = migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
}
|
|
equalDbSeq(t, 1, expectedSequence, dbDrv)
|
|
|
|
// go 1 Up and then all the way Up
|
|
if err := m.Steps(1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expectedSequence = migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
}
|
|
equalDbSeq(t, 2, expectedSequence, dbDrv)
|
|
|
|
if err := m.Up(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expectedSequence = migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
}
|
|
equalDbSeq(t, 3, expectedSequence, dbDrv)
|
|
|
|
// go 1 Down and then all the way Down
|
|
if err := m.Steps(-1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expectedSequence = migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
}
|
|
equalDbSeq(t, 1, expectedSequence, dbDrv)
|
|
|
|
if err := m.Down(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expectedSequence = migrationSequence{
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
mr("CREATE 1"),
|
|
mr("CREATE 3"),
|
|
mr("CREATE 4"),
|
|
mr("CREATE 7"),
|
|
mr("DROP 7"),
|
|
mr("DROP 5"),
|
|
mr("DROP 4"),
|
|
mr("DROP 1"),
|
|
}
|
|
equalDbSeq(t, 1, expectedSequence, dbDrv)
|
|
}
|
|
|
|
func TestUpDirty(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
if err := dbDrv.SetVersion(0, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err := m.Up()
|
|
if _, ok := err.(ErrDirty); !ok {
|
|
t.Fatalf("expected ErrDirty, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDownDirty(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
if err := dbDrv.SetVersion(0, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err := m.Down()
|
|
if _, ok := err.(ErrDirty); !ok {
|
|
t.Fatalf("expected ErrDirty, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDrop(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
|
|
if err := m.Drop(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if dbDrv.MigrationSequence[len(dbDrv.MigrationSequence)-1] != dStub.DROP {
|
|
t.Fatalf("expected database to DROP, got sequence %v", dbDrv.MigrationSequence)
|
|
}
|
|
}
|
|
|
|
func TestVersion(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
|
|
_, _, err := m.Version()
|
|
if err != ErrNilVersion {
|
|
t.Fatalf("expected ErrNilVersion, got %v", err)
|
|
}
|
|
|
|
if err := dbDrv.Run(bytes.NewBufferString("1_up")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := dbDrv.SetVersion(1, false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
v, _, err := m.Version()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if v != 1 {
|
|
t.Fatalf("expected version 1, got %v", v)
|
|
}
|
|
}
|
|
|
|
func TestRun(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
|
|
mx, err := NewMigration(nil, "", 1, 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := m.Run(mx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
v, _, err := m.Version()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if v != 2 {
|
|
t.Errorf("expected version 2, got %v", v)
|
|
}
|
|
}
|
|
|
|
func TestRunDirty(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
if err := dbDrv.SetVersion(0, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
migr, err := NewMigration(nil, "", 1, 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = m.Run(migr)
|
|
if _, ok := err.(ErrDirty); !ok {
|
|
t.Fatalf("expected ErrDirty, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestForce(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
|
|
if err := m.Force(7); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
v, dirty, err := m.Version()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if dirty {
|
|
t.Errorf("expected dirty to be false")
|
|
}
|
|
if v != 7 {
|
|
t.Errorf("expected version to be 7")
|
|
}
|
|
}
|
|
|
|
func TestForceDirty(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
dbDrv := m.databaseDrv.(*dStub.Stub)
|
|
if err := dbDrv.SetVersion(0, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := m.Force(1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRead(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
|
|
tt := []struct {
|
|
from int
|
|
to int
|
|
expectErr error
|
|
expectMigrations migrationSequence
|
|
}{
|
|
{from: -1, to: -1, expectErr: ErrNoChange},
|
|
{from: -1, to: 0, expectErr: os.ErrNotExist},
|
|
{from: -1, to: 1, expectErr: nil, expectMigrations: newMigSeq(M(1))},
|
|
{from: -1, to: 2, expectErr: os.ErrNotExist},
|
|
{from: -1, to: 3, expectErr: nil, expectMigrations: newMigSeq(M(1), M(3))},
|
|
{from: -1, to: 4, expectErr: nil, expectMigrations: newMigSeq(M(1), M(3), M(4))},
|
|
{from: -1, to: 5, expectErr: nil, expectMigrations: newMigSeq(M(1), M(3), M(4), M(5))},
|
|
{from: -1, to: 6, expectErr: os.ErrNotExist},
|
|
{from: -1, to: 7, expectErr: nil, expectMigrations: newMigSeq(M(1), M(3), M(4), M(5), M(7))},
|
|
{from: -1, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 0, to: -1, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 1, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 3, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 4, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 5, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 7, expectErr: os.ErrNotExist},
|
|
{from: 0, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 1, to: -1, expectErr: nil, expectMigrations: newMigSeq(M(1, -1))},
|
|
{from: 1, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 1, to: 1, expectErr: ErrNoChange},
|
|
{from: 1, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 1, to: 3, expectErr: nil, expectMigrations: newMigSeq(M(3))},
|
|
{from: 1, to: 4, expectErr: nil, expectMigrations: newMigSeq(M(3), M(4))},
|
|
{from: 1, to: 5, expectErr: nil, expectMigrations: newMigSeq(M(3), M(4), M(5))},
|
|
{from: 1, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 1, to: 7, expectErr: nil, expectMigrations: newMigSeq(M(3), M(4), M(5), M(7))},
|
|
{from: 1, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 2, to: -1, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 1, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 3, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 4, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 5, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 7, expectErr: os.ErrNotExist},
|
|
{from: 2, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 3, to: -1, expectErr: nil, expectMigrations: newMigSeq(M(3, 1), M(1, -1))},
|
|
{from: 3, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 3, to: 1, expectErr: nil, expectMigrations: newMigSeq(M(3, 1))},
|
|
{from: 3, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 3, to: 3, expectErr: ErrNoChange},
|
|
{from: 3, to: 4, expectErr: nil, expectMigrations: newMigSeq(M(4))},
|
|
{from: 3, to: 5, expectErr: nil, expectMigrations: newMigSeq(M(4), M(5))},
|
|
{from: 3, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 3, to: 7, expectErr: nil, expectMigrations: newMigSeq(M(4), M(5), M(7))},
|
|
{from: 3, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 4, to: -1, expectErr: nil, expectMigrations: newMigSeq(M(4, 3), M(3, 1), M(1, -1))},
|
|
{from: 4, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 4, to: 1, expectErr: nil, expectMigrations: newMigSeq(M(4, 3), M(3, 1))},
|
|
{from: 4, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 4, to: 3, expectErr: nil, expectMigrations: newMigSeq(M(4, 3))},
|
|
{from: 4, to: 4, expectErr: ErrNoChange},
|
|
{from: 4, to: 5, expectErr: nil, expectMigrations: newMigSeq(M(5))},
|
|
{from: 4, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 4, to: 7, expectErr: nil, expectMigrations: newMigSeq(M(5), M(7))},
|
|
{from: 4, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 5, to: -1, expectErr: nil, expectMigrations: newMigSeq(M(5, 4), M(4, 3), M(3, 1), M(1, -1))},
|
|
{from: 5, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 5, to: 1, expectErr: nil, expectMigrations: newMigSeq(M(5, 4), M(4, 3), M(3, 1))},
|
|
{from: 5, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 5, to: 3, expectErr: nil, expectMigrations: newMigSeq(M(5, 4), M(4, 3))},
|
|
{from: 5, to: 4, expectErr: nil, expectMigrations: newMigSeq(M(5, 4))},
|
|
{from: 5, to: 5, expectErr: ErrNoChange},
|
|
{from: 5, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 5, to: 7, expectErr: nil, expectMigrations: newMigSeq(M(7))},
|
|
{from: 5, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 6, to: -1, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 1, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 3, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 4, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 5, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 7, expectErr: os.ErrNotExist},
|
|
{from: 6, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 7, to: -1, expectErr: nil, expectMigrations: newMigSeq(M(7, 5), M(5, 4), M(4, 3), M(3, 1), M(1, -1))},
|
|
{from: 7, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 7, to: 1, expectErr: nil, expectMigrations: newMigSeq(M(7, 5), M(5, 4), M(4, 3), M(3, 1))},
|
|
{from: 7, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 7, to: 3, expectErr: nil, expectMigrations: newMigSeq(M(7, 5), M(5, 4), M(4, 3))},
|
|
{from: 7, to: 4, expectErr: nil, expectMigrations: newMigSeq(M(7, 5), M(5, 4))},
|
|
{from: 7, to: 5, expectErr: nil, expectMigrations: newMigSeq(M(7, 5))},
|
|
{from: 7, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 7, to: 7, expectErr: ErrNoChange},
|
|
{from: 7, to: 8, expectErr: os.ErrNotExist},
|
|
|
|
{from: 8, to: -1, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 0, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 1, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 2, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 3, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 4, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 5, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 6, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 7, expectErr: os.ErrNotExist},
|
|
{from: 8, to: 8, expectErr: os.ErrNotExist},
|
|
}
|
|
|
|
for i, v := range tt {
|
|
ret := make(chan interface{})
|
|
go m.read(v.from, v.to, ret)
|
|
migrations, err := migrationsFromChannel(ret)
|
|
|
|
if (v.expectErr == os.ErrNotExist && !os.IsNotExist(err)) ||
|
|
(v.expectErr != os.ErrNotExist && v.expectErr != err) {
|
|
t.Errorf("expected %v, got %v, in %v", v.expectErr, err, i)
|
|
t.Logf("%v, in %v", migrations, i)
|
|
}
|
|
if len(v.expectMigrations) > 0 {
|
|
equalMigSeq(t, i, v.expectMigrations, migrations)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadUp(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
|
|
tt := []struct {
|
|
from int
|
|
limit int // -1 means no limit
|
|
expectErr error
|
|
expectMigrations migrationSequence
|
|
}{
|
|
{from: -1, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(1), M(3), M(4), M(5), M(7))},
|
|
{from: -1, limit: 0, expectErr: ErrNoChange},
|
|
{from: -1, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(1))},
|
|
{from: -1, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(1), M(3))},
|
|
|
|
{from: 0, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 0, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 0, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 0, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 1, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(3), M(4), M(5), M(7))},
|
|
{from: 1, limit: 0, expectErr: ErrNoChange},
|
|
{from: 1, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(3))},
|
|
{from: 1, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(3), M(4))},
|
|
|
|
{from: 2, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 2, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 2, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 2, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 3, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(4), M(5), M(7))},
|
|
{from: 3, limit: 0, expectErr: ErrNoChange},
|
|
{from: 3, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(4))},
|
|
{from: 3, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(4), M(5))},
|
|
|
|
{from: 4, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(5), M(7))},
|
|
{from: 4, limit: 0, expectErr: ErrNoChange},
|
|
{from: 4, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(5))},
|
|
{from: 4, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(5), M(7))},
|
|
|
|
{from: 5, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(7))},
|
|
{from: 5, limit: 0, expectErr: ErrNoChange},
|
|
{from: 5, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(7))},
|
|
{from: 5, limit: 2, expectErr: ErrShortLimit{1}, expectMigrations: newMigSeq(M(7))},
|
|
|
|
{from: 6, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 6, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 6, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 6, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 7, limit: -1, expectErr: ErrNoChange},
|
|
{from: 7, limit: 0, expectErr: ErrNoChange},
|
|
{from: 7, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 7, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 8, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 8, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 8, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 8, limit: 2, expectErr: os.ErrNotExist},
|
|
}
|
|
|
|
for i, v := range tt {
|
|
ret := make(chan interface{})
|
|
go m.readUp(v.from, v.limit, ret)
|
|
migrations, err := migrationsFromChannel(ret)
|
|
|
|
if (v.expectErr == os.ErrNotExist && !os.IsNotExist(err)) ||
|
|
(v.expectErr != os.ErrNotExist && v.expectErr != err) {
|
|
t.Errorf("expected %v, got %v, in %v", v.expectErr, err, i)
|
|
t.Logf("%v, in %v", migrations, i)
|
|
}
|
|
if len(v.expectMigrations) > 0 {
|
|
equalMigSeq(t, i, v.expectMigrations, migrations)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadDown(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
|
|
tt := []struct {
|
|
from int
|
|
limit int // -1 means no limit
|
|
expectErr error
|
|
expectMigrations migrationSequence
|
|
}{
|
|
{from: -1, limit: -1, expectErr: ErrNoChange},
|
|
{from: -1, limit: 0, expectErr: ErrNoChange},
|
|
{from: -1, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: -1, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 0, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 0, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 0, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 0, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 1, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(1, -1))},
|
|
{from: 1, limit: 0, expectErr: ErrNoChange},
|
|
{from: 1, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(1, -1))},
|
|
{from: 1, limit: 2, expectErr: ErrShortLimit{1}, expectMigrations: newMigSeq(M(1, -1))},
|
|
|
|
{from: 2, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 2, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 2, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 2, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 3, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(3, 1), M(1, -1))},
|
|
{from: 3, limit: 0, expectErr: ErrNoChange},
|
|
{from: 3, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(3, 1))},
|
|
{from: 3, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(3, 1), M(1, -1))},
|
|
|
|
{from: 4, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(4, 3), M(3, 1), M(1, -1))},
|
|
{from: 4, limit: 0, expectErr: ErrNoChange},
|
|
{from: 4, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(4, 3))},
|
|
{from: 4, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(4, 3), M(3, 1))},
|
|
|
|
{from: 5, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(5, 4), M(4, 3), M(3, 1), M(1, -1))},
|
|
{from: 5, limit: 0, expectErr: ErrNoChange},
|
|
{from: 5, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(5, 4))},
|
|
{from: 5, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(5, 4), M(4, 3))},
|
|
|
|
{from: 6, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 6, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 6, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 6, limit: 2, expectErr: os.ErrNotExist},
|
|
|
|
{from: 7, limit: -1, expectErr: nil, expectMigrations: newMigSeq(M(7, 5), M(5, 4), M(4, 3), M(3, 1), M(1, -1))},
|
|
{from: 7, limit: 0, expectErr: ErrNoChange},
|
|
{from: 7, limit: 1, expectErr: nil, expectMigrations: newMigSeq(M(7, 5))},
|
|
{from: 7, limit: 2, expectErr: nil, expectMigrations: newMigSeq(M(7, 5), M(5, 4))},
|
|
|
|
{from: 8, limit: -1, expectErr: os.ErrNotExist},
|
|
{from: 8, limit: 0, expectErr: os.ErrNotExist},
|
|
{from: 8, limit: 1, expectErr: os.ErrNotExist},
|
|
{from: 8, limit: 2, expectErr: os.ErrNotExist},
|
|
}
|
|
|
|
for i, v := range tt {
|
|
ret := make(chan interface{})
|
|
go m.readDown(v.from, v.limit, ret)
|
|
migrations, err := migrationsFromChannel(ret)
|
|
|
|
if (v.expectErr == os.ErrNotExist && !os.IsNotExist(err)) ||
|
|
(v.expectErr != os.ErrNotExist && v.expectErr != err) {
|
|
t.Errorf("expected %v, got %v, in %v", v.expectErr, err, i)
|
|
t.Logf("%v, in %v", migrations, i)
|
|
}
|
|
if len(v.expectMigrations) > 0 {
|
|
equalMigSeq(t, i, v.expectMigrations, migrations)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLock(t *testing.T) {
|
|
m, _ := New("stub://", "stub://")
|
|
if err := m.lock(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := m.lock(); err == nil {
|
|
t.Fatal("should be locked already")
|
|
}
|
|
}
|
|
|
|
func migrationsFromChannel(ret chan interface{}) ([]*Migration, error) {
|
|
slice := make([]*Migration, 0)
|
|
for r := range ret {
|
|
switch t := r.(type) {
|
|
case error:
|
|
return slice, t
|
|
|
|
case *Migration:
|
|
slice = append(slice, t)
|
|
}
|
|
}
|
|
return slice, nil
|
|
}
|
|
|
|
type migrationSequence []*Migration
|
|
|
|
func newMigSeq(migr ...*Migration) migrationSequence {
|
|
return migr
|
|
}
|
|
|
|
func (m *migrationSequence) add(migr ...*Migration) migrationSequence { // nolint:unused
|
|
*m = append(*m, migr...)
|
|
return *m
|
|
}
|
|
|
|
func (m *migrationSequence) bodySequence() []string {
|
|
r := make([]string, 0)
|
|
for _, v := range *m {
|
|
if v.Body != nil {
|
|
body, err := ioutil.ReadAll(v.Body)
|
|
if err != nil {
|
|
panic(err) // that should never happen
|
|
}
|
|
|
|
// reset body reader
|
|
// TODO: is there a better/nicer way?
|
|
v.Body = ioutil.NopCloser(bytes.NewReader(body))
|
|
|
|
r = append(r, string(body[:]))
|
|
} else {
|
|
r = append(r, "<empty>")
|
|
}
|
|
}
|
|
return r
|
|
}
|
|
|
|
// M is a convenience func to create a new *Migration
|
|
func M(version uint, targetVersion ...int) *Migration {
|
|
if len(targetVersion) > 1 {
|
|
panic("only one targetVersion allowed")
|
|
}
|
|
ts := int(version)
|
|
if len(targetVersion) == 1 {
|
|
ts = targetVersion[0]
|
|
}
|
|
|
|
m, _ := New("stub://", "stub://")
|
|
m.sourceDrv.(*sStub.Stub).Migrations = sourceStubMigrations
|
|
migr, err := m.newMigration(version, ts)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return migr
|
|
}
|
|
|
|
// mr is a convenience func to create a new *Migration from the raw database query
|
|
func mr(value string) *Migration {
|
|
return &Migration{
|
|
Body: ioutil.NopCloser(strings.NewReader(value)),
|
|
}
|
|
}
|
|
|
|
func equalMigSeq(t *testing.T, i int, expected, got migrationSequence) {
|
|
if len(expected) != len(got) {
|
|
t.Errorf("expected migrations %v, got %v, in %v", expected, got, i)
|
|
|
|
} else {
|
|
for ii := 0; ii < len(expected); ii++ {
|
|
if expected[ii].Version != got[ii].Version {
|
|
t.Errorf("expected version %v, got %v, in %v", expected[ii].Version, got[ii].Version, i)
|
|
}
|
|
|
|
if expected[ii].TargetVersion != got[ii].TargetVersion {
|
|
t.Errorf("expected targetVersion %v, got %v, in %v", expected[ii].TargetVersion, got[ii].TargetVersion, i)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func equalDbSeq(t *testing.T, i int, expected migrationSequence, got *dStub.Stub) {
|
|
bs := expected.bodySequence()
|
|
if !got.EqualSequence(bs) {
|
|
t.Fatalf("\nexpected sequence %v,\ngot %v, in %v", bs, got.MigrationSequence, i)
|
|
}
|
|
}
|