Files
kra-new/pkg/database/migration/check_test.go
T

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")
}
}