Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions tests/update_belongs_to_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,99 @@ func TestUpdateBelongsTo(t *testing.T) {
}
CheckUserSkipUpdatedAt(t, user, user5)
}

func TestUpdateBelongsToWithReturning(t *testing.T) {
user := *GetUser("update-belongs-to-returning", Config{})

// Test that RETURNING clauses work properly when updating with associations
if err := DB.Create(&user).Error; err != nil {
t.Fatalf("errors happened when create: %v", err)
}

originalUpdatedAt := user.UpdatedAt
user.Company = Company{Name: "returning-test-company"}
user.Manager = &User{Name: "returning-test-manager"}

// Save and verify that UpdatedAt was properly returned from db
if err := DB.Save(&user).Error; err != nil {
t.Fatalf("errors happened when update with returning: %v", err)
}

// Verify RETURNING clause populated the UpdatedAt field
if !user.UpdatedAt.After(originalUpdatedAt) {
t.Errorf("expected UpdatedAt to be updated via RETURNING clause")
}

// Verify the associations were created properly
var result User
if err := DB.Preload("Company").Preload("Manager").First(&result, user.ID).Error; err != nil {
t.Fatalf("failed to load user with associations: %v", err)
}

if result.Company.Name != "returning-test-company" {
t.Errorf("expected company name to be saved correctly")
}
if result.Manager.Name != "returning-test-manager" {
t.Errorf("expected manager name to be saved correctly")
}
}

func TestUpdateBelongsToWithNullValues(t *testing.T) {
user := *GetUser("update-belongs-to-null", Config{})

// Create user with associations
user.Company = Company{Name: "initial-company"}
user.Manager = &User{Name: "initial-manager"}

if err := DB.Create(&user).Error; err != nil {
t.Fatalf("errors happened when create: %v", err)
}

// Verify associations were created
if user.CompanyID == nil {
t.Fatalf("expected CompanyID to be set after create")
}
if user.ManagerID == nil {
t.Fatalf("expected ManagerID to be set after create")
}

// Test setting foreign keys to NULL - clear both the foreign keys AND the association objects
user.CompanyID = nil // Clear foreign key
user.ManagerID = nil // Clear foreign key
user.Company = Company{} // Clear association object (zero value)
user.Manager = nil // Clear association pointer

if err := DB.Model(&user).Updates(map[string]interface{}{
"company_id": nil,
"manager_id": nil,
}).Error; err != nil {
t.Fatalf("errors happened when setting associations to null: %v", err)
}

var result User
if err := DB.First(&result, user.ID).Error; err != nil {
t.Fatalf("failed to load user: %v", err)
}

// Verify foreign keys are properly NULL
if result.CompanyID != nil {
t.Errorf("expected CompanyID to be NULL, got %v", *result.CompanyID)
}
if result.ManagerID != nil {
t.Errorf("expected ManagerID to be NULL, got %v", *result.ManagerID)
}

// Try to load with preload to ensure NULL handling works
var resultWithPreload User
if err := DB.Preload("Company").Preload("Manager").First(&resultWithPreload, user.ID).Error; err != nil {
t.Fatalf("failed to load user with preload: %v", err)
}

// When foreign keys are NULL, preloaded associations should be zero values
if resultWithPreload.Company.ID != 0 {
t.Errorf("expected Company to be zero value when foreign key is NULL")
}
if resultWithPreload.Manager != nil {
t.Errorf("expected Manager to be nil when foreign key is NULL")
}
}
48 changes: 48 additions & 0 deletions tests/update_has_many_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,51 @@ func TestUpdateHasManyAssociations(t *testing.T) {
CheckUserSkipUpdatedAt(t, user4, user)
})
}

func TestHasManyWithReturning(t *testing.T) {
user := *GetUser("returning-has-many", Config{})

if err := DB.Create(&user).Error; err != nil {
t.Fatalf("failed to create user: %v", err)
}

initialPets := []*Pet{
{Name: "returning-pet1"},
{Name: "returning-pet2"},
}

user.Pets = initialPets

// This should trigger RETURNING clauses for the new pets
if err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user).Error; err != nil {
t.Fatalf("failed to save user with pets: %v", err)
}

// Verify RETURNING populated IDs and timestamps for pets
for _, pet := range user.Pets {
if pet.ID == 0 {
t.Errorf("pet ID should be populated via RETURNING clause")
}
if pet.CreatedAt.IsZero() {
t.Errorf("pet CreatedAt should be populated via RETURNING clause")
}
if pet.UserID == nil || *pet.UserID != user.ID {
t.Errorf("pet UserID should be set correctly")
}
}

// try updating existing pets with RETURNING
for _, pet := range user.Pets {
originalUpdatedAt := pet.UpdatedAt
pet.Name += "-updated"

if err := DB.Save(pet).Error; err != nil {
t.Fatalf("failed to update pet: %v", err)
}

// Verify RETURNING updated the UpdatedAt timestamp
if !pet.UpdatedAt.After(originalUpdatedAt) {
t.Errorf("pet UpdatedAt should be updated via RETURNING clause")
}
}
}
Loading