1package gorm_test 2 3import ( 4 "os" 5 "reflect" 6 "sort" 7 "testing" 8) 9 10type Blog struct { 11 ID uint `gorm:"primary_key"` 12 Locale string `gorm:"primary_key"` 13 Subject string 14 Body string 15 Tags []Tag `gorm:"many2many:blog_tags;"` 16 SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;AssociationForeignKey:id"` 17 LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;AssociationForeignKey:id"` 18} 19 20type Tag struct { 21 ID uint `gorm:"primary_key"` 22 Locale string `gorm:"primary_key"` 23 Value string 24 Blogs []*Blog `gorm:"many2many:blogs_tags"` 25} 26 27func compareTags(tags []Tag, contents []string) bool { 28 var tagContents []string 29 for _, tag := range tags { 30 tagContents = append(tagContents, tag.Value) 31 } 32 sort.Strings(tagContents) 33 sort.Strings(contents) 34 return reflect.DeepEqual(tagContents, contents) 35} 36 37func TestManyToManyWithMultiPrimaryKeys(t *testing.T) { 38 if dialect := os.Getenv("GORM_DIALECT"); dialect != "" && dialect != "sqlite" && dialect != "mssql" { 39 DB.DropTable(&Blog{}, &Tag{}) 40 DB.DropTable("blog_tags") 41 DB.CreateTable(&Blog{}, &Tag{}) 42 blog := Blog{ 43 Locale: "ZH", 44 Subject: "subject", 45 Body: "body", 46 Tags: []Tag{ 47 {Locale: "ZH", Value: "tag1"}, 48 {Locale: "ZH", Value: "tag2"}, 49 }, 50 } 51 52 DB.Save(&blog) 53 if !compareTags(blog.Tags, []string{"tag1", "tag2"}) { 54 t.Errorf("Blog should has two tags") 55 } 56 57 // Append 58 var tag3 = &Tag{Locale: "ZH", Value: "tag3"} 59 DB.Model(&blog).Association("Tags").Append([]*Tag{tag3}) 60 if !compareTags(blog.Tags, []string{"tag1", "tag2", "tag3"}) { 61 t.Errorf("Blog should has three tags after Append") 62 } 63 64 if DB.Model(&blog).Association("Tags").Count() != 3 { 65 t.Errorf("Blog should has three tags after Append") 66 } 67 68 var tags []Tag 69 DB.Model(&blog).Related(&tags, "Tags") 70 if !compareTags(tags, []string{"tag1", "tag2", "tag3"}) { 71 t.Errorf("Should find 3 tags with Related") 72 } 73 74 var blog1 Blog 75 DB.Preload("Tags").Find(&blog1) 76 if !compareTags(blog1.Tags, []string{"tag1", "tag2", "tag3"}) { 77 t.Errorf("Preload many2many relations") 78 } 79 80 // Replace 81 var tag5 = &Tag{Locale: "ZH", Value: "tag5"} 82 var tag6 = &Tag{Locale: "ZH", Value: "tag6"} 83 DB.Model(&blog).Association("Tags").Replace(tag5, tag6) 84 var tags2 []Tag 85 DB.Model(&blog).Related(&tags2, "Tags") 86 if !compareTags(tags2, []string{"tag5", "tag6"}) { 87 t.Errorf("Should find 2 tags after Replace") 88 } 89 90 if DB.Model(&blog).Association("Tags").Count() != 2 { 91 t.Errorf("Blog should has three tags after Replace") 92 } 93 94 // Delete 95 DB.Model(&blog).Association("Tags").Delete(tag5) 96 var tags3 []Tag 97 DB.Model(&blog).Related(&tags3, "Tags") 98 if !compareTags(tags3, []string{"tag6"}) { 99 t.Errorf("Should find 1 tags after Delete") 100 } 101 102 if DB.Model(&blog).Association("Tags").Count() != 1 { 103 t.Errorf("Blog should has three tags after Delete") 104 } 105 106 DB.Model(&blog).Association("Tags").Delete(tag3) 107 var tags4 []Tag 108 DB.Model(&blog).Related(&tags4, "Tags") 109 if !compareTags(tags4, []string{"tag6"}) { 110 t.Errorf("Tag should not be deleted when Delete with a unrelated tag") 111 } 112 113 // Clear 114 DB.Model(&blog).Association("Tags").Clear() 115 if DB.Model(&blog).Association("Tags").Count() != 0 { 116 t.Errorf("All tags should be cleared") 117 } 118 } 119} 120 121func TestManyToManyWithCustomizedForeignKeys(t *testing.T) { 122 if dialect := os.Getenv("GORM_DIALECT"); dialect != "" && dialect != "sqlite" && dialect != "mssql" { 123 DB.DropTable(&Blog{}, &Tag{}) 124 DB.DropTable("shared_blog_tags") 125 DB.CreateTable(&Blog{}, &Tag{}) 126 blog := Blog{ 127 Locale: "ZH", 128 Subject: "subject", 129 Body: "body", 130 SharedTags: []Tag{ 131 {Locale: "ZH", Value: "tag1"}, 132 {Locale: "ZH", Value: "tag2"}, 133 }, 134 } 135 DB.Save(&blog) 136 137 blog2 := Blog{ 138 ID: blog.ID, 139 Locale: "EN", 140 } 141 DB.Create(&blog2) 142 143 if !compareTags(blog.SharedTags, []string{"tag1", "tag2"}) { 144 t.Errorf("Blog should has two tags") 145 } 146 147 // Append 148 var tag3 = &Tag{Locale: "ZH", Value: "tag3"} 149 DB.Model(&blog).Association("SharedTags").Append([]*Tag{tag3}) 150 if !compareTags(blog.SharedTags, []string{"tag1", "tag2", "tag3"}) { 151 t.Errorf("Blog should has three tags after Append") 152 } 153 154 if DB.Model(&blog).Association("SharedTags").Count() != 3 { 155 t.Errorf("Blog should has three tags after Append") 156 } 157 158 if DB.Model(&blog2).Association("SharedTags").Count() != 3 { 159 t.Errorf("Blog should has three tags after Append") 160 } 161 162 var tags []Tag 163 DB.Model(&blog).Related(&tags, "SharedTags") 164 if !compareTags(tags, []string{"tag1", "tag2", "tag3"}) { 165 t.Errorf("Should find 3 tags with Related") 166 } 167 168 DB.Model(&blog2).Related(&tags, "SharedTags") 169 if !compareTags(tags, []string{"tag1", "tag2", "tag3"}) { 170 t.Errorf("Should find 3 tags with Related") 171 } 172 173 var blog1 Blog 174 DB.Preload("SharedTags").Find(&blog1) 175 if !compareTags(blog1.SharedTags, []string{"tag1", "tag2", "tag3"}) { 176 t.Errorf("Preload many2many relations") 177 } 178 179 var tag4 = &Tag{Locale: "ZH", Value: "tag4"} 180 DB.Model(&blog2).Association("SharedTags").Append(tag4) 181 182 DB.Model(&blog).Related(&tags, "SharedTags") 183 if !compareTags(tags, []string{"tag1", "tag2", "tag3", "tag4"}) { 184 t.Errorf("Should find 3 tags with Related") 185 } 186 187 DB.Model(&blog2).Related(&tags, "SharedTags") 188 if !compareTags(tags, []string{"tag1", "tag2", "tag3", "tag4"}) { 189 t.Errorf("Should find 3 tags with Related") 190 } 191 192 // Replace 193 var tag5 = &Tag{Locale: "ZH", Value: "tag5"} 194 var tag6 = &Tag{Locale: "ZH", Value: "tag6"} 195 DB.Model(&blog2).Association("SharedTags").Replace(tag5, tag6) 196 var tags2 []Tag 197 DB.Model(&blog).Related(&tags2, "SharedTags") 198 if !compareTags(tags2, []string{"tag5", "tag6"}) { 199 t.Errorf("Should find 2 tags after Replace") 200 } 201 202 DB.Model(&blog2).Related(&tags2, "SharedTags") 203 if !compareTags(tags2, []string{"tag5", "tag6"}) { 204 t.Errorf("Should find 2 tags after Replace") 205 } 206 207 if DB.Model(&blog).Association("SharedTags").Count() != 2 { 208 t.Errorf("Blog should has three tags after Replace") 209 } 210 211 // Delete 212 DB.Model(&blog).Association("SharedTags").Delete(tag5) 213 var tags3 []Tag 214 DB.Model(&blog).Related(&tags3, "SharedTags") 215 if !compareTags(tags3, []string{"tag6"}) { 216 t.Errorf("Should find 1 tags after Delete") 217 } 218 219 if DB.Model(&blog).Association("SharedTags").Count() != 1 { 220 t.Errorf("Blog should has three tags after Delete") 221 } 222 223 DB.Model(&blog2).Association("SharedTags").Delete(tag3) 224 var tags4 []Tag 225 DB.Model(&blog).Related(&tags4, "SharedTags") 226 if !compareTags(tags4, []string{"tag6"}) { 227 t.Errorf("Tag should not be deleted when Delete with a unrelated tag") 228 } 229 230 // Clear 231 DB.Model(&blog2).Association("SharedTags").Clear() 232 if DB.Model(&blog).Association("SharedTags").Count() != 0 { 233 t.Errorf("All tags should be cleared") 234 } 235 } 236} 237 238func TestManyToManyWithCustomizedForeignKeys2(t *testing.T) { 239 if dialect := os.Getenv("GORM_DIALECT"); dialect != "" && dialect != "sqlite" && dialect != "mssql" { 240 DB.DropTable(&Blog{}, &Tag{}) 241 DB.DropTable("locale_blog_tags") 242 DB.CreateTable(&Blog{}, &Tag{}) 243 blog := Blog{ 244 Locale: "ZH", 245 Subject: "subject", 246 Body: "body", 247 LocaleTags: []Tag{ 248 {Locale: "ZH", Value: "tag1"}, 249 {Locale: "ZH", Value: "tag2"}, 250 }, 251 } 252 DB.Save(&blog) 253 254 blog2 := Blog{ 255 ID: blog.ID, 256 Locale: "EN", 257 } 258 DB.Create(&blog2) 259 260 // Append 261 var tag3 = &Tag{Locale: "ZH", Value: "tag3"} 262 DB.Model(&blog).Association("LocaleTags").Append([]*Tag{tag3}) 263 if !compareTags(blog.LocaleTags, []string{"tag1", "tag2", "tag3"}) { 264 t.Errorf("Blog should has three tags after Append") 265 } 266 267 if DB.Model(&blog).Association("LocaleTags").Count() != 3 { 268 t.Errorf("Blog should has three tags after Append") 269 } 270 271 if DB.Model(&blog2).Association("LocaleTags").Count() != 0 { 272 t.Errorf("EN Blog should has 0 tags after ZH Blog Append") 273 } 274 275 var tags []Tag 276 DB.Model(&blog).Related(&tags, "LocaleTags") 277 if !compareTags(tags, []string{"tag1", "tag2", "tag3"}) { 278 t.Errorf("Should find 3 tags with Related") 279 } 280 281 DB.Model(&blog2).Related(&tags, "LocaleTags") 282 if len(tags) != 0 { 283 t.Errorf("Should find 0 tags with Related for EN Blog") 284 } 285 286 var blog1 Blog 287 DB.Preload("LocaleTags").Find(&blog1, "locale = ? AND id = ?", "ZH", blog.ID) 288 if !compareTags(blog1.LocaleTags, []string{"tag1", "tag2", "tag3"}) { 289 t.Errorf("Preload many2many relations") 290 } 291 292 var tag4 = &Tag{Locale: "ZH", Value: "tag4"} 293 DB.Model(&blog2).Association("LocaleTags").Append(tag4) 294 295 DB.Model(&blog).Related(&tags, "LocaleTags") 296 if !compareTags(tags, []string{"tag1", "tag2", "tag3"}) { 297 t.Errorf("Should find 3 tags with Related for EN Blog") 298 } 299 300 DB.Model(&blog2).Related(&tags, "LocaleTags") 301 if !compareTags(tags, []string{"tag4"}) { 302 t.Errorf("Should find 1 tags with Related for EN Blog") 303 } 304 305 // Replace 306 var tag5 = &Tag{Locale: "ZH", Value: "tag5"} 307 var tag6 = &Tag{Locale: "ZH", Value: "tag6"} 308 DB.Model(&blog2).Association("LocaleTags").Replace(tag5, tag6) 309 310 var tags2 []Tag 311 DB.Model(&blog).Related(&tags2, "LocaleTags") 312 if !compareTags(tags2, []string{"tag1", "tag2", "tag3"}) { 313 t.Errorf("CN Blog's tags should not be changed after EN Blog Replace") 314 } 315 316 var blog11 Blog 317 DB.Preload("LocaleTags").First(&blog11, "id = ? AND locale = ?", blog.ID, blog.Locale) 318 if !compareTags(blog11.LocaleTags, []string{"tag1", "tag2", "tag3"}) { 319 t.Errorf("CN Blog's tags should not be changed after EN Blog Replace") 320 } 321 322 DB.Model(&blog2).Related(&tags2, "LocaleTags") 323 if !compareTags(tags2, []string{"tag5", "tag6"}) { 324 t.Errorf("Should find 2 tags after Replace") 325 } 326 327 var blog21 Blog 328 DB.Preload("LocaleTags").First(&blog21, "id = ? AND locale = ?", blog2.ID, blog2.Locale) 329 if !compareTags(blog21.LocaleTags, []string{"tag5", "tag6"}) { 330 t.Errorf("EN Blog's tags should be changed after Replace") 331 } 332 333 if DB.Model(&blog).Association("LocaleTags").Count() != 3 { 334 t.Errorf("ZH Blog should has three tags after Replace") 335 } 336 337 if DB.Model(&blog2).Association("LocaleTags").Count() != 2 { 338 t.Errorf("EN Blog should has two tags after Replace") 339 } 340 341 // Delete 342 DB.Model(&blog).Association("LocaleTags").Delete(tag5) 343 344 if DB.Model(&blog).Association("LocaleTags").Count() != 3 { 345 t.Errorf("ZH Blog should has three tags after Delete with EN's tag") 346 } 347 348 if DB.Model(&blog2).Association("LocaleTags").Count() != 2 { 349 t.Errorf("EN Blog should has two tags after ZH Blog Delete with EN's tag") 350 } 351 352 DB.Model(&blog2).Association("LocaleTags").Delete(tag5) 353 354 if DB.Model(&blog).Association("LocaleTags").Count() != 3 { 355 t.Errorf("ZH Blog should has three tags after EN Blog Delete with EN's tag") 356 } 357 358 if DB.Model(&blog2).Association("LocaleTags").Count() != 1 { 359 t.Errorf("EN Blog should has 1 tags after EN Blog Delete with EN's tag") 360 } 361 362 // Clear 363 DB.Model(&blog2).Association("LocaleTags").Clear() 364 if DB.Model(&blog).Association("LocaleTags").Count() != 3 { 365 t.Errorf("ZH Blog's tags should not be cleared when clear EN Blog's tags") 366 } 367 368 if DB.Model(&blog2).Association("LocaleTags").Count() != 0 { 369 t.Errorf("EN Blog's tags should be cleared when clear EN Blog's tags") 370 } 371 372 DB.Model(&blog).Association("LocaleTags").Clear() 373 if DB.Model(&blog).Association("LocaleTags").Count() != 0 { 374 t.Errorf("ZH Blog's tags should be cleared when clear ZH Blog's tags") 375 } 376 377 if DB.Model(&blog2).Association("LocaleTags").Count() != 0 { 378 t.Errorf("EN Blog's tags should be cleared") 379 } 380 } 381} 382