OSDN Git Service

feat: init cross_tx keepers (#146)
[bytom/vapor.git] / vendor / github.com / jinzhu / gorm / migration_test.go
diff --git a/vendor/github.com/jinzhu/gorm/migration_test.go b/vendor/github.com/jinzhu/gorm/migration_test.go
new file mode 100755 (executable)
index 0000000..3fb0664
--- /dev/null
@@ -0,0 +1,540 @@
+package gorm_test
+
+import (
+       "database/sql"
+       "database/sql/driver"
+       "errors"
+       "fmt"
+       "os"
+       "reflect"
+       "strconv"
+       "testing"
+       "time"
+
+       "github.com/jinzhu/gorm"
+)
+
+type User struct {
+       Id                int64
+       Age               int64
+       UserNum           Num
+       Name              string `sql:"size:255"`
+       Email             string
+       Birthday          *time.Time    // Time
+       CreatedAt         time.Time     // CreatedAt: Time of record is created, will be insert automatically
+       UpdatedAt         time.Time     // UpdatedAt: Time of record is updated, will be updated automatically
+       Emails            []Email       // Embedded structs
+       BillingAddress    Address       // Embedded struct
+       BillingAddressID  sql.NullInt64 // Embedded struct's foreign key
+       ShippingAddress   Address       // Embedded struct
+       ShippingAddressId int64         // Embedded struct's foreign key
+       CreditCard        CreditCard
+       Latitude          float64
+       Languages         []Language `gorm:"many2many:user_languages;"`
+       CompanyID         *int
+       Company           Company
+       Role              Role
+       Password          EncryptedData
+       PasswordHash      []byte
+       IgnoreMe          int64                 `sql:"-"`
+       IgnoreStringSlice []string              `sql:"-"`
+       Ignored           struct{ Name string } `sql:"-"`
+       IgnoredPointer    *User                 `sql:"-"`
+}
+
+type NotSoLongTableName struct {
+       Id                int64
+       ReallyLongThingID int64
+       ReallyLongThing   ReallyLongTableNameToTestMySQLNameLengthLimit
+}
+
+type ReallyLongTableNameToTestMySQLNameLengthLimit struct {
+       Id int64
+}
+
+type ReallyLongThingThatReferencesShort struct {
+       Id      int64
+       ShortID int64
+       Short   Short
+}
+
+type Short struct {
+       Id int64
+}
+
+type CreditCard struct {
+       ID        int8
+       Number    string
+       UserId    sql.NullInt64
+       CreatedAt time.Time `sql:"not null"`
+       UpdatedAt time.Time
+       DeletedAt *time.Time `sql:"column:deleted_time"`
+}
+
+type Email struct {
+       Id        int16
+       UserId    int
+       Email     string `sql:"type:varchar(100);"`
+       CreatedAt time.Time
+       UpdatedAt time.Time
+}
+
+type Address struct {
+       ID        int
+       Address1  string
+       Address2  string
+       Post      string
+       CreatedAt time.Time
+       UpdatedAt time.Time
+       DeletedAt *time.Time
+}
+
+type Language struct {
+       gorm.Model
+       Name  string
+       Users []User `gorm:"many2many:user_languages;"`
+}
+
+type Product struct {
+       Id                    int64
+       Code                  string
+       Price                 int64
+       CreatedAt             time.Time
+       UpdatedAt             time.Time
+       AfterFindCallTimes    int64
+       BeforeCreateCallTimes int64
+       AfterCreateCallTimes  int64
+       BeforeUpdateCallTimes int64
+       AfterUpdateCallTimes  int64
+       BeforeSaveCallTimes   int64
+       AfterSaveCallTimes    int64
+       BeforeDeleteCallTimes int64
+       AfterDeleteCallTimes  int64
+}
+
+type Company struct {
+       Id    int64
+       Name  string
+       Owner *User `sql:"-"`
+}
+
+type Place struct {
+       Id             int64
+       PlaceAddressID int
+       PlaceAddress   *Address `gorm:"save_associations:false"`
+       OwnerAddressID int
+       OwnerAddress   *Address `gorm:"save_associations:true"`
+}
+
+type EncryptedData []byte
+
+func (data *EncryptedData) Scan(value interface{}) error {
+       if b, ok := value.([]byte); ok {
+               if len(b) < 3 || b[0] != '*' || b[1] != '*' || b[2] != '*' {
+                       return errors.New("Too short")
+               }
+
+               *data = b[3:]
+               return nil
+       }
+
+       return errors.New("Bytes expected")
+}
+
+func (data EncryptedData) Value() (driver.Value, error) {
+       if len(data) > 0 && data[0] == 'x' {
+               //needed to test failures
+               return nil, errors.New("Should not start with 'x'")
+       }
+
+       //prepend asterisks
+       return append([]byte("***"), data...), nil
+}
+
+type Role struct {
+       Name string `gorm:"size:256"`
+}
+
+func (role *Role) Scan(value interface{}) error {
+       if b, ok := value.([]uint8); ok {
+               role.Name = string(b)
+       } else {
+               role.Name = value.(string)
+       }
+       return nil
+}
+
+func (role Role) Value() (driver.Value, error) {
+       return role.Name, nil
+}
+
+func (role Role) IsAdmin() bool {
+       return role.Name == "admin"
+}
+
+type Num int64
+
+func (i *Num) Scan(src interface{}) error {
+       switch s := src.(type) {
+       case []byte:
+               n, _ := strconv.Atoi(string(s))
+               *i = Num(n)
+       case int64:
+               *i = Num(s)
+       default:
+               return errors.New("Cannot scan NamedInt from " + reflect.ValueOf(src).String())
+       }
+       return nil
+}
+
+type Animal struct {
+       Counter    uint64    `gorm:"primary_key:yes"`
+       Name       string    `sql:"DEFAULT:'galeone'"`
+       From       string    //test reserved sql keyword as field name
+       Age        time.Time `sql:"DEFAULT:current_timestamp"`
+       unexported string    // unexported value
+       CreatedAt  time.Time
+       UpdatedAt  time.Time
+}
+
+type JoinTable struct {
+       From uint64
+       To   uint64
+       Time time.Time `sql:"default: null"`
+}
+
+type Post struct {
+       Id             int64
+       CategoryId     sql.NullInt64
+       MainCategoryId int64
+       Title          string
+       Body           string
+       Comments       []*Comment
+       Category       Category
+       MainCategory   Category
+}
+
+type Category struct {
+       gorm.Model
+       Name string
+
+       Categories []Category
+       CategoryID *uint
+}
+
+type Comment struct {
+       gorm.Model
+       PostId  int64
+       Content string
+       Post    Post
+}
+
+// Scanner
+type NullValue struct {
+       Id      int64
+       Name    sql.NullString  `sql:"not null"`
+       Gender  *sql.NullString `sql:"not null"`
+       Age     sql.NullInt64
+       Male    sql.NullBool
+       Height  sql.NullFloat64
+       AddedAt NullTime
+}
+
+type NullTime struct {
+       Time  time.Time
+       Valid bool
+}
+
+func (nt *NullTime) Scan(value interface{}) error {
+       if value == nil {
+               nt.Valid = false
+               return nil
+       }
+       nt.Time, nt.Valid = value.(time.Time), true
+       return nil
+}
+
+func (nt NullTime) Value() (driver.Value, error) {
+       if !nt.Valid {
+               return nil, nil
+       }
+       return nt.Time, nil
+}
+
+func getPreparedUser(name string, role string) *User {
+       var company Company
+       DB.Where(Company{Name: role}).FirstOrCreate(&company)
+
+       return &User{
+               Name:            name,
+               Age:             20,
+               Role:            Role{role},
+               BillingAddress:  Address{Address1: fmt.Sprintf("Billing Address %v", name)},
+               ShippingAddress: Address{Address1: fmt.Sprintf("Shipping Address %v", name)},
+               CreditCard:      CreditCard{Number: fmt.Sprintf("123456%v", name)},
+               Emails: []Email{
+                       {Email: fmt.Sprintf("user_%v@example1.com", name)}, {Email: fmt.Sprintf("user_%v@example2.com", name)},
+               },
+               Company: company,
+               Languages: []Language{
+                       {Name: fmt.Sprintf("lang_1_%v", name)},
+                       {Name: fmt.Sprintf("lang_2_%v", name)},
+               },
+       }
+}
+
+func runMigration() {
+       if err := DB.DropTableIfExists(&User{}).Error; err != nil {
+               fmt.Printf("Got error when try to delete table users, %+v\n", err)
+       }
+
+       for _, table := range []string{"animals", "user_languages"} {
+               DB.Exec(fmt.Sprintf("drop table %v;", table))
+       }
+
+       values := []interface{}{&Short{}, &ReallyLongThingThatReferencesShort{}, &ReallyLongTableNameToTestMySQLNameLengthLimit{}, &NotSoLongTableName{}, &Product{}, &Email{}, &Address{}, &CreditCard{}, &Company{}, &Role{}, &Language{}, &HNPost{}, &EngadgetPost{}, &Animal{}, &User{}, &JoinTable{}, &Post{}, &Category{}, &Comment{}, &Cat{}, &Dog{}, &Hamster{}, &Toy{}, &ElementWithIgnoredField{}, &Place{}}
+       for _, value := range values {
+               DB.DropTable(value)
+       }
+       if err := DB.AutoMigrate(values...).Error; err != nil {
+               panic(fmt.Sprintf("No error should happen when create table, but got %+v", err))
+       }
+}
+
+func TestIndexes(t *testing.T) {
+       if err := DB.Model(&Email{}).AddIndex("idx_email_email", "email").Error; err != nil {
+               t.Errorf("Got error when tried to create index: %+v", err)
+       }
+
+       scope := DB.NewScope(&Email{})
+       if !scope.Dialect().HasIndex(scope.TableName(), "idx_email_email") {
+               t.Errorf("Email should have index idx_email_email")
+       }
+
+       if err := DB.Model(&Email{}).RemoveIndex("idx_email_email").Error; err != nil {
+               t.Errorf("Got error when tried to remove index: %+v", err)
+       }
+
+       if scope.Dialect().HasIndex(scope.TableName(), "idx_email_email") {
+               t.Errorf("Email's index idx_email_email should be deleted")
+       }
+
+       if err := DB.Model(&Email{}).AddIndex("idx_email_email_and_user_id", "user_id", "email").Error; err != nil {
+               t.Errorf("Got error when tried to create index: %+v", err)
+       }
+
+       if !scope.Dialect().HasIndex(scope.TableName(), "idx_email_email_and_user_id") {
+               t.Errorf("Email should have index idx_email_email_and_user_id")
+       }
+
+       if err := DB.Model(&Email{}).RemoveIndex("idx_email_email_and_user_id").Error; err != nil {
+               t.Errorf("Got error when tried to remove index: %+v", err)
+       }
+
+       if scope.Dialect().HasIndex(scope.TableName(), "idx_email_email_and_user_id") {
+               t.Errorf("Email's index idx_email_email_and_user_id should be deleted")
+       }
+
+       if err := DB.Model(&Email{}).AddUniqueIndex("idx_email_email_and_user_id", "user_id", "email").Error; err != nil {
+               t.Errorf("Got error when tried to create index: %+v", err)
+       }
+
+       if !scope.Dialect().HasIndex(scope.TableName(), "idx_email_email_and_user_id") {
+               t.Errorf("Email should have index idx_email_email_and_user_id")
+       }
+
+       if DB.Save(&User{Name: "unique_indexes", Emails: []Email{{Email: "user1@example.comiii"}, {Email: "user1@example.com"}, {Email: "user1@example.com"}}}).Error == nil {
+               t.Errorf("Should get to create duplicate record when having unique index")
+       }
+
+       var user = User{Name: "sample_user"}
+       DB.Save(&user)
+       if DB.Model(&user).Association("Emails").Append(Email{Email: "not-1duplicated@gmail.com"}, Email{Email: "not-duplicated2@gmail.com"}).Error != nil {
+               t.Errorf("Should get no error when append two emails for user")
+       }
+
+       if DB.Model(&user).Association("Emails").Append(Email{Email: "duplicated@gmail.com"}, Email{Email: "duplicated@gmail.com"}).Error == nil {
+               t.Errorf("Should get no duplicated email error when insert duplicated emails for a user")
+       }
+
+       if err := DB.Model(&Email{}).RemoveIndex("idx_email_email_and_user_id").Error; err != nil {
+               t.Errorf("Got error when tried to remove index: %+v", err)
+       }
+
+       if scope.Dialect().HasIndex(scope.TableName(), "idx_email_email_and_user_id") {
+               t.Errorf("Email's index idx_email_email_and_user_id should be deleted")
+       }
+
+       if DB.Save(&User{Name: "unique_indexes", Emails: []Email{{Email: "user1@example.com"}, {Email: "user1@example.com"}}}).Error != nil {
+               t.Errorf("Should be able to create duplicated emails after remove unique index")
+       }
+}
+
+type EmailWithIdx struct {
+       Id           int64
+       UserId       int64
+       Email        string     `sql:"index:idx_email_agent"`
+       UserAgent    string     `sql:"index:idx_email_agent"`
+       RegisteredAt *time.Time `sql:"unique_index"`
+       CreatedAt    time.Time
+       UpdatedAt    time.Time
+}
+
+func TestAutoMigration(t *testing.T) {
+       DB.AutoMigrate(&Address{})
+       DB.DropTable(&EmailWithIdx{})
+       if err := DB.AutoMigrate(&EmailWithIdx{}).Error; err != nil {
+               t.Errorf("Auto Migrate should not raise any error")
+       }
+
+       now := time.Now()
+       DB.Save(&EmailWithIdx{Email: "jinzhu@example.org", UserAgent: "pc", RegisteredAt: &now})
+
+       scope := DB.NewScope(&EmailWithIdx{})
+       if !scope.Dialect().HasIndex(scope.TableName(), "idx_email_agent") {
+               t.Errorf("Failed to create index")
+       }
+
+       if !scope.Dialect().HasIndex(scope.TableName(), "uix_email_with_idxes_registered_at") {
+               t.Errorf("Failed to create index")
+       }
+
+       var bigemail EmailWithIdx
+       DB.First(&bigemail, "user_agent = ?", "pc")
+       if bigemail.Email != "jinzhu@example.org" || bigemail.UserAgent != "pc" || bigemail.RegisteredAt.IsZero() {
+               t.Error("Big Emails should be saved and fetched correctly")
+       }
+}
+
+func TestCreateAndAutomigrateTransaction(t *testing.T) {
+       tx := DB.Begin()
+
+       func() {
+               type Bar struct {
+                       ID uint
+               }
+               DB.DropTableIfExists(&Bar{})
+
+               if ok := DB.HasTable("bars"); ok {
+                       t.Errorf("Table should not exist, but does")
+               }
+
+               if ok := tx.HasTable("bars"); ok {
+                       t.Errorf("Table should not exist, but does")
+               }
+       }()
+
+       func() {
+               type Bar struct {
+                       Name string
+               }
+               err := tx.CreateTable(&Bar{}).Error
+
+               if err != nil {
+                       t.Errorf("Should have been able to create the table, but couldn't: %s", err)
+               }
+
+               if ok := tx.HasTable(&Bar{}); !ok {
+                       t.Errorf("The transaction should be able to see the table")
+               }
+       }()
+
+       func() {
+               type Bar struct {
+                       Stuff string
+               }
+
+               err := tx.AutoMigrate(&Bar{}).Error
+               if err != nil {
+                       t.Errorf("Should have been able to alter the table, but couldn't")
+               }
+       }()
+
+       tx.Rollback()
+}
+
+type MultipleIndexes struct {
+       ID     int64
+       UserID int64  `sql:"unique_index:uix_multipleindexes_user_name,uix_multipleindexes_user_email;index:idx_multipleindexes_user_other"`
+       Name   string `sql:"unique_index:uix_multipleindexes_user_name"`
+       Email  string `sql:"unique_index:,uix_multipleindexes_user_email"`
+       Other  string `sql:"index:,idx_multipleindexes_user_other"`
+}
+
+func TestMultipleIndexes(t *testing.T) {
+       if err := DB.DropTableIfExists(&MultipleIndexes{}).Error; err != nil {
+               fmt.Printf("Got error when try to delete table multiple_indexes, %+v\n", err)
+       }
+
+       DB.AutoMigrate(&MultipleIndexes{})
+       if err := DB.AutoMigrate(&EmailWithIdx{}).Error; err != nil {
+               t.Errorf("Auto Migrate should not raise any error")
+       }
+
+       DB.Save(&MultipleIndexes{UserID: 1, Name: "jinzhu", Email: "jinzhu@example.org", Other: "foo"})
+
+       scope := DB.NewScope(&MultipleIndexes{})
+       if !scope.Dialect().HasIndex(scope.TableName(), "uix_multipleindexes_user_name") {
+               t.Errorf("Failed to create index")
+       }
+
+       if !scope.Dialect().HasIndex(scope.TableName(), "uix_multipleindexes_user_email") {
+               t.Errorf("Failed to create index")
+       }
+
+       if !scope.Dialect().HasIndex(scope.TableName(), "uix_multiple_indexes_email") {
+               t.Errorf("Failed to create index")
+       }
+
+       if !scope.Dialect().HasIndex(scope.TableName(), "idx_multipleindexes_user_other") {
+               t.Errorf("Failed to create index")
+       }
+
+       if !scope.Dialect().HasIndex(scope.TableName(), "idx_multiple_indexes_other") {
+               t.Errorf("Failed to create index")
+       }
+
+       var mutipleIndexes MultipleIndexes
+       DB.First(&mutipleIndexes, "name = ?", "jinzhu")
+       if mutipleIndexes.Email != "jinzhu@example.org" || mutipleIndexes.Name != "jinzhu" {
+               t.Error("MutipleIndexes should be saved and fetched correctly")
+       }
+
+       // Check unique constraints
+       if err := DB.Save(&MultipleIndexes{UserID: 1, Name: "name1", Email: "jinzhu@example.org", Other: "foo"}).Error; err == nil {
+               t.Error("MultipleIndexes unique index failed")
+       }
+
+       if err := DB.Save(&MultipleIndexes{UserID: 1, Name: "name1", Email: "foo@example.org", Other: "foo"}).Error; err != nil {
+               t.Error("MultipleIndexes unique index failed")
+       }
+
+       if err := DB.Save(&MultipleIndexes{UserID: 2, Name: "name1", Email: "jinzhu@example.org", Other: "foo"}).Error; err == nil {
+               t.Error("MultipleIndexes unique index failed")
+       }
+
+       if err := DB.Save(&MultipleIndexes{UserID: 2, Name: "name1", Email: "foo2@example.org", Other: "foo"}).Error; err != nil {
+               t.Error("MultipleIndexes unique index failed")
+       }
+}
+
+func TestModifyColumnType(t *testing.T) {
+       if dialect := os.Getenv("GORM_DIALECT"); dialect != "postgres" && dialect != "mysql" && dialect != "mssql" {
+               t.Skip("Skipping this because only postgres, mysql and mssql support altering a column type")
+       }
+
+       type ModifyColumnType struct {
+               gorm.Model
+               Name1 string `gorm:"length:100"`
+               Name2 string `gorm:"length:200"`
+       }
+       DB.DropTable(&ModifyColumnType{})
+       DB.CreateTable(&ModifyColumnType{})
+
+       name2Field, _ := DB.NewScope(&ModifyColumnType{}).FieldByName("Name2")
+       name2Type := DB.Dialect().DataTypeOf(name2Field.StructField)
+
+       if err := DB.Model(&ModifyColumnType{}).ModifyColumn("name1", name2Type).Error; err != nil {
+               t.Errorf("No error should happen when ModifyColumn, but got %v", err)
+       }
+}