github.com/systematiccaos/gorm@v1.22.6/schema/schema_test.go (about)

     1  package schema_test
     2  
     3  import (
     4  	"strings"
     5  	"sync"
     6  	"testing"
     7  
     8  	"github.com/systematiccaos/gorm"
     9  	"github.com/systematiccaos/gorm/schema"
    10  	"github.com/systematiccaos/gorm/utils/tests"
    11  )
    12  
    13  func TestParseSchema(t *testing.T) {
    14  	user, err := schema.Parse(&tests.User{}, &sync.Map{}, schema.NamingStrategy{})
    15  	if err != nil {
    16  		t.Fatalf("failed to parse user, got error %v", err)
    17  	}
    18  
    19  	checkUserSchema(t, user)
    20  }
    21  
    22  func TestParseSchemaWithPointerFields(t *testing.T) {
    23  	user, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})
    24  	if err != nil {
    25  		t.Fatalf("failed to parse pointer user, got error %v", err)
    26  	}
    27  
    28  	checkUserSchema(t, user)
    29  }
    30  
    31  func checkUserSchema(t *testing.T, user *schema.Schema) {
    32  	// check schema
    33  	checkSchema(t, user, schema.Schema{Name: "User", Table: "users"}, []string{"ID"})
    34  
    35  	// check fields
    36  	fields := []schema.Field{
    37  		{Name: "ID", DBName: "id", BindNames: []string{"Model", "ID"}, DataType: schema.Uint, PrimaryKey: true, Tag: `gorm:"primarykey"`, TagSettings: map[string]string{"PRIMARYKEY": "PRIMARYKEY"}, Size: 64, HasDefaultValue: true, AutoIncrement: true},
    38  		{Name: "CreatedAt", DBName: "created_at", BindNames: []string{"Model", "CreatedAt"}, DataType: schema.Time},
    39  		{Name: "UpdatedAt", DBName: "updated_at", BindNames: []string{"Model", "UpdatedAt"}, DataType: schema.Time},
    40  		{Name: "DeletedAt", DBName: "deleted_at", BindNames: []string{"Model", "DeletedAt"}, Tag: `gorm:"index"`, DataType: schema.Time},
    41  		{Name: "Name", DBName: "name", BindNames: []string{"Name"}, DataType: schema.String},
    42  		{Name: "Age", DBName: "age", BindNames: []string{"Age"}, DataType: schema.Uint, Size: 64},
    43  		{Name: "Birthday", DBName: "birthday", BindNames: []string{"Birthday"}, DataType: schema.Time},
    44  		{Name: "CompanyID", DBName: "company_id", BindNames: []string{"CompanyID"}, DataType: schema.Int, Size: 64},
    45  		{Name: "ManagerID", DBName: "manager_id", BindNames: []string{"ManagerID"}, DataType: schema.Uint, Size: 64},
    46  		{Name: "Active", DBName: "active", BindNames: []string{"Active"}, DataType: schema.Bool},
    47  	}
    48  
    49  	for _, f := range fields {
    50  		checkSchemaField(t, user, &f, func(f *schema.Field) {
    51  			f.Creatable = true
    52  			f.Updatable = true
    53  			f.Readable = true
    54  		})
    55  	}
    56  
    57  	// check relations
    58  	relations := []Relation{
    59  		{
    60  			Name: "Account", Type: schema.HasOne, Schema: "User", FieldSchema: "Account",
    61  			References: []Reference{{"ID", "User", "UserID", "Account", "", true}},
    62  		},
    63  		{
    64  			Name: "Pets", Type: schema.HasMany, Schema: "User", FieldSchema: "Pet",
    65  			References: []Reference{{"ID", "User", "UserID", "Pet", "", true}},
    66  		},
    67  		{
    68  			Name: "Toys", Type: schema.HasMany, Schema: "User", FieldSchema: "Toy",
    69  			Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"},
    70  			References:  []Reference{{"ID", "User", "OwnerID", "Toy", "", true}, {"", "", "OwnerType", "Toy", "users", false}},
    71  		},
    72  		{
    73  			Name: "Company", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Company",
    74  			References: []Reference{{"ID", "Company", "CompanyID", "User", "", false}},
    75  		},
    76  		{
    77  			Name: "Manager", Type: schema.BelongsTo, Schema: "User", FieldSchema: "User",
    78  			References: []Reference{{"ID", "User", "ManagerID", "User", "", false}},
    79  		},
    80  		{
    81  			Name: "Team", Type: schema.HasMany, Schema: "User", FieldSchema: "User",
    82  			References: []Reference{{"ID", "User", "ManagerID", "User", "", true}},
    83  		},
    84  		{
    85  			Name: "Languages", Type: schema.Many2Many, Schema: "User", FieldSchema: "Language",
    86  			JoinTable: JoinTable{Name: "UserSpeak", Table: "user_speaks", Fields: []schema.Field{
    87  				{
    88  					Name: "UserID", DBName: "user_id", BindNames: []string{"UserID"}, DataType: schema.Uint,
    89  					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true, Size: 64,
    90  				},
    91  				{
    92  					Name: "LanguageCode", DBName: "language_code", BindNames: []string{"LanguageCode"}, DataType: schema.String,
    93  					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true,
    94  				},
    95  			}},
    96  			References: []Reference{{"ID", "User", "UserID", "UserSpeak", "", true}, {"Code", "Language", "LanguageCode", "UserSpeak", "", false}},
    97  		},
    98  		{
    99  			Name: "Friends", Type: schema.Many2Many, Schema: "User", FieldSchema: "User",
   100  			JoinTable: JoinTable{Name: "user_friends", Table: "user_friends", Fields: []schema.Field{
   101  				{
   102  					Name: "UserID", DBName: "user_id", BindNames: []string{"UserID"}, DataType: schema.Uint,
   103  					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true, Size: 64,
   104  				},
   105  				{
   106  					Name: "FriendID", DBName: "friend_id", BindNames: []string{"FriendID"}, DataType: schema.Uint,
   107  					Tag: `gorm:"primarykey"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true, Size: 64,
   108  				},
   109  			}},
   110  			References: []Reference{{"ID", "User", "UserID", "user_friends", "", true}, {"ID", "User", "FriendID", "user_friends", "", false}},
   111  		},
   112  	}
   113  
   114  	for _, relation := range relations {
   115  		checkSchemaRelation(t, user, relation)
   116  	}
   117  }
   118  
   119  func TestParseSchemaWithAdvancedDataType(t *testing.T) {
   120  	user, err := schema.Parse(&AdvancedDataTypeUser{}, &sync.Map{}, schema.NamingStrategy{})
   121  	if err != nil {
   122  		t.Fatalf("failed to parse pointer user, got error %v", err)
   123  	}
   124  
   125  	// check schema
   126  	checkSchema(t, user, schema.Schema{Name: "AdvancedDataTypeUser", Table: "advanced_data_type_users"}, []string{"ID"})
   127  
   128  	// check fields
   129  	fields := []schema.Field{
   130  		{Name: "ID", DBName: "id", BindNames: []string{"ID"}, DataType: schema.Int, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true},
   131  		{Name: "Name", DBName: "name", BindNames: []string{"Name"}, DataType: schema.String},
   132  		{Name: "Birthday", DBName: "birthday", BindNames: []string{"Birthday"}, DataType: schema.Time},
   133  		{Name: "RegisteredAt", DBName: "registered_at", BindNames: []string{"RegisteredAt"}, DataType: schema.Time},
   134  		{Name: "DeletedAt", DBName: "deleted_at", BindNames: []string{"DeletedAt"}, DataType: schema.Time},
   135  		{Name: "Active", DBName: "active", BindNames: []string{"Active"}, DataType: schema.Bool},
   136  		{Name: "Admin", DBName: "admin", BindNames: []string{"Admin"}, DataType: schema.Bool},
   137  	}
   138  
   139  	for _, f := range fields {
   140  		checkSchemaField(t, user, &f, func(f *schema.Field) {
   141  			f.Creatable = true
   142  			f.Updatable = true
   143  			f.Readable = true
   144  		})
   145  	}
   146  }
   147  
   148  type CustomizeTable struct {
   149  }
   150  
   151  func (CustomizeTable) TableName() string {
   152  	return "customize"
   153  }
   154  
   155  func TestCustomizeTableName(t *testing.T) {
   156  	customize, err := schema.Parse(&CustomizeTable{}, &sync.Map{}, schema.NamingStrategy{})
   157  	if err != nil {
   158  		t.Fatalf("failed to parse pointer user, got error %v", err)
   159  	}
   160  
   161  	if customize.Table != "customize" {
   162  		t.Errorf("Failed to customize table with TableName method")
   163  	}
   164  }
   165  
   166  func TestNestedModel(t *testing.T) {
   167  	versionUser, err := schema.Parse(&VersionUser{}, &sync.Map{}, schema.NamingStrategy{})
   168  
   169  	if err != nil {
   170  		t.Fatalf("failed to parse nested user, got error %v", err)
   171  	}
   172  
   173  	fields := []schema.Field{
   174  		{Name: "ID", DBName: "id", BindNames: []string{"VersionModel", "BaseModel", "ID"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true},
   175  		{Name: "CreatedBy", DBName: "created_by", BindNames: []string{"VersionModel", "BaseModel", "CreatedBy"}, DataType: schema.Uint, Size: 64},
   176  		{Name: "Version", DBName: "version", BindNames: []string{"VersionModel", "Version"}, DataType: schema.Int, Size: 64},
   177  	}
   178  
   179  	for _, f := range fields {
   180  		checkSchemaField(t, versionUser, &f, func(f *schema.Field) {
   181  			f.Creatable = true
   182  			f.Updatable = true
   183  			f.Readable = true
   184  		})
   185  	}
   186  }
   187  
   188  func TestEmbeddedStruct(t *testing.T) {
   189  	type CorpBase struct {
   190  		gorm.Model
   191  		OwnerID string
   192  	}
   193  
   194  	type Company struct {
   195  		ID      int
   196  		OwnerID int
   197  		Name    string
   198  		Ignored string `gorm:"-"`
   199  	}
   200  
   201  	type Corp struct {
   202  		CorpBase
   203  		Base Company `gorm:"embedded;embeddedPrefix:company_"`
   204  	}
   205  
   206  	cropSchema, err := schema.Parse(&Corp{}, &sync.Map{}, schema.NamingStrategy{})
   207  
   208  	if err != nil {
   209  		t.Fatalf("failed to parse embedded struct with primary key, got error %v", err)
   210  	}
   211  
   212  	fields := []schema.Field{
   213  		{Name: "ID", DBName: "id", BindNames: []string{"CorpBase", "Model", "ID"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true, TagSettings: map[string]string{"PRIMARYKEY": "PRIMARYKEY"}},
   214  		{Name: "ID", DBName: "company_id", BindNames: []string{"Base", "ID"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{"EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   215  		{Name: "Name", DBName: "company_name", BindNames: []string{"Base", "Name"}, DataType: schema.String, TagSettings: map[string]string{"EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   216  		{Name: "Ignored", BindNames: []string{"Base", "Ignored"}, TagSettings: map[string]string{"-": "-", "EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   217  		{Name: "OwnerID", DBName: "company_owner_id", BindNames: []string{"Base", "OwnerID"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{"EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   218  		{Name: "OwnerID", DBName: "owner_id", BindNames: []string{"CorpBase", "OwnerID"}, DataType: schema.String},
   219  	}
   220  
   221  	for _, f := range fields {
   222  		checkSchemaField(t, cropSchema, &f, func(f *schema.Field) {
   223  			if f.Name != "Ignored" {
   224  				f.Creatable = true
   225  				f.Updatable = true
   226  				f.Readable = true
   227  			}
   228  		})
   229  	}
   230  }
   231  
   232  type CustomizedNamingStrategy struct {
   233  	schema.NamingStrategy
   234  }
   235  
   236  func (ns CustomizedNamingStrategy) ColumnName(table, column string) string {
   237  	baseColumnName := ns.NamingStrategy.ColumnName(table, column)
   238  
   239  	if table == "" {
   240  		return baseColumnName
   241  	}
   242  
   243  	s := strings.Split(table, "_")
   244  
   245  	var prefix string
   246  	switch len(s) {
   247  	case 1:
   248  		prefix = s[0][:3]
   249  	case 2:
   250  		prefix = s[0][:1] + s[1][:2]
   251  	default:
   252  		prefix = s[0][:1] + s[1][:1] + s[2][:1]
   253  	}
   254  	return prefix + "_" + baseColumnName
   255  }
   256  
   257  func TestEmbeddedStructForCustomizedNamingStrategy(t *testing.T) {
   258  	type CorpBase struct {
   259  		gorm.Model
   260  		OwnerID string
   261  	}
   262  
   263  	type Company struct {
   264  		ID      int
   265  		OwnerID int
   266  		Name    string
   267  		Ignored string `gorm:"-"`
   268  	}
   269  
   270  	type Corp struct {
   271  		CorpBase
   272  		Base Company `gorm:"embedded;embeddedPrefix:company_"`
   273  	}
   274  
   275  	cropSchema, err := schema.Parse(&Corp{}, &sync.Map{}, CustomizedNamingStrategy{schema.NamingStrategy{}})
   276  
   277  	if err != nil {
   278  		t.Fatalf("failed to parse embedded struct with primary key, got error %v", err)
   279  	}
   280  
   281  	fields := []schema.Field{
   282  		{Name: "ID", DBName: "cor_id", BindNames: []string{"CorpBase", "Model", "ID"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true, TagSettings: map[string]string{"PRIMARYKEY": "PRIMARYKEY"}},
   283  		{Name: "ID", DBName: "company_cor_id", BindNames: []string{"Base", "ID"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{"EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   284  		{Name: "Name", DBName: "company_cor_name", BindNames: []string{"Base", "Name"}, DataType: schema.String, TagSettings: map[string]string{"EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   285  		{Name: "Ignored", BindNames: []string{"Base", "Ignored"}, TagSettings: map[string]string{"-": "-", "EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   286  		{Name: "OwnerID", DBName: "company_cor_owner_id", BindNames: []string{"Base", "OwnerID"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{"EMBEDDED": "EMBEDDED", "EMBEDDEDPREFIX": "company_"}},
   287  		{Name: "OwnerID", DBName: "cor_owner_id", BindNames: []string{"CorpBase", "OwnerID"}, DataType: schema.String},
   288  	}
   289  
   290  	for _, f := range fields {
   291  		checkSchemaField(t, cropSchema, &f, func(f *schema.Field) {
   292  			if f.Name != "Ignored" {
   293  				f.Creatable = true
   294  				f.Updatable = true
   295  				f.Readable = true
   296  			}
   297  		})
   298  	}
   299  }