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

     1  package schema_test
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  
     7  	"github.com/systematiccaos/gorm"
     8  	"github.com/systematiccaos/gorm/schema"
     9  )
    10  
    11  func checkStructRelation(t *testing.T, data interface{}, relations ...Relation) {
    12  	if s, err := schema.Parse(data, &sync.Map{}, schema.NamingStrategy{}); err != nil {
    13  		t.Errorf("Failed to parse schema")
    14  	} else {
    15  		for _, rel := range relations {
    16  			checkSchemaRelation(t, s, rel)
    17  		}
    18  	}
    19  }
    20  
    21  func TestBelongsToOverrideForeignKey(t *testing.T) {
    22  	type Profile struct {
    23  		gorm.Model
    24  		Name string
    25  	}
    26  
    27  	type User struct {
    28  		gorm.Model
    29  		Profile      Profile `gorm:"ForeignKey:ProfileRefer"`
    30  		ProfileRefer int
    31  	}
    32  
    33  	checkStructRelation(t, &User{}, Relation{
    34  		Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile",
    35  		References: []Reference{{"ID", "Profile", "ProfileRefer", "User", "", false}},
    36  	})
    37  }
    38  
    39  func TestBelongsToOverrideReferences(t *testing.T) {
    40  	type Profile struct {
    41  		gorm.Model
    42  		Refer string
    43  		Name  string
    44  	}
    45  
    46  	type User struct {
    47  		gorm.Model
    48  		Profile   Profile `gorm:"ForeignKey:ProfileID;References:Refer"`
    49  		ProfileID int
    50  	}
    51  
    52  	checkStructRelation(t, &User{}, Relation{
    53  		Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile",
    54  		References: []Reference{{"Refer", "Profile", "ProfileID", "User", "", false}},
    55  	})
    56  }
    57  
    58  func TestBelongsToWithOnlyReferences(t *testing.T) {
    59  	type Profile struct {
    60  		gorm.Model
    61  		Refer string
    62  		Name  string
    63  	}
    64  
    65  	type User struct {
    66  		gorm.Model
    67  		Profile      Profile `gorm:"References:Refer"`
    68  		ProfileRefer int
    69  	}
    70  
    71  	checkStructRelation(t, &User{}, Relation{
    72  		Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile",
    73  		References: []Reference{{"Refer", "Profile", "ProfileRefer", "User", "", false}},
    74  	})
    75  }
    76  
    77  func TestBelongsToWithOnlyReferences2(t *testing.T) {
    78  	type Profile struct {
    79  		gorm.Model
    80  		Refer string
    81  		Name  string
    82  	}
    83  
    84  	type User struct {
    85  		gorm.Model
    86  		Profile   Profile `gorm:"References:Refer"`
    87  		ProfileID int
    88  	}
    89  
    90  	checkStructRelation(t, &User{}, Relation{
    91  		Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile",
    92  		References: []Reference{{"Refer", "Profile", "ProfileID", "User", "", false}},
    93  	})
    94  }
    95  
    96  func TestSelfReferentialBelongsTo(t *testing.T) {
    97  	type User struct {
    98  		ID        int32 `gorm:"primaryKey"`
    99  		Name      string
   100  		CreatorID *int32
   101  		Creator   *User
   102  	}
   103  
   104  	checkStructRelation(t, &User{}, Relation{
   105  		Name: "Creator", Type: schema.BelongsTo, Schema: "User", FieldSchema: "User",
   106  		References: []Reference{{"ID", "User", "CreatorID", "User", "", false}},
   107  	})
   108  
   109  }
   110  
   111  func TestSelfReferentialBelongsToOverrideReferences(t *testing.T) {
   112  	type User struct {
   113  		ID        int32 `gorm:"primaryKey"`
   114  		Name      string
   115  		CreatedBy *int32
   116  		Creator   *User `gorm:"foreignKey:CreatedBy;references:ID"`
   117  	}
   118  
   119  	checkStructRelation(t, &User{}, Relation{
   120  		Name: "Creator", Type: schema.BelongsTo, Schema: "User", FieldSchema: "User",
   121  		References: []Reference{{"ID", "User", "CreatedBy", "User", "", false}},
   122  	})
   123  }
   124  
   125  func TestHasOneOverrideForeignKey(t *testing.T) {
   126  	type Profile struct {
   127  		gorm.Model
   128  		Name      string
   129  		UserRefer uint
   130  	}
   131  
   132  	type User struct {
   133  		gorm.Model
   134  		Profile Profile `gorm:"ForeignKey:UserRefer"`
   135  	}
   136  
   137  	checkStructRelation(t, &User{}, Relation{
   138  		Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile",
   139  		References: []Reference{{"ID", "User", "UserRefer", "Profile", "", true}},
   140  	})
   141  }
   142  
   143  func TestHasOneOverrideReferences(t *testing.T) {
   144  	type Profile struct {
   145  		gorm.Model
   146  		Name   string
   147  		UserID uint
   148  	}
   149  
   150  	type User struct {
   151  		gorm.Model
   152  		Refer   string
   153  		Profile Profile `gorm:"ForeignKey:UserID;References:Refer"`
   154  	}
   155  
   156  	checkStructRelation(t, &User{}, Relation{
   157  		Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile",
   158  		References: []Reference{{"Refer", "User", "UserID", "Profile", "", true}},
   159  	})
   160  }
   161  
   162  func TestHasOneOverrideReferences2(t *testing.T) {
   163  
   164  	type Profile struct {
   165  		gorm.Model
   166  		Name string
   167  	}
   168  
   169  	type User struct {
   170  		gorm.Model
   171  		ProfileID uint     `gorm:"column:profile_id"`
   172  		Profile   *Profile `gorm:"foreignKey:ID;references:ProfileID"`
   173  	}
   174  
   175  	checkStructRelation(t, &User{}, Relation{
   176  		Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile",
   177  		References: []Reference{{"ProfileID", "User", "ID", "Profile", "", true}},
   178  	})
   179  }
   180  
   181  func TestHasOneWithOnlyReferences(t *testing.T) {
   182  	type Profile struct {
   183  		gorm.Model
   184  		Name      string
   185  		UserRefer uint
   186  	}
   187  
   188  	type User struct {
   189  		gorm.Model
   190  		Refer   string
   191  		Profile Profile `gorm:"References:Refer"`
   192  	}
   193  
   194  	checkStructRelation(t, &User{}, Relation{
   195  		Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile",
   196  		References: []Reference{{"Refer", "User", "UserRefer", "Profile", "", true}},
   197  	})
   198  }
   199  
   200  func TestHasOneWithOnlyReferences2(t *testing.T) {
   201  	type Profile struct {
   202  		gorm.Model
   203  		Name   string
   204  		UserID uint
   205  	}
   206  
   207  	type User struct {
   208  		gorm.Model
   209  		Refer   string
   210  		Profile Profile `gorm:"References:Refer"`
   211  	}
   212  
   213  	checkStructRelation(t, &User{}, Relation{
   214  		Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile",
   215  		References: []Reference{{"Refer", "User", "UserID", "Profile", "", true}},
   216  	})
   217  }
   218  
   219  func TestHasManyOverrideForeignKey(t *testing.T) {
   220  	type Profile struct {
   221  		gorm.Model
   222  		Name      string
   223  		UserRefer uint
   224  	}
   225  
   226  	type User struct {
   227  		gorm.Model
   228  		Profile []Profile `gorm:"ForeignKey:UserRefer"`
   229  	}
   230  
   231  	checkStructRelation(t, &User{}, Relation{
   232  		Name: "Profile", Type: schema.HasMany, Schema: "User", FieldSchema: "Profile",
   233  		References: []Reference{{"ID", "User", "UserRefer", "Profile", "", true}},
   234  	})
   235  }
   236  
   237  func TestHasManyOverrideReferences(t *testing.T) {
   238  	type Profile struct {
   239  		gorm.Model
   240  		Name   string
   241  		UserID uint
   242  	}
   243  
   244  	type User struct {
   245  		gorm.Model
   246  		Refer   string
   247  		Profile []Profile `gorm:"ForeignKey:UserID;References:Refer"`
   248  	}
   249  
   250  	checkStructRelation(t, &User{}, Relation{
   251  		Name: "Profile", Type: schema.HasMany, Schema: "User", FieldSchema: "Profile",
   252  		References: []Reference{{"Refer", "User", "UserID", "Profile", "", true}},
   253  	})
   254  }
   255  
   256  func TestMany2ManyOverrideForeignKeyAndReferences(t *testing.T) {
   257  	type Profile struct {
   258  		gorm.Model
   259  		Name      string
   260  		UserRefer uint
   261  	}
   262  
   263  	type User struct {
   264  		gorm.Model
   265  		Profiles  []Profile `gorm:"many2many:user_profiles;ForeignKey:Refer;JoinForeignKey:UserReferID;References:UserRefer;JoinReferences:ProfileRefer"`
   266  		Profiles2 []Profile `gorm:"many2many:user_profiles2;ForeignKey:refer;JoinForeignKey:user_refer_id;References:user_refer;JoinReferences:profile_refer"`
   267  		Refer     uint
   268  	}
   269  
   270  	checkStructRelation(t, &User{}, Relation{
   271  		Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile",
   272  		JoinTable: JoinTable{Name: "user_profiles", Table: "user_profiles"},
   273  		References: []Reference{
   274  			{"Refer", "User", "UserReferID", "user_profiles", "", true},
   275  			{"UserRefer", "Profile", "ProfileRefer", "user_profiles", "", false},
   276  		},
   277  	}, Relation{
   278  		Name: "Profiles2", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile",
   279  		JoinTable: JoinTable{Name: "user_profiles2", Table: "user_profiles2"},
   280  		References: []Reference{
   281  			{"Refer", "User", "User_refer_id", "user_profiles2", "", true},
   282  			{"UserRefer", "Profile", "Profile_refer", "user_profiles2", "", false},
   283  		},
   284  	})
   285  }
   286  
   287  func TestMany2ManyOverrideForeignKey(t *testing.T) {
   288  	type Profile struct {
   289  		gorm.Model
   290  		Name      string
   291  		UserRefer uint
   292  	}
   293  
   294  	type User struct {
   295  		gorm.Model
   296  		Profiles []Profile `gorm:"many2many:user_profiles;ForeignKey:Refer;References:UserRefer"`
   297  		Refer    uint
   298  	}
   299  
   300  	checkStructRelation(t, &User{}, Relation{
   301  		Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile",
   302  		JoinTable: JoinTable{Name: "user_profiles", Table: "user_profiles"},
   303  		References: []Reference{
   304  			{"Refer", "User", "UserRefer", "user_profiles", "", true},
   305  			{"UserRefer", "Profile", "ProfileUserRefer", "user_profiles", "", false},
   306  		},
   307  	})
   308  }
   309  
   310  func TestMany2ManyOverrideJoinForeignKey(t *testing.T) {
   311  	type Profile struct {
   312  		gorm.Model
   313  		Name      string
   314  		UserRefer uint
   315  	}
   316  
   317  	type User struct {
   318  		gorm.Model
   319  		Profiles []Profile `gorm:"many2many:user_profile;JoinForeignKey:UserReferID;JoinReferences:ProfileRefer"`
   320  		Refer    uint
   321  	}
   322  
   323  	checkStructRelation(t, &User{}, Relation{
   324  		Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile",
   325  		JoinTable: JoinTable{Name: "user_profile", Table: "user_profile"},
   326  		References: []Reference{
   327  			{"ID", "User", "UserReferID", "user_profile", "", true},
   328  			{"ID", "Profile", "ProfileRefer", "user_profile", "", false},
   329  		},
   330  	})
   331  }
   332  
   333  func TestBuildReadonlyMany2ManyRelation(t *testing.T) {
   334  	type Profile struct {
   335  		gorm.Model
   336  		Name      string
   337  		UserRefer uint
   338  	}
   339  
   340  	type User struct {
   341  		gorm.Model
   342  		Profiles []Profile `gorm:"->;many2many:user_profile;JoinForeignKey:UserReferID;JoinReferences:ProfileRefer"`
   343  		Refer    uint
   344  	}
   345  
   346  	checkStructRelation(t, &User{}, Relation{
   347  		Name: "Profiles", Type: schema.Many2Many, Schema: "User", FieldSchema: "Profile",
   348  		JoinTable: JoinTable{Name: "user_profile", Table: "user_profile"},
   349  		References: []Reference{
   350  			{"ID", "User", "UserReferID", "user_profile", "", true},
   351  			{"ID", "Profile", "ProfileRefer", "user_profile", "", false},
   352  		},
   353  	})
   354  }
   355  
   356  func TestMany2ManyWithMultiPrimaryKeys(t *testing.T) {
   357  	type Tag struct {
   358  		ID     uint   `gorm:"primary_key"`
   359  		Locale string `gorm:"primary_key"`
   360  		Value  string
   361  	}
   362  
   363  	type Blog struct {
   364  		ID         uint   `gorm:"primary_key"`
   365  		Locale     string `gorm:"primary_key"`
   366  		Subject    string
   367  		Body       string
   368  		Tags       []Tag `gorm:"many2many:blog_tags;"`
   369  		SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
   370  		LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
   371  	}
   372  
   373  	checkStructRelation(t, &Blog{},
   374  		Relation{
   375  			Name: "Tags", Type: schema.Many2Many, Schema: "Blog", FieldSchema: "Tag",
   376  			JoinTable: JoinTable{Name: "blog_tags", Table: "blog_tags"},
   377  			References: []Reference{
   378  				{"ID", "Blog", "BlogID", "blog_tags", "", true},
   379  				{"Locale", "Blog", "BlogLocale", "blog_tags", "", true},
   380  				{"ID", "Tag", "TagID", "blog_tags", "", false},
   381  				{"Locale", "Tag", "TagLocale", "blog_tags", "", false},
   382  			},
   383  		},
   384  		Relation{
   385  			Name: "SharedTags", Type: schema.Many2Many, Schema: "Blog", FieldSchema: "Tag",
   386  			JoinTable: JoinTable{Name: "shared_blog_tags", Table: "shared_blog_tags"},
   387  			References: []Reference{
   388  				{"ID", "Blog", "BlogID", "shared_blog_tags", "", true},
   389  				{"ID", "Tag", "TagID", "shared_blog_tags", "", false},
   390  			},
   391  		},
   392  		Relation{
   393  			Name: "LocaleTags", Type: schema.Many2Many, Schema: "Blog", FieldSchema: "Tag",
   394  			JoinTable: JoinTable{Name: "locale_blog_tags", Table: "locale_blog_tags"},
   395  			References: []Reference{
   396  				{"ID", "Blog", "BlogID", "locale_blog_tags", "", true},
   397  				{"Locale", "Blog", "BlogLocale", "locale_blog_tags", "", true},
   398  				{"ID", "Tag", "TagID", "locale_blog_tags", "", false},
   399  			},
   400  		},
   401  	)
   402  }
   403  
   404  func TestMultipleMany2Many(t *testing.T) {
   405  	type Thing struct {
   406  		ID int
   407  	}
   408  
   409  	type Person struct {
   410  		ID       int
   411  		Likes    []Thing `gorm:"many2many:likes"`
   412  		Dislikes []Thing `gorm:"many2many:dislikes"`
   413  	}
   414  
   415  	checkStructRelation(t, &Person{},
   416  		Relation{
   417  			Name: "Likes", Type: schema.Many2Many, Schema: "Person", FieldSchema: "Thing",
   418  			JoinTable: JoinTable{Name: "likes", Table: "likes"},
   419  			References: []Reference{
   420  				{"ID", "Person", "PersonID", "likes", "", true},
   421  				{"ID", "Thing", "ThingID", "likes", "", false},
   422  			},
   423  		},
   424  		Relation{
   425  			Name: "Dislikes", Type: schema.Many2Many, Schema: "Person", FieldSchema: "Thing",
   426  			JoinTable: JoinTable{Name: "dislikes", Table: "dislikes"},
   427  			References: []Reference{
   428  				{"ID", "Person", "PersonID", "dislikes", "", true},
   429  				{"ID", "Thing", "ThingID", "dislikes", "", false},
   430  			},
   431  		},
   432  	)
   433  }
   434  
   435  func TestSelfReferentialMany2Many(t *testing.T) {
   436  	type User struct {
   437  		ID         int32 `gorm:"primaryKey"`
   438  		Name       string
   439  		CreatedBy  int32
   440  		Creators   []User      `gorm:"foreignKey:CreatedBy"`
   441  		AnotherPro interface{} `gorm:"-"`
   442  	}
   443  
   444  	checkStructRelation(t, &User{}, Relation{
   445  		Name: "Creators", Type: schema.HasMany, Schema: "User", FieldSchema: "User",
   446  		References: []Reference{{"ID", "User", "CreatedBy", "User", "", true}},
   447  	})
   448  
   449  	user, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})
   450  	if err != nil {
   451  		t.Fatalf("failed to parse schema")
   452  	}
   453  
   454  	relSchema := user.Relationships.Relations["Creators"].FieldSchema
   455  	if user != relSchema {
   456  		t.Fatalf("schema should be same, expects %p but got %p", user, relSchema)
   457  	}
   458  }
   459  
   460  type CreatedByModel struct {
   461  	CreatedByID uint
   462  	CreatedBy   *CreatedUser
   463  }
   464  
   465  type CreatedUser struct {
   466  	gorm.Model
   467  	CreatedByModel
   468  }
   469  
   470  func TestEmbeddedRelation(t *testing.T) {
   471  	checkStructRelation(t, &CreatedUser{}, Relation{
   472  		Name: "CreatedBy", Type: schema.BelongsTo, Schema: "CreatedUser", FieldSchema: "CreatedUser",
   473  		References: []Reference{
   474  			{"ID", "CreatedUser", "CreatedByID", "CreatedUser", "", false},
   475  		},
   476  	})
   477  
   478  	userSchema, err := schema.Parse(&CreatedUser{}, &sync.Map{}, schema.NamingStrategy{})
   479  	if err != nil {
   480  		t.Fatalf("failed to parse schema, got error %v", err)
   481  	}
   482  
   483  	if len(userSchema.Relationships.Relations) != 1 {
   484  		t.Fatalf("expects 1 relations, but got %v", len(userSchema.Relationships.Relations))
   485  	}
   486  
   487  	if createdByRel, ok := userSchema.Relationships.Relations["CreatedBy"]; ok {
   488  		if createdByRel.FieldSchema != userSchema {
   489  			t.Fatalf("expects same field schema, but got new %p, old %p", createdByRel.FieldSchema, userSchema)
   490  		}
   491  	} else {
   492  		t.Fatalf("expects created by relations, but not found")
   493  	}
   494  }
   495  
   496  func TestSameForeignKey(t *testing.T) {
   497  	type UserAux struct {
   498  		gorm.Model
   499  		Aux  string
   500  		UUID string
   501  	}
   502  
   503  	type User struct {
   504  		gorm.Model
   505  		Name string
   506  		UUID string
   507  		Aux  *UserAux `gorm:"foreignkey:UUID;references:UUID"`
   508  	}
   509  
   510  	checkStructRelation(t, &User{},
   511  		Relation{
   512  			Name: "Aux", Type: schema.HasOne, Schema: "User", FieldSchema: "UserAux",
   513  			References: []Reference{
   514  				{"UUID", "User", "UUID", "UserAux", "", true},
   515  			},
   516  		},
   517  	)
   518  }
   519  
   520  func TestBelongsToSameForeignKey(t *testing.T) {
   521  
   522  	type User struct {
   523  		gorm.Model
   524  		Name string
   525  		UUID string
   526  	}
   527  
   528  	type UserAux struct {
   529  		gorm.Model
   530  		Aux  string
   531  		UUID string
   532  		User User `gorm:"ForeignKey:UUID;references:UUID;belongsTo"`
   533  	}
   534  
   535  	checkStructRelation(t, &UserAux{},
   536  		Relation{
   537  			Name: "User", Type: schema.BelongsTo, Schema: "UserAux", FieldSchema: "User",
   538  			References: []Reference{
   539  				{"UUID", "User", "UUID", "UserAux", "", false},
   540  			},
   541  		},
   542  	)
   543  }
   544  
   545  func TestHasOneWithSameForeignKey(t *testing.T) {
   546  	type Profile struct {
   547  		gorm.Model
   548  		Name         string
   549  		ProfileRefer int // not used in relationship
   550  	}
   551  
   552  	type User struct {
   553  		gorm.Model
   554  		Profile      Profile `gorm:"ForeignKey:ID;references:ProfileRefer"`
   555  		ProfileRefer int
   556  	}
   557  
   558  	checkStructRelation(t, &User{}, Relation{
   559  		Name: "Profile", Type: schema.HasOne, Schema: "User", FieldSchema: "Profile",
   560  		References: []Reference{{"ProfileRefer", "User", "ID", "Profile", "", true}},
   561  	})
   562  }
   563  
   564  func TestHasManySameForeignKey(t *testing.T) {
   565  	type Profile struct {
   566  		gorm.Model
   567  		Name      string
   568  		UserRefer uint
   569  	}
   570  
   571  	type User struct {
   572  		gorm.Model
   573  		UserRefer uint
   574  		Profile   []Profile `gorm:"ForeignKey:UserRefer"`
   575  	}
   576  
   577  	checkStructRelation(t, &User{}, Relation{
   578  		Name: "Profile", Type: schema.HasMany, Schema: "User", FieldSchema: "Profile",
   579  		References: []Reference{{"ID", "User", "UserRefer", "Profile", "", true}},
   580  	})
   581  }