@@ -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+ }
0 commit comments