110 lines
3.5 KiB
Go
110 lines
3.5 KiB
Go
package migration
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/glebarez/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type checkedSchema struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
Owner uint `gorm:"index:idx_checked_owner_state,priority:1"`
|
|
State int `gorm:"index:idx_checked_owner_state,priority:2"`
|
|
Code string `gorm:"uniqueIndex"`
|
|
}
|
|
|
|
func TestSchemaCheckRejectsMissingColumnsAndIncorrectIndexes(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := CheckModels(db, &checkedSchema{}); err == nil {
|
|
t.Fatal("missing table passed")
|
|
}
|
|
if err := db.Migrator().CreateTable(&checkedSchema{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := CheckModels(db, &checkedSchema{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, statement := range []string{
|
|
"DROP INDEX idx_checked_owner_state",
|
|
"CREATE INDEX idx_checked_owner_state ON checked_schemas(state,owner)",
|
|
} {
|
|
if err := db.Exec(statement).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := CheckModels(db, &checkedSchema{}); err == nil || !strings.Contains(err.Error(), "idx_checked_owner_state") {
|
|
t.Fatalf("reversed index passed: %v", err)
|
|
}
|
|
if err := db.Exec("CREATE INDEX legacy_owner_state ON checked_schemas(owner,state,id)").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := CheckModels(db, &checkedSchema{}); err != nil {
|
|
t.Fatalf("equivalent covering index rejected: %v", err)
|
|
}
|
|
if err := db.Exec("DROP INDEX idx_checked_schemas_code").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Exec("CREATE INDEX idx_checked_schemas_code ON checked_schemas(code)").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := CheckModels(db, &checkedSchema{}); err == nil {
|
|
t.Fatal("non-unique replacement passed")
|
|
}
|
|
if err := db.Migrator().DropColumn(&checkedSchema{}, "Owner"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := CheckModels(db, &checkedSchema{}); err == nil || !strings.Contains(err.Error(), "missing column") {
|
|
t.Fatalf("missing column passed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckAppliedDoesNotCreateOrApplyMigrations(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
called := false
|
|
steps := []Step{{ID: "required", Migrate: func(*gorm.DB) error { called = true; return nil }}}
|
|
if err := CheckApplied(db, steps); err == nil {
|
|
t.Fatal("missing ledger passed")
|
|
}
|
|
if db.Migrator().HasTable(TableName) || called {
|
|
t.Fatal("read-only check mutated the database")
|
|
}
|
|
if err := db.Migrator().CreateTable(&record{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := CheckApplied(db, steps); err == nil || !strings.Contains(err.Error(), "required") {
|
|
t.Fatalf("pending migration passed: %v", err)
|
|
}
|
|
if err := db.Create(&record{ID: "required"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// openWithDriverConfig returns a settings-bearing handle for MySQL's
|
|
// table options. Read-only checks must not retain their Model on it.
|
|
configured := db.Set("gorm:table_options", "ENGINE=InnoDB")
|
|
if err := CheckApplied(configured, steps); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if configured.Statement.Model != nil {
|
|
t.Fatal("schema check leaked the migration model into the shared handle")
|
|
}
|
|
if err := CheckApplied(db, steps); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&record{ID: "unknown"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := CheckApplied(db, steps); err == nil {
|
|
t.Fatal("unknown version passed")
|
|
}
|
|
if called {
|
|
t.Fatal("read-only check ran a migration")
|
|
}
|
|
}
|