package schema_test import ( "database/sql" "reflect" "sync" "testing" "time" "gorm.io/gorm" "gorm.io/gorm/schema" "gorm.io/gorm/utils/tests" ) func TestFieldValuerAndSetter(t *testing.T) { var ( userSchema, _ = schema.Parse(&tests.User{}, &sync.Map{}, schema.NamingStrategy{}) user = tests.User{ Model: gorm.Model{ ID: 10, CreatedAt: time.Now(), UpdatedAt: time.Now(), DeletedAt: gorm.DeletedAt{Time: time.Now(), Valid: true}, }, Name: "valuer_and_setter", Age: 18, Birthday: tests.Now(), Active: true, } reflectValue = reflect.ValueOf(&user) ) // test valuer values := map[string]interface{}{ "name": user.Name, "id": user.ID, "created_at": user.CreatedAt, "updated_at": user.UpdatedAt, "deleted_at": user.DeletedAt, "age": user.Age, "birthday": user.Birthday, "active": true, } checkField(t, userSchema, reflectValue, values) var f *bool // test setter newValues := map[string]interface{}{ "name": "valuer_and_setter_2", "id": 2, "created_at": time.Now(), "updated_at": nil, "deleted_at": time.Now(), "age": 20, "birthday": time.Now(), "active": f, } for k, v := range newValues { if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) } } newValues["updated_at"] = time.Time{} newValues["active"] = false checkField(t, userSchema, reflectValue, newValues) // test valuer and other type age := myint(10) var nilTime *time.Time newValues2 := map[string]interface{}{ "name": sql.NullString{String: "valuer_and_setter_3", Valid: true}, "id": &sql.NullInt64{Int64: 3, Valid: true}, "created_at": tests.Now(), "updated_at": nilTime, "deleted_at": time.Now(), "age": &age, "birthday": mytime(time.Now()), "active": mybool(true), } for k, v := range newValues2 { if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) } } newValues2["updated_at"] = time.Time{} checkField(t, userSchema, reflectValue, newValues2) } func TestPointerFieldValuerAndSetter(t *testing.T) { var ( userSchema, _ = schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{}) name = "pointer_field_valuer_and_setter" age uint = 18 active = true user = User{ Model: &gorm.Model{ ID: 10, CreatedAt: time.Now(), DeletedAt: gorm.DeletedAt{Time: time.Now(), Valid: true}, }, Name: &name, Age: &age, Birthday: tests.Now(), Active: &active, } reflectValue = reflect.ValueOf(&user) ) // test valuer values := map[string]interface{}{ "name": user.Name, "id": user.ID, "created_at": user.CreatedAt, "deleted_at": user.DeletedAt, "age": user.Age, "birthday": user.Birthday, "active": true, } checkField(t, userSchema, reflectValue, values) // test setter newValues := map[string]interface{}{ "name": "valuer_and_setter_2", "id": 2, "created_at": time.Now(), "deleted_at": time.Now(), "age": 20, "birthday": time.Now(), "active": false, } for k, v := range newValues { if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) } } checkField(t, userSchema, reflectValue, newValues) // test valuer and other type age2 := myint(10) newValues2 := map[string]interface{}{ "name": sql.NullString{String: "valuer_and_setter_3", Valid: true}, "id": &sql.NullInt64{Int64: 3, Valid: true}, "created_at": tests.Now(), "deleted_at": time.Now(), "age": &age2, "birthday": mytime(time.Now()), "active": mybool(true), } for k, v := range newValues2 { if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) } } checkField(t, userSchema, reflectValue, newValues2) } func TestAdvancedDataTypeValuerAndSetter(t *testing.T) { var ( userSchema, _ = schema.Parse(&AdvancedDataTypeUser{}, &sync.Map{}, schema.NamingStrategy{}) name = "advanced_data_type_valuer_and_setter" deletedAt = mytime(time.Now()) isAdmin = mybool(false) user = AdvancedDataTypeUser{ ID: sql.NullInt64{Int64: 10, Valid: true}, Name: &sql.NullString{String: name, Valid: true}, Birthday: sql.NullTime{Time: time.Now(), Valid: true}, RegisteredAt: mytime(time.Now()), DeletedAt: &deletedAt, Active: mybool(true), Admin: &isAdmin, } reflectValue = reflect.ValueOf(&user) ) // test valuer values := map[string]interface{}{ "id": user.ID, "name": user.Name, "birthday": user.Birthday, "registered_at": user.RegisteredAt, "deleted_at": user.DeletedAt, "active": user.Active, "admin": user.Admin, } checkField(t, userSchema, reflectValue, values) // test setter newDeletedAt := mytime(time.Now()) newIsAdmin := mybool(true) newValues := map[string]interface{}{ "id": sql.NullInt64{Int64: 1, Valid: true}, "name": &sql.NullString{String: name + "rename", Valid: true}, "birthday": time.Now(), "registered_at": mytime(time.Now()), "deleted_at": &newDeletedAt, "active": mybool(false), "admin": &newIsAdmin, } for k, v := range newValues { if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) } } checkField(t, userSchema, reflectValue, newValues) newValues2 := map[string]interface{}{ "id": 5, "name": name + "rename2", "birthday": time.Now(), "registered_at": time.Now(), "deleted_at": time.Now(), "active": true, "admin": false, } for k, v := range newValues2 { if err := userSchema.FieldsByDBName[k].Set(reflectValue, v); err != nil { t.Errorf("no error should happen when assign value to field %v, but got %v", k, err) } } checkField(t, userSchema, reflectValue, newValues2) } type UserWithPermissionControl struct { ID uint Name string `gorm:"-"` Name2 string `gorm:"->"` Name3 string `gorm:"<-"` Name4 string `gorm:"<-:create"` Name5 string `gorm:"<-:update"` Name6 string `gorm:"<-:create,update"` Name7 string `gorm:"->:false;<-:create,update"` Name8 string `gorm:"->;-:migration"` } func TestParseFieldWithPermission(t *testing.T) { user, err := schema.Parse(&UserWithPermissionControl{}, &sync.Map{}, schema.NamingStrategy{}) if err != nil { t.Fatalf("Failed to parse user with permission, got error %v", err) } fields := []*schema.Field{ {Name: "ID", DBName: "id", BindNames: []string{"ID"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, Creatable: true, Updatable: true, Readable: true, HasDefaultValue: true, AutoIncrement: true}, {Name: "Name", DBName: "", BindNames: []string{"Name"}, DataType: "", Tag: `gorm:"-"`, Creatable: false, Updatable: false, Readable: false}, {Name: "Name2", DBName: "name2", BindNames: []string{"Name2"}, DataType: schema.String, Tag: `gorm:"->"`, Creatable: false, Updatable: false, Readable: true}, {Name: "Name3", DBName: "name3", BindNames: []string{"Name3"}, DataType: schema.String, Tag: `gorm:"<-"`, Creatable: true, Updatable: true, Readable: true}, {Name: "Name4", DBName: "name4", BindNames: []string{"Name4"}, DataType: schema.String, Tag: `gorm:"<-:create"`, Creatable: true, Updatable: false, Readable: true}, {Name: "Name5", DBName: "name5", BindNames: []string{"Name5"}, DataType: schema.String, Tag: `gorm:"<-:update"`, Creatable: false, Updatable: true, Readable: true}, {Name: "Name6", DBName: "name6", BindNames: []string{"Name6"}, DataType: schema.String, Tag: `gorm:"<-:create,update"`, Creatable: true, Updatable: true, Readable: true}, {Name: "Name7", DBName: "name7", BindNames: []string{"Name7"}, DataType: schema.String, Tag: `gorm:"->:false;<-:create,update"`, Creatable: true, Updatable: true, Readable: false}, {Name: "Name8", DBName: "name8", BindNames: []string{"Name8"}, DataType: schema.String, Tag: `gorm:"->;-:migration"`, Creatable: false, Updatable: false, Readable: true, IgnoreMigration: true}, } for _, f := range fields { checkSchemaField(t, user, f, func(f *schema.Field) {}) } } type ID int64 type INT int type INT8 int8 type INT16 int16 type INT32 int32 type INT64 int64 type UINT uint type UINT8 uint8 type UINT16 uint16 type UINT32 uint32 type UINT64 uint64 type FLOAT32 float32 type FLOAT64 float64 type BOOL bool type STRING string type TypeAlias struct { ID INT `gorm:"column:fint"` INT8 `gorm:"column:fint8"` INT16 `gorm:"column:fint16"` INT32 `gorm:"column:fint32"` INT64 `gorm:"column:fint64"` UINT `gorm:"column:fuint"` UINT8 `gorm:"column:fuint8"` UINT16 `gorm:"column:fuint16"` UINT32 `gorm:"column:fuint32"` UINT64 `gorm:"column:fuint64"` FLOAT32 `gorm:"column:ffloat32"` FLOAT64 `gorm:"column:ffloat64"` BOOL `gorm:"column:fbool"` STRING `gorm:"column:fstring"` } func TestTypeAliasField(t *testing.T){ alias, err := schema.Parse(&TypeAlias{}, &sync.Map{}, schema.NamingStrategy{}) if err != nil { t.Fatalf("Failed to parse TypeAlias with permission, got error %v", err) } fields := []*schema.Field{ {Name: "ID", DBName: "id", BindNames: []string{"ID"}, DataType: schema.Int , Creatable: true, Updatable: true, Readable: true, Size: 64, PrimaryKey: true, HasDefaultValue: true, AutoIncrement: true }, {Name: "INT", DBName: "fint", BindNames: []string{"INT"}, DataType: schema.Int , Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:"column:fint"`}, {Name: "INT8", DBName: "fint8", BindNames: []string{"INT8"}, DataType: schema.Int , Creatable: true, Updatable: true, Readable: true, Size: 8, Tag: `gorm:"column:fint8"`}, {Name: "INT16", DBName: "fint16", BindNames: []string{"INT16"}, DataType: schema.Int , Creatable: true, Updatable: true, Readable: true, Size: 16, Tag: `gorm:"column:fint16"`}, {Name: "INT32", DBName: "fint32", BindNames: []string{"INT32"}, DataType: schema.Int , Creatable: true, Updatable: true, Readable: true, Size: 32, Tag: `gorm:"column:fint32"`}, {Name: "INT64", DBName: "fint64", BindNames: []string{"INT64"}, DataType: schema.Int , Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:"column:fint64"`}, {Name: "UINT", DBName: "fuint", BindNames: []string{"UINT"}, DataType: schema.Uint , Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:"column:fuint"`}, {Name: "UINT8", DBName: "fuint8", BindNames: []string{"UINT8"}, DataType: schema.Uint , Creatable: true, Updatable: true, Readable: true, Size: 8, Tag: `gorm:"column:fuint8"`}, {Name: "UINT16", DBName: "fuint16", BindNames: []string{"UINT16"}, DataType: schema.Uint , Creatable: true, Updatable: true, Readable: true, Size: 16, Tag: `gorm:"column:fuint16"`}, {Name: "UINT32", DBName: "fuint32", BindNames: []string{"UINT32"}, DataType: schema.Uint , Creatable: true, Updatable: true, Readable: true, Size: 32, Tag: `gorm:"column:fuint32"`}, {Name: "UINT64", DBName: "fuint64", BindNames: []string{"UINT64"}, DataType: schema.Uint , Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:"column:fuint64"`}, {Name: "FLOAT32", DBName: "ffloat32", BindNames: []string{"FLOAT32"}, DataType: schema.Float , Creatable: true, Updatable: true, Readable: true, Size: 32, Tag: `gorm:"column:ffloat32"`}, {Name: "FLOAT64", DBName: "ffloat64", BindNames: []string{"FLOAT64"}, DataType: schema.Float , Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:"column:ffloat64"`}, {Name: "BOOL", DBName: "fbool", BindNames: []string{"BOOL"}, DataType: schema.Bool , Creatable: true, Updatable: true, Readable: true, Tag: `gorm:"column:fbool"`}, {Name: "STRING", DBName: "fstring", BindNames: []string{"STRING"}, DataType: schema.String, Creatable: true, Updatable: true, Readable: true, Tag: `gorm:"column:fstring"`}, } for _, f := range fields { checkSchemaField(t, alias, f, func(f *schema.Field) {}) } }