fix: validate explicit migrations and preserve large business ids
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user