package migrate import ( "bytes" "database/sql" "io/ioutil" "log" "os" "testing" dStub "github.com/mattes/migrate/database/stub" "github.com/mattes/migrate/source" sStub "github.com/mattes/migrate/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 func init() { sourceStubMigrations = source.NewMigrations() sourceStubMigrations.Append(&source.Migration{Version: 1, Direction: source.Up}) sourceStubMigrations.Append(&source.Migration{Version: 1, Direction: source.Down}) sourceStubMigrations.Append(&source.Migration{Version: 3, Direction: source.Up}) sourceStubMigrations.Append(&source.Migration{Version: 4, Direction: source.Up}) sourceStubMigrations.Append(&source.Migration{Version: 4, Direction: source.Down}) sourceStubMigrations.Append(&source.Migration{Version: 5, Direction: source.Down}) sourceStubMigrations.Append(&source.Migration{Version: 7, Direction: source.Up}) sourceStubMigrations.Append(&source.Migration{Version: 7, Direction: source.Down}) } type DummyInstance struct{ Name string } func TestNew(t *testing.T) { m, err := New("stub://", "stub://") if err != nil { t.Fatal(err) } if m.sourceName != "stub" { t.Errorf("expected stub, got %v", m.sourceName) } if m.sourceDrv == nil { t.Error("expected sourceDrv not to be nil") } if m.databaseName != "stub" { 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://", "stub", dbInst) if err != nil { t.Fatal(err) } if m.sourceName != "stub" { t.Errorf("expected stub, got %v", m.sourceName) } if m.sourceDrv == nil { t.Error("expected sourceDrv not to be nil") } if m.databaseName != "stub" { 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 db.Close() // Create driver instance from db. // Check each driver if it supports the WithInstance function. // `import "github.com/mattes/migrate/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("stub", sInst, "stub://") if err != nil { t.Fatal(err) } if m.sourceName != "stub" { t.Errorf("expected stub, got %v", m.sourceName) } if m.sourceDrv == nil { t.Error("expected sourceDrv not to be nil") } if m.databaseName != "stub" { 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/mattes/migrate/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("stub", 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("stub", sInst, "stub", dbInst) if err != nil { t.Fatal(err) } if m.sourceName != "stub" { t.Errorf("expected stub, got %v", m.sourceName) } if m.sourceDrv == nil { t.Error("expected sourceDrv not to be nil") } if m.databaseName != "stub" { 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) seq := newMigSeq() 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, expectErr: nil, expectVersion: 1, expectSeq: seq.add(M(1))}, {version: 2, expectErr: os.ErrNotExist}, {version: 3, expectErr: nil, expectVersion: 3, expectSeq: seq.add(M(3))}, {version: 4, expectErr: nil, expectVersion: 4, expectSeq: seq.add(M(4))}, {version: 5, expectErr: nil, expectVersion: 5, expectSeq: seq.add()}, // 5 has no up migration {version: 6, expectErr: os.ErrNotExist}, {version: 7, expectErr: nil, expectVersion: 7, expectSeq: seq.add(M(7))}, {version: 8, expectErr: os.ErrNotExist}, // migrate all the way Down in single steps {version: 6, expectErr: os.ErrNotExist}, {version: 5, expectErr: nil, expectVersion: 5, expectSeq: seq.add(M(7, 5))}, {version: 4, expectErr: nil, expectVersion: 4, expectSeq: seq.add(M(5, 4))}, {version: 3, expectErr: nil, expectVersion: 3, expectSeq: seq.add(M(4, 3))}, {version: 2, expectErr: os.ErrNotExist}, {version: 1, expectErr: nil, expectVersion: 1, expectSeq: seq.add()}, // 3 has no down migration {version: 0, expectErr: os.ErrNotExist}, // migrate all the way Up in one step {version: 7, expectErr: nil, expectVersion: 7, expectSeq: seq.add(M(3), M(4), M(7))}, // migrate all the way Down in one step {version: 1, expectErr: nil, expectVersion: 1, expectSeq: seq.add(M(7, 5), M(5, 4), M(4, 3), M(3, 1))}, // can't migrate the same version twice {version: 1, expectErr: ErrNoChange}, } 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) seq := newMigSeq() tt := []struct { n int expectErr error expectVersion int expectSeq migrationSequence }{ // step must be != 0 {n: 0, expectErr: ErrNoChange}, // can't go Down if ErrNilVersion {n: -1, expectErr: os.ErrNotExist}, // migrate all the way Up {n: 1, expectErr: nil, expectVersion: 1, expectSeq: seq.add(M(1))}, {n: 1, expectErr: nil, expectVersion: 3, expectSeq: seq.add(M(3))}, {n: 1, expectErr: nil, expectVersion: 4, expectSeq: seq.add(M(4))}, {n: 1, expectErr: nil, expectVersion: 5, expectSeq: seq.add()}, {n: 1, expectErr: nil, expectVersion: 7, expectSeq: seq.add(M(7))}, {n: 1, expectErr: os.ErrNotExist}, // migrate all the way Down {n: -1, expectErr: nil, expectVersion: 5, expectSeq: seq.add(M(7, 5))}, {n: -1, expectErr: nil, expectVersion: 4, expectSeq: seq.add(M(5, 4))}, {n: -1, expectErr: nil, expectVersion: 3, expectSeq: seq.add(M(4, 3))}, {n: -1, expectErr: nil, expectVersion: 1, expectSeq: seq.add(M(3, 1))}, {n: -1, expectErr: nil, expectVersion: -1, expectSeq: seq.add(M(1, -1))}, // migrate Up in bigger step {n: 4, expectErr: nil, expectVersion: 5, expectSeq: seq.add(M(1), M(3), M(4), M(5))}, // apply one migration, then reaches out of boundary {n: 2, expectErr: ErrShortLimit{1}, expectVersion: 7, expectSeq: seq.add(M(7))}, // migrate Down in bigger step {n: -4, expectErr: nil, expectVersion: 1, expectSeq: seq.add(M(7, 5), M(5, 4), M(4, 3), M(3, 1))}, // apply one migration, then reaches out of boundary {n: -2, expectErr: ErrShortLimit{1}, expectVersion: -1, expectSeq: seq.add(M(1, -1))}, } for i, v := range tt { err := m.Steps(v.n) 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) seq := newMigSeq() // go Up first if err := m.Up(); err != nil { t.Fatal(err) } equalDbSeq(t, 0, seq.add(M(1), M(3), M(4), M(5), M(7)), dbDrv) // go Down if err := m.Down(); err != nil { t.Fatal(err) } equalDbSeq(t, 1, seq.add(M(7, 5), M(5, 4), M(4, 3), M(3, 1), M(1, -1)), dbDrv) // go 1 Up and then all the way Up if err := m.Steps(1); err != nil { t.Fatal(err) } if err := m.Up(); err != nil { t.Fatal(err) } equalDbSeq(t, 2, seq.add(M(1), M(3), M(4), M(5), M(7)), dbDrv) // go 1 Down and then all the way Down if err := m.Steps(-1); err != nil { t.Fatal(err) } if err := m.Down(); err != nil { t.Fatal(err) } equalDbSeq(t, 0, seq.add(M(7, 5), M(5, 4), M(4, 3), M(3, 1), M(1, -1)), 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 r.(type) { case error: return slice, r.(error) case *Migration: slice = append(slice, r.(*Migration)) } } return slice, nil } type migrationSequence []*Migration func newMigSeq(migr ...*Migration) migrationSequence { return migr } func (m *migrationSequence) add(migr ...*Migration) migrationSequence { *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[:])) } } 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 } 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) } }