Add tests model definition and basic fields tests

This commit is contained in:
Jinzhu 2020-02-01 20:18:25 +08:00
parent fd9b688084
commit 14724ddeae
7 changed files with 150 additions and 10 deletions

View File

@ -22,7 +22,7 @@ var (
// gorm.Model // gorm.Model
// } // }
type Model struct { type Model struct {
ID uint `gorm:"primary_key"` ID uint `gorm:"primarykey"`
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
DeletedAt *time.Time `gorm:"index"` DeletedAt *time.Time `gorm:"index"`

View File

@ -54,7 +54,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
Creatable: true, Creatable: true,
Updatable: true, Updatable: true,
Tag: fieldStruct.Tag, Tag: fieldStruct.Tag,
TagSettings: parseTagSetting(fieldStruct.Tag), TagSettings: ParseTagSetting(fieldStruct.Tag),
} }
for field.FieldType.Kind() == reflect.Ptr { for field.FieldType.Kind() == reflect.Ptr {
@ -84,7 +84,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
} }
// copy tag settings from valuer // copy tag settings from valuer
for key, value := range parseTagSetting(field.FieldType.Field(i).Tag) { for key, value := range ParseTagSetting(field.FieldType.Field(i).Tag) {
if _, ok := field.TagSettings[key]; !ok { if _, ok := field.TagSettings[key]; !ok {
field.TagSettings[key] = value field.TagSettings[key] = value
} }
@ -141,7 +141,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
field.DBDataType = val field.DBDataType = val
} }
switch fieldValue.Kind() { switch fieldValue.Elem().Kind() {
case reflect.Bool: case reflect.Bool:
field.DataType = Bool field.DataType = Bool
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@ -153,7 +153,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
case reflect.String: case reflect.String:
field.DataType = String field.DataType = String
case reflect.Struct: case reflect.Struct:
if _, ok := fieldValue.Interface().(time.Time); ok { if _, ok := fieldValue.Interface().(*time.Time); ok {
field.DataType = Time field.DataType = Time
} }
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
@ -176,7 +176,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
} }
if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous { if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
field.EmbeddedSchema, schema.err = Parse(fieldValue, sync.Map{}, schema.namer) field.EmbeddedSchema, schema.err = Parse(fieldValue.Interface(), &sync.Map{}, schema.namer)
for _, ef := range field.EmbeddedSchema.Fields { for _, ef := range field.EmbeddedSchema.Fields {
ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...) ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...)

View File

@ -6,6 +6,8 @@ import (
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"github.com/jinzhu/gorm/logger"
) )
type Schema struct { type Schema struct {
@ -20,7 +22,7 @@ type Schema struct {
Relationships Relationships Relationships Relationships
err error err error
namer Namer namer Namer
cacheStore sync.Map cacheStore *sync.Map
} }
func (schema Schema) String() string { func (schema Schema) String() string {
@ -38,7 +40,7 @@ func (schema Schema) LookUpField(name string) *Field {
} }
// get data type from dialector // get data type from dialector
func Parse(dest interface{}, cacheStore sync.Map, namer Namer) (*Schema, error) { func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error) {
modelType := reflect.ValueOf(dest).Type() modelType := reflect.ValueOf(dest).Type()
for modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Ptr { for modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Ptr {
modelType = modelType.Elem() modelType = modelType.Elem()
@ -62,10 +64,12 @@ func Parse(dest interface{}, cacheStore sync.Map, namer Namer) (*Schema, error)
FieldsByName: map[string]*Field{}, FieldsByName: map[string]*Field{},
FieldsByDBName: map[string]*Field{}, FieldsByDBName: map[string]*Field{},
cacheStore: cacheStore, cacheStore: cacheStore,
namer: namer,
} }
defer func() { defer func() {
if schema.err != nil { if schema.err != nil {
logger.Default.Error(schema.err.Error())
cacheStore.Delete(modelType) cacheStore.Delete(modelType)
} }
}() }()

78
schema/schema_test.go Normal file
View File

@ -0,0 +1,78 @@
package schema_test
import (
"reflect"
"sync"
"testing"
"github.com/jinzhu/gorm/schema"
"github.com/jinzhu/gorm/tests"
)
func TestParseSchema(t *testing.T) {
cacheMap := sync.Map{}
user, err := schema.Parse(&tests.User{}, &cacheMap, schema.NamingStrategy{})
if err != nil {
t.Fatalf("failed to parse user, got error %v", err)
}
checkSchemaFields(t, user)
}
func checkSchemaFields(t *testing.T, s *schema.Schema) {
fields := []schema.Field{
schema.Field{
Name: "ID", DBName: "id", BindNames: []string{"Model", "ID"}, DataType: schema.Uint,
PrimaryKey: true, Tag: `gorm:"primarykey"`, TagSettings: map[string]string{"PRIMARYKEY": "PRIMARYKEY"},
},
schema.Field{Name: "CreatedAt", DBName: "created_at", BindNames: []string{"Model", "CreatedAt"}, DataType: schema.Time},
schema.Field{Name: "UpdatedAt", DBName: "updated_at", BindNames: []string{"Model", "UpdatedAt"}, DataType: schema.Time},
schema.Field{Name: "DeletedAt", DBName: "deleted_at", BindNames: []string{"Model", "DeletedAt"}, Tag: `gorm:"index"`, DataType: schema.Time},
schema.Field{Name: "Name", DBName: "name", BindNames: []string{"Name"}, DataType: schema.String},
schema.Field{Name: "Age", DBName: "age", BindNames: []string{"Age"}, DataType: schema.Uint},
schema.Field{Name: "Birthday", DBName: "birthday", BindNames: []string{"Birthday"}, DataType: schema.Time},
schema.Field{Name: "CompanyID", DBName: "company_id", BindNames: []string{"CompanyID"}, DataType: schema.Int},
schema.Field{Name: "ManagerID", DBName: "manager_id", BindNames: []string{"ManagerID"}, DataType: schema.Uint},
}
for _, f := range fields {
f.Creatable = true
f.Updatable = true
if f.TagSettings == nil {
if f.Tag != "" {
f.TagSettings = schema.ParseTagSetting(f.Tag)
} else {
f.TagSettings = map[string]string{}
}
}
if foundField, ok := s.FieldsByName[f.Name]; !ok {
t.Errorf("schema %v failed to look up field with name %v", s, f.Name)
} else {
checkSchemaField(t, foundField, f)
if field, ok := s.FieldsByDBName[f.DBName]; !ok || foundField != field {
t.Errorf("schema %v failed to look up field with dbname %v", s, f.DBName)
}
for _, name := range []string{f.DBName, f.Name} {
if field := s.LookUpField(name); field == nil || foundField != field {
t.Errorf("schema %v failed to look up field with dbname %v", s, f.DBName)
}
}
}
}
}
func checkSchemaField(t *testing.T, parsedField *schema.Field, field schema.Field) {
equalFieldNames := []string{"Name", "DBName", "BindNames", "DataType", "DBDataType", "PrimaryKey", "AutoIncrement", "Creatable", "Updatable", "HasDefaultValue", "DefaultValue", "NotNull", "Unique", "Comment", "Size", "Precision", "Tag", "TagSettings"}
for _, name := range equalFieldNames {
got := reflect.ValueOf(parsedField).Elem().FieldByName(name).Interface()
expects := reflect.ValueOf(field).FieldByName(name).Interface()
if !reflect.DeepEqual(got, expects) {
t.Errorf("%v is not equal, expects: %v, got %v", name, expects, got)
}
}
}

View File

@ -6,7 +6,7 @@ import (
"strings" "strings"
) )
func parseTagSetting(tags reflect.StructTag) map[string]string { func ParseTagSetting(tags reflect.StructTag) map[string]string {
setting := map[string]string{} setting := map[string]string{}
for _, value := range strings.Split(tags.Get("gorm"), ";") { for _, value := range strings.Split(tags.Get("gorm"), ";") {

View File

@ -1,4 +1,4 @@
package gorm_test package tests_test
import ( import (
"fmt" "fmt"

58
tests/model.go Normal file
View File

@ -0,0 +1,58 @@
package tests
import (
"database/sql"
"time"
"github.com/jinzhu/gorm"
)
// User has one `Account` (has one), many `Pets` (has many) and `Toys` (has many - polymorphic)
// He works in a Company (belongs to), he has a Manager (belongs to - single-table), and also managed a Team (has many - single-table)
// He speaks many languages (many to many) and has many friends (many to many - single-table)
// His pet also has one Toy (has one - polymorphic)
type User struct {
gorm.Model
Name string
Age uint
Birthday *time.Time
Account Account
Pets []*Pet
Toys []Toy `gorm:"polymorphic:Owner"`
CompanyID *int
Company Company
ManagerID uint
Manager *User
Team []User `foreignkey:ManagerID`
Friends []*User `gorm:"many2many:user_friends"`
Languages []Language `gorm:"many2many:user_speaks"`
}
type Account struct {
gorm.Model
UserID sql.NullInt64
Number string
}
type Pet struct {
gorm.Model
UserID uint
Name string
Toy Toy `gorm:"polymorphic:Owner;"`
}
type Toy struct {
gorm.Model
OwnerID string
OwnerType string
}
type Company struct {
ID uint
Name string
}
type Language struct {
Code string `gorm:primarykey`
Name string
}