Skip to content

Commit 0166c04

Browse files
Add tests in associations_many2many_test.go and associations_test.go (#59)
* Add Many-to-many association tests with edge cases, add basic association tests for BelongsTo, HasMany, and ManyToMany
1 parent 95240a1 commit 0166c04

File tree

2 files changed

+329
-2
lines changed

2 files changed

+329
-2
lines changed

tests/associations_many2many_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,3 +461,179 @@ func TestMany2ManyDuplicateBelongsToAssociation(t *testing.T) {
461461
tests.AssertEqual(t, nil, err)
462462
tests.AssertEqual(t, user2, findUser2)
463463
}
464+
465+
func TestMany2ManyEmptyAssociations(t *testing.T) {
466+
user := User{Name: "TestEmptyAssociations"}
467+
468+
// Create user with no associations
469+
if err := DB.Create(&user).Error; err != nil {
470+
t.Fatalf("errors happened when create user: %v", err)
471+
}
472+
473+
var languages []Language
474+
if err := DB.Model(&user).Association("Languages").Find(&languages); err != nil {
475+
t.Errorf("Error finding empty associations: %v", err)
476+
}
477+
478+
if len(languages) != 0 {
479+
t.Errorf("Expected 0 languages, got %d", len(languages))
480+
}
481+
482+
count := DB.Model(&user).Association("Languages").Count()
483+
if count != 0 {
484+
t.Errorf("Expected count 0 for empty association, got %d", count)
485+
}
486+
487+
if err := DB.Model(&user).Association("Languages").Clear(); err != nil {
488+
t.Errorf("Error clearing empty association: %v", err)
489+
}
490+
491+
if err := DB.Model(&user).Association("Languages").Delete(&Language{}); err != nil {
492+
t.Errorf("Error deleting from empty association: %v", err)
493+
}
494+
}
495+
496+
func TestMany2ManyAssociationCountValidation(t *testing.T) {
497+
user := *GetUser("count-validation", Config{Languages: 3})
498+
499+
if err := DB.Create(&user).Error; err != nil {
500+
t.Fatalf("errors happened when create: %v", err)
501+
}
502+
503+
// Initial count check
504+
initialCount := DB.Model(&user).Association("Languages").Count()
505+
if initialCount != 3 {
506+
t.Fatalf("Expected initial count 3, got %d", initialCount)
507+
}
508+
509+
newLanguages := []Language{
510+
{Code: "count-test-1", Name: "Count Test 1"},
511+
{Code: "count-test-2", Name: "Count Test 2"},
512+
}
513+
DB.Create(&newLanguages)
514+
515+
if err := DB.Model(&user).Association("Languages").Append(&newLanguages); err != nil {
516+
t.Fatalf("Error appending languages: %v", err)
517+
}
518+
519+
// Check count after append
520+
countAfterAppend := DB.Model(&user).Association("Languages").Count()
521+
if countAfterAppend != 5 {
522+
t.Errorf("Expected count 5 after append, got %d", countAfterAppend)
523+
}
524+
525+
replaceLanguage := Language{Code: "count-replace", Name: "Count Replace"}
526+
DB.Create(&replaceLanguage)
527+
528+
if err := DB.Model(&user).Association("Languages").Replace(&replaceLanguage); err != nil {
529+
t.Fatalf("Error replacing languages: %v", err)
530+
}
531+
532+
// Check count after replace
533+
countAfterReplace := DB.Model(&user).Association("Languages").Count()
534+
if countAfterReplace != 1 {
535+
t.Errorf("Expected count 1 after replace, got %d", countAfterReplace)
536+
}
537+
538+
// Verify actual data matches count
539+
var actualLanguages []Language
540+
DB.Model(&user).Association("Languages").Find(&actualLanguages)
541+
if len(actualLanguages) != int(countAfterReplace) {
542+
t.Errorf("Count mismatch: Count() returned %d but Find() returned %d languages",
543+
countAfterReplace, len(actualLanguages))
544+
}
545+
}
546+
547+
func TestMany2ManyConstraintViolations(t *testing.T) {
548+
user := *GetUser("constraint-test", Config{Languages: 1})
549+
550+
if err := DB.Create(&user).Error; err != nil {
551+
t.Fatalf("errors happened when create: %v", err)
552+
}
553+
554+
existingLanguage := user.Languages[0]
555+
556+
// append the same language again
557+
if err := DB.Model(&user).Association("Languages").Append(&existingLanguage); err != nil {
558+
t.Logf("Appending duplicate language resulted in: %v", err)
559+
}
560+
561+
// Verify count is still correct after duplicate append attempt
562+
count := DB.Model(&user).Association("Languages").Count()
563+
if count > 1 {
564+
t.Errorf("Expected count 1 after duplicate append, got %d", count)
565+
}
566+
567+
tempLanguage := Language{Code: "temp-invalid", Name: "Temp Invalid"}
568+
if err := DB.Create(&tempLanguage).Error; err != nil {
569+
t.Logf("Could not create temp language for FK test: %v", err)
570+
return
571+
}
572+
573+
// append, then delete the language record to create inconsistency
574+
if err := DB.Model(&user).Association("Languages").Append(&tempLanguage); err != nil {
575+
t.Logf("Could not append temp language: %v", err)
576+
}
577+
578+
// Delete the language record directly
579+
DB.Unscoped().Delete(&tempLanguage)
580+
581+
// access associations after deleting referenced record
582+
var languages []Language
583+
if err := DB.Model(&user).Association("Languages").Find(&languages); err != nil {
584+
t.Logf("Finding associations after FK deletion resulted in: %v", err)
585+
}
586+
587+
// Get count before mass operation for verification
588+
countBeforeMass := DB.Model(&user).Association("Languages").Count()
589+
t.Logf("Language count before mass operation: %d", countBeforeMass)
590+
591+
var manyLanguages []Language
592+
for i := 0; i < 10; i++ {
593+
manyLanguages = append(manyLanguages, Language{
594+
Code: fmt.Sprintf("mass-test-%d", i),
595+
Name: fmt.Sprintf("Mass Test %d", i),
596+
})
597+
}
598+
599+
// Create the languages first
600+
if err := DB.Create(&manyLanguages).Error; err != nil {
601+
t.Logf("Creating many languages failed: %v", err)
602+
return
603+
}
604+
605+
// append all at once
606+
if err := DB.Model(&user).Association("Languages").Append(&manyLanguages); err != nil {
607+
t.Logf("Mass append operation resulted in: %v", err)
608+
}
609+
610+
// Verify the operation completed with proper count validation
611+
finalCount := DB.Model(&user).Association("Languages").Count()
612+
// Should have at least the previous count + 10 new languages
613+
expectedMinCount := countBeforeMass + 10
614+
615+
t.Logf("Final language count after mass operation: %d (expected at least %d)", finalCount, expectedMinCount)
616+
617+
if finalCount < expectedMinCount {
618+
t.Errorf("Expected at least %d languages after mass append, got %d", expectedMinCount, finalCount)
619+
}
620+
621+
var actualLanguages []Language
622+
if err := DB.Model(&user).Association("Languages").Find(&actualLanguages); err == nil {
623+
actualCount := len(actualLanguages)
624+
if actualCount != int(finalCount) {
625+
t.Errorf("Count mismatch: Count() returned %d but Find() returned %d languages",
626+
finalCount, actualCount)
627+
}
628+
}
629+
630+
if err := DB.Model(&user).Association("Languages").Clear(); err != nil {
631+
t.Errorf("Error clearing associations after mass operation: %v", err)
632+
}
633+
634+
// Verify clear worked
635+
countAfterClear := DB.Model(&user).Association("Languages").Count()
636+
if countAfterClear != 0 {
637+
t.Errorf("Expected count 0 after clear, got %d", countAfterClear)
638+
}
639+
}

tests/associations_test.go

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,8 @@ func TestAssociationEmptyQueryClause(t *testing.T) {
367367
Organizations []Organization `gorm:"many2many:region_orgs;"`
368368
}
369369
type RegionOrg struct {
370-
RegionId uint
371-
OrganizationId uint
370+
RegionID uint
371+
OrganizationID uint
372372
Empty myType
373373
}
374374
if err := DB.SetupJoinTable(&Region{}, "Organizations", &RegionOrg{}); err != nil {
@@ -394,3 +394,154 @@ func TestAssociationEmptyQueryClause(t *testing.T) {
394394
tests.AssertEqual(t, len(orgs), 0)
395395
}
396396
}
397+
398+
func TestBasicBelongsToAssociation(t *testing.T) {
399+
// Test basic BelongsTo association operations
400+
user := GetUser("TestBelongsTo", Config{Company: true})
401+
402+
if err := DB.Create(&user).Error; err != nil {
403+
t.Fatalf("Failed to create user with company: %v", err)
404+
}
405+
406+
// finding the association
407+
var foundUser User
408+
if err := DB.Preload("Company").First(&foundUser, "\"id\" = ?", user.ID).Error; err != nil {
409+
t.Fatalf("Failed to find user with company: %v", err)
410+
}
411+
412+
if foundUser.Company.ID != user.Company.ID {
413+
t.Fatalf("Company ID mismatch: expected %d, got %d", user.Company.ID, foundUser.Company.ID)
414+
}
415+
416+
// association count
417+
AssertAssociationCount(t, user, "Company", 1, "after creation")
418+
419+
// replacing association
420+
newCompany := Company{Name: "New Test Company"}
421+
if err := DB.Create(&newCompany).Error; err != nil {
422+
t.Fatalf("Failed to create new company: %v", err)
423+
}
424+
425+
if err := DB.Model(&user).Association("Company").Replace(&newCompany); err != nil {
426+
t.Fatalf("Failed to replace company association: %v", err)
427+
}
428+
429+
var updatedUser User
430+
if err := DB.Preload("Company").First(&updatedUser, "\"id\" = ?", user.ID).Error; err != nil {
431+
t.Fatalf("Failed to find updated user: %v", err)
432+
}
433+
434+
if updatedUser.Company.ID != newCompany.ID {
435+
t.Fatalf("Company was not replaced: expected %d, got %d", newCompany.ID, updatedUser.Company.ID)
436+
}
437+
}
438+
439+
func TestBasicHasManyAssociation(t *testing.T) {
440+
// Test basic HasMany association operations
441+
user := GetUser("TestHasMany", Config{Pets: 3})
442+
443+
if err := DB.Create(&user).Error; err != nil {
444+
t.Fatalf("Failed to create user with pets: %v", err)
445+
}
446+
447+
// association count
448+
AssertAssociationCount(t, user, "Pets", 3, "after creation")
449+
450+
// finding pets
451+
var pets []Pet
452+
if err := DB.Model(&user).Association("Pets").Find(&pets); err != nil {
453+
t.Fatalf("Failed to find pets: %v", err)
454+
}
455+
456+
if len(pets) != 3 {
457+
t.Fatalf("Expected 3 pets, got %d", len(pets))
458+
}
459+
460+
// appending new pet
461+
newPet := Pet{Name: "Additional Pet", UserID: &user.ID}
462+
if err := DB.Model(&user).Association("Pets").Append(&newPet); err != nil {
463+
t.Fatalf("Failed to append pet: %v", err)
464+
}
465+
466+
AssertAssociationCount(t, user, "Pets", 4, "after append")
467+
468+
// deleting one pet from association
469+
if err := DB.Model(&user).Association("Pets").Delete(&pets[0]); err != nil {
470+
t.Fatalf("Failed to delete pet from association: %v", err)
471+
}
472+
473+
AssertAssociationCount(t, user, "Pets", 3, "after delete")
474+
}
475+
476+
func TestBasicManyToManyAssociation(t *testing.T) {
477+
// Test basic ManyToMany association operations
478+
user := GetUser("TestManyToMany", Config{Languages: 2})
479+
480+
if err := DB.Create(&user).Error; err != nil {
481+
t.Fatalf("Failed to create user with languages: %v", err)
482+
}
483+
484+
// association count
485+
AssertAssociationCount(t, user, "Languages", 2, "after creation")
486+
487+
// finding languages
488+
var languages []Language
489+
if err := DB.Model(&user).Association("Languages").Find(&languages); err != nil {
490+
t.Fatalf("Failed to find languages: %v", err)
491+
}
492+
493+
if len(languages) != 2 {
494+
t.Fatalf("Expected 2 languages, got %d", len(languages))
495+
}
496+
497+
// appending new language
498+
newLanguage := Language{Code: "FR", Name: "French"}
499+
if err := DB.Create(&newLanguage).Error; err != nil {
500+
t.Fatalf("Failed to create new language: %v", err)
501+
}
502+
503+
if err := DB.Model(&user).Association("Languages").Append(&newLanguage); err != nil {
504+
t.Fatalf("Failed to append language: %v", err)
505+
}
506+
507+
AssertAssociationCount(t, user, "Languages", 3, "after append")
508+
509+
// replacing all languages
510+
replaceLanguages := []Language{
511+
{Code: "DE", Name: "German"},
512+
{Code: "IT", Name: "Italian"},
513+
}
514+
515+
for i := range replaceLanguages {
516+
if err := DB.Create(&replaceLanguages[i]).Error; err != nil {
517+
t.Fatalf("Failed to create replacement language: %v", err)
518+
}
519+
}
520+
521+
if err := DB.Model(&user).Association("Languages").Replace(replaceLanguages); err != nil {
522+
t.Fatalf("Failed to replace languages: %v", err)
523+
}
524+
525+
AssertAssociationCount(t, user, "Languages", 2, "after replace")
526+
527+
var finalLanguages []Language
528+
if err := DB.Model(&user).Association("Languages").Find(&finalLanguages); err != nil {
529+
t.Fatalf("Failed to find final languages: %v", err)
530+
}
531+
532+
languageCodes := make(map[string]bool)
533+
for _, lang := range finalLanguages {
534+
languageCodes[lang.Code] = true
535+
}
536+
537+
if !languageCodes["DE"] || !languageCodes["IT"] {
538+
t.Fatal("Languages were not replaced correctly")
539+
}
540+
541+
// clearing all associations
542+
if err := DB.Model(&user).Association("Languages").Clear(); err != nil {
543+
t.Fatalf("Failed to clear languages: %v", err)
544+
}
545+
546+
AssertAssociationCount(t, user, "Languages", 0, "after clear")
547+
}

0 commit comments

Comments
 (0)