From 387fca3478e0cddfc840cf6bf8c6cea32a05bc48 Mon Sep 17 00:00:00 2001 From: black Date: Thu, 8 Jun 2023 19:24:06 +0800 Subject: [PATCH] add test --- tests/go.mod | 1 + tests/migrate_test.go | 161 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/tests/go.mod b/tests/go.mod index f47d175f..5c0b142f 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -8,6 +8,7 @@ require ( github.com/jinzhu/now v1.1.5 github.com/lib/pq v1.10.8 github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/stretchr/testify v1.8.1 golang.org/x/crypto v0.8.0 // indirect gorm.io/driver/mysql v1.5.0 gorm.io/driver/postgres v1.5.0 diff --git a/tests/migrate_test.go b/tests/migrate_test.go index 69f86412..5241b1c9 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -2,6 +2,7 @@ package tests_test import ( "context" + "database/sql" "fmt" "math/rand" "os" @@ -10,10 +11,14 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "gorm.io/driver/postgres" + "gorm.io/gorm" "gorm.io/gorm/logger" + "gorm.io/gorm/migrator" "gorm.io/gorm/schema" + "gorm.io/gorm/utils" . "gorm.io/gorm/utils/tests" ) @@ -1598,3 +1603,159 @@ func TestMigrateExistingBoolColumnPG(t *testing.T) { } } } + +func TestMigrateWithUniqueIndexAndUnique(t *testing.T) { + const table = "unique_struct" + + checkColumnType := func(t *testing.T, fieldName string, unique bool) { + columnTypes, err := DB.Debug().Migrator().ColumnTypes(table) + if err != nil { + t.Fatalf("%v: failed to get column types, got error: %v", utils.FileWithLineNum(), err) + } + var found gorm.ColumnType + for _, columnType := range columnTypes { + if columnType.Name() == fieldName { + found = columnType + } + } + if found == nil { + t.Fatalf("%v: failed to find column type %q", utils.FileWithLineNum(), fieldName) + } + if actualUnique, ok := found.Unique(); !ok || actualUnique != unique { + t.Fatalf("%v: column %q unique should be %v but got %v", utils.FileWithLineNum(), fieldName, unique, actualUnique) + } + } + + checkIndex := func(t *testing.T, expected []gorm.Index) { + indexes, err := DB.Migrator().GetIndexes(table) + if err != nil { + t.Fatalf("%v: failed to get indexes, got error: %v", utils.FileWithLineNum(), err) + } + assert.ElementsMatch(t, expected, indexes) + } + + checkField := func(model interface{}, fieldName string, unique, uniqueIndex bool) { + stmt := &gorm.Statement{DB: DB} + err := stmt.Parse(model) + if err != nil { + t.Fatalf("%v: failed to parse schema, got error: %v", utils.FileWithLineNum(), err) + } + _ = stmt.Schema.ParseIndexes() + field := stmt.Schema.LookUpField(fieldName) + if field == nil { + t.Fatalf("%v: failed to find column %q", utils.FileWithLineNum(), fieldName) + } + if field.Unique != unique { + t.Fatalf("%v: %q column %q unique should be %v but got %v", utils.FileWithLineNum(), stmt.Schema.Table, fieldName, unique, field.Unique) + } + if field.UniqueIndex != uniqueIndex { + t.Fatalf("%v: %q column %q uniqueIndex should be %v but got %v", utils.FileWithLineNum(), stmt.Schema, fieldName, uniqueIndex, field.UniqueIndex) + } + } + + // not unique + type UniqueStruct1 struct { + Name string `gorm:"size:20"` + } + checkField(&UniqueStruct1{}, "name", false, false) + + type ( // unique + UniqueStruct2 struct { + Name string `gorm:"size:20;unique"` + } + UniqueStruct3 struct { + Name string `gorm:"size:30;unique"` + } + ) + checkField(&UniqueStruct2{}, "name", true, false) + checkField(&UniqueStruct3{}, "name", true, false) + + type ( // uniqueIndex + UniqueStruct4 struct { + Name string `gorm:"size:40;uniqueIndex"` + } + UniqueStruct5 struct { + Name string `gorm:"size:50;uniqueIndex"` + } + UniqueStruct6 struct { + Name string `gorm:"size:20;uniqueIndex:idx_us6_all_names"` + NickName string `gorm:"size:20;uniqueIndex:idx_us6_all_names"` + } + ) + checkField(&UniqueStruct4{}, "name", false, true) + checkField(&UniqueStruct5{}, "name", false, true) + + checkField(&UniqueStruct6{}, "name", false, false) + checkField(&UniqueStruct6{}, "nick_name", false, false) + checkField(&UniqueStruct6{}, "nick_name", false, false) + + type TestCase struct { + name string + from, to interface{} + checkFunc func(t *testing.T) + } + + checkNotUnique := func(t *testing.T) { + checkColumnType(t, "name", false) + checkIndex(t, nil) + } + checkUnique := func(t *testing.T) { + checkColumnType(t, "name", true) + checkIndex(t, nil) + } + index := &migrator.Index{ + TableName: table, + NameValue: DB.Config.NamingStrategy.IndexName(table, "name"), + ColumnList: []string{"name"}, + PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, + UniqueValue: sql.NullBool{Bool: true, Valid: true}, + } + if DB.Dialector.Name() != "mysql" { + // in mysql, unique equals uniqueIndex + checkUnique = func(t *testing.T) { + checkColumnType(t, "name", true) + checkIndex(t, []gorm.Index{index}) + } + } + checkUniqueIndex := func(t *testing.T) { + checkColumnType(t, "name", false) + checkIndex(t, []gorm.Index{index}) + } + + tests := []TestCase{ + {name: "notUnique to notUnique", from: &UniqueStruct1{}, to: &UniqueStruct1{}, checkFunc: checkNotUnique}, + {name: "notUnique to unique", from: &UniqueStruct1{}, to: &UniqueStruct2{}, checkFunc: checkUnique}, + {name: "notUnique to uniqueIndex", from: &UniqueStruct1{}, to: &UniqueStruct4{}, checkFunc: checkUniqueIndex}, + {name: "unique to notUnique", from: &UniqueStruct2{}, to: &UniqueStruct1{}, checkFunc: checkNotUnique}, + {name: "unique to unique", from: &UniqueStruct2{}, to: &UniqueStruct3{}, checkFunc: checkUnique}, + {name: "unique to uniqueIndex", from: &UniqueStruct2{}, to: &UniqueStruct4{}, checkFunc: checkUniqueIndex}, + {name: "uniqueIndex to notUnique", from: &UniqueStruct4{}, to: &UniqueStruct2{}, checkFunc: checkNotUnique}, + {name: "uniqueIndex to unique", from: &UniqueStruct4{}, to: &UniqueStruct2{}, checkFunc: checkUnique}, + {name: "uniqueIndex to uniqueIndex", from: &UniqueStruct4{}, to: &UniqueStruct5{}, checkFunc: checkUniqueIndex}, + {name: "uniqueIndex to multi uniqueIndex", from: &UniqueStruct4{}, to: &UniqueStruct6{}, checkFunc: func(t *testing.T) { + checkColumnType(t, "name", false) + checkColumnType(t, "nick_name", false) + checkIndex(t, []gorm.Index{&migrator.Index{ + TableName: table, + NameValue: "idx_us6_all_names", + ColumnList: []string{"name", "nick_name"}, + PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, + UniqueValue: sql.NullBool{Bool: true, Valid: true}, + }}) + }}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if err := DB.Migrator().DropTable(table); err != nil { + t.Fatalf("failed to drop table, got error: %v", err) + } + if err := DB.Debug().Table(table).AutoMigrate(test.from); err != nil { + t.Fatalf("failed to migrate table, got error: %v", err) + } + if err := DB.Debug().Table(table).AutoMigrate(test.to); err != nil { + t.Fatalf("failed to migrate table, got error: %v", err) + } + test.checkFunc(t) + }) + } +}