diff --git a/tests/generics_test.go b/tests/generics_test.go index 315b3eb..8e346d2 100644 --- a/tests/generics_test.go +++ b/tests/generics_test.go @@ -134,16 +134,25 @@ func TestGenericsCreateInBatches(t *testing.T) { } found, err := gorm.G[User](DB).Raw("SELECT * FROM \"users\" WHERE \"name\" LIKE ?", "GenericsCreateInBatches%").Find(ctx) + if err != nil { + t.Fatalf("Raw Find failed: %v", err) + } if len(found) != len(batch) { t.Errorf("expected %d from Raw Find, got %d", len(batch), len(found)) } found, err = gorm.G[User](DB).Where("\"name\" like ?", "GenericsCreateInBatches%").Limit(2).Find(ctx) + if err != nil { + t.Fatalf("Raw Find failed: %v", err) + } if len(found) != 2 { t.Errorf("expected %d from Raw Find, got %d", 2, len(found)) } found, err = gorm.G[User](DB).Where("\"name\" like ?", "GenericsCreateInBatches%").Offset(2).Limit(2).Find(ctx) + if err != nil { + t.Fatalf("Raw Find failed: %v", err) + } if len(found) != 1 { t.Errorf("expected %d from Raw Find, got %d", 1, len(found)) } @@ -168,7 +177,9 @@ func TestGenericsExecAndUpdate(t *testing.T) { name += "Update" rows, err := gorm.G[User](DB).Where("\"id\" = ?", u.ID).Update(ctx, "name", name) - if rows != 1 { + if err != nil { + t.Fatalf("Update failed: %v", err) + } else if rows != 1 { t.Fatalf("failed to get affected rows, got %d, should be %d", rows, 1) } @@ -180,7 +191,9 @@ func TestGenericsExecAndUpdate(t *testing.T) { } rows, err = gorm.G[User](DB).Where("\"id\" = ?", u.ID).Updates(ctx, User{Name: "GenericsExecUpdates", Age: 18}) - if rows != 1 { + if err != nil { + t.Fatalf("Updates failed: %v", err) + } else if rows != 1 { t.Fatalf("failed to get affected rows, got %d, should be %d", rows, 1) } @@ -251,6 +264,37 @@ func TestGenericsDelete(t *testing.T) { if err != gorm.ErrRecordNotFound { t.Fatalf("User after delete failed: %v", err) } + + u2 := User{Name: "GenericsDeleteCond"} + if err := gorm.G[User](DB).Create(ctx, &u2); err != nil { + t.Fatalf("Create failed: %v", err) + } + rows, err = gorm.G[User](DB).Where("\"name\" = ?", "GenericsDeleteCond").Delete(ctx) + if err != nil { + t.Fatalf("Conditional delete failed: %v", err) + } + if rows != 1 { + t.Fatalf("Conditional delete failed, err=%v, rows=%d", err, rows) + } + _, err = gorm.G[User](DB).Where("\"id\" = ?", u2.ID).First(ctx) + if err != gorm.ErrRecordNotFound { + t.Errorf("Expected deleted record to be gone, got: %v", err) + } + + users := []User{ + {Name: "GenericsBatchDel1"}, + {Name: "GenericsBatchDel2"}, + } + if err := gorm.G[User](DB).CreateInBatches(ctx, &users, 2); err != nil { + t.Fatalf("Batch create for delete failed: %v", err) + } + rows, err = gorm.G[User](DB).Where("\"name\" LIKE ?", "GenericsBatchDel%").Delete(ctx) + if err != nil { + t.Fatalf("Batch delete failed: %v", err) + } + if rows != 2 { + t.Errorf("batch delete expected 2 rows, got %d", rows) + } } func TestGenericsFindInBatches(t *testing.T) { @@ -301,20 +345,23 @@ func TestGenericsScopes(t *testing.T) { results, err := gorm.G[User](DB).Scopes(filterName1).Find(ctx) if err != nil { t.Fatalf("Scopes failed: %v", err) - } - if len(results) != 1 || results[0].Name != "GenericsScopes1" { + } else if len(results) != 1 || results[0].Name != "GenericsScopes1" { t.Fatalf("Scopes expected 1, got %d", len(results)) } notResult, err := gorm.G[User](DB).Where("\"name\" like ?", "GenericsScopes%").Not("\"name\" = ?", "GenericsScopes1").Order("\"name\"").Find(ctx) - if len(notResult) != 2 { + if err != nil { + t.Fatalf("Not failed: %v", err) + } else if len(notResult) != 2 { t.Fatalf("expected 2 results, got %d", len(notResult)) } else if notResult[0].Name != "GenericsScopes2" || notResult[1].Name != "GenericsScopes3" { t.Fatalf("expected names 'GenericsScopes2' and 'GenericsScopes3', got %s and %s", notResult[0].Name, notResult[1].Name) } orResult, err := gorm.G[User](DB).Or("\"name\" = ?", "GenericsScopes1").Or("\"name\" = ?", "GenericsScopes2").Order("\"name\"").Find(ctx) - if len(orResult) != 2 { + if err != nil { + t.Fatalf("Or failed: %v", err) + } else if len(orResult) != 2 { t.Fatalf("expected 2 results, got %d", len(notResult)) } else if orResult[0].Name != "GenericsScopes1" || orResult[1].Name != "GenericsScopes2" { t.Fatalf("expected names 'GenericsScopes2' and 'GenericsScopes3', got %s and %s", orResult[0].Name, orResult[1].Name) @@ -554,6 +601,9 @@ func TestGenericsPreloads(t *testing.T) { return nil }).Where("\"name\" in ?", names).Find(ctx) + if err != nil { + t.Fatalf("Preload failed: %v", err) + } for _, result := range results { if result.Name == u.Name { if len(result.Pets) != len(u.Pets) { @@ -577,6 +627,9 @@ func TestGenericsPreloads(t *testing.T) { return nil }).Where("\"name\" in ?", names).Find(ctx) + if err != nil { + t.Fatalf("Preload failed: %v", err) + } for _, result := range results { if result.Name == u.Name { if len(result.Pets) != len(u.Pets) { @@ -608,6 +661,9 @@ func TestGenericsPreloads(t *testing.T) { return nil }).Where("\"name\" in ?", names).Find(ctx) + if err != nil { + t.Fatalf("Preload failed: %v", err) + } for _, result := range results { if result.Name == u.Name { if len(result.Pets) != len(u.Pets) { @@ -868,6 +924,9 @@ func TestGenericsWithTransaction(t *testing.T) { users := []User{{Name: "TestGenericsTransaction", Age: 18}, {Name: "TestGenericsTransaction2", Age: 18}} err := gorm.G[User](tx).CreateInBatches(ctx, &users, 2) + if err != nil { + t.Fatalf("CreateInBatches failed: %v", err) + } count, err := gorm.G[User](tx).Where("\"name\" like ?", "TestGenericsTransaction%").Count(ctx, "*") if err != nil { @@ -897,7 +956,95 @@ func TestGenericsToSQL(t *testing.T) { return tx }) - if !regexp.MustCompile("SELECT \\* FROM .users..* 10").MatchString(sql) { + if !regexp.MustCompile(`SELECT \* FROM .users..* 10`).MatchString(sql) { t.Errorf("ToSQL: got wrong sql with Generics API %v", sql) } } + +func TestGenericsCountOmitSelect(t *testing.T) { + ctx := context.Background() + users := []User{{Name: "GenericsCount1", Age: 5}, {Name: "GenericsCount2", Age: 7}} + err := gorm.G[User](DB).CreateInBatches(ctx, &users, 2) + if err != nil { + t.Fatalf("CreateInBatches failed: %v", err) + } + count, err := gorm.G[User](DB).Omit("age").Where("\"name\" LIKE ?", "GenericsCount%").Count(ctx, "*") + if err != nil { + t.Fatalf("Count failed: %v", err) + } + if count != 2 { + t.Errorf("Count with Omit: expected 2, got %d", count) + } +} + +func TestGenericsSelectAndOmitFind(t *testing.T) { + ctx := context.Background() + u := User{Name: "GenericsSelectOmit", Age: 30} + err := gorm.G[User](DB).Create(ctx, &u) + if err != nil { + t.Fatalf("Create failed: %v", err) + } + result, err := gorm.G[User](DB).Select("id").Omit("age").Where("\"name\" = ?", u.Name).First(ctx) + if err != nil { + t.Fatalf("Select and Omit Find failed: %v", err) + } + if result.ID != u.ID || result.Name != "" || result.Age != 0 { + t.Errorf("SelectAndOmitFind expects partial zero values, got: %+v", result) + } +} + +func TestGenericsSelectWithPreloadAssociations(t *testing.T) { + ctx := context.Background() + user := User{Name: "SelectPreloadCombo", Age: 40, Company: Company{Name: "ComboCompany"}} + for i := 1; i <= 2; i++ { + user.Pets = append(user.Pets, &Pet{Name: fmt.Sprintf("Pet-%d", i)}) + } + if err := gorm.G[User](DB).Create(ctx, &user); err != nil { + t.Fatalf("Create failed: %v", err) + } + result, err := gorm.G[User](DB).Select("id", "name", "company_id").Preload("Company", nil).Preload("Pets", nil).Where("\"name\" = ?", user.Name).First(ctx) + if err != nil { + t.Fatalf("Select+Preload First failed: %v", err) + } + if result.ID == 0 || result.Name == "" { + t.Errorf("Expected user id/name; got: %+v", result) + } + if result.Age != 0 { + t.Errorf("Expected omitted Age=0, got %d", result.Age) + } + if result.Company.Name != user.Company.Name { + t.Errorf("Expected company %+v, got %+v", user.Company, result.Company) + } + if len(result.Pets) != len(user.Pets) { + t.Errorf("Expected %d pets, got %d", len(user.Pets), len(result.Pets)) + } +} + +func TestGenericsTransactionRollbackOnPreloadError(t *testing.T) { + ctx := context.Background() + tx := DB.Begin() + if tx.Error != nil { + t.Fatalf("Failed to begin transaction: %v", tx.Error) + } + user := User{Name: "TxRollbackPreload", Age: 25, Company: Company{Name: "TxCompany"}} + if err := gorm.G[User](tx).Create(ctx, &user); err != nil { + _ = tx.Rollback() + t.Fatalf("Failed to create user in tx: %v", err) + } + var gotErr error + _, gotErr = gorm.G[User](tx). + Preload("Company", func(db gorm.PreloadBuilder) error { return fmt.Errorf("bad preload") }). + Where("\"name\" = ?", user.Name). + First(ctx) + errRollback := tx.Rollback().Error + if gotErr == nil { + t.Errorf("Expected preload error, got nil") + } + if errRollback != nil { + t.Fatalf("Failed to rollback on Preload error: %v", errRollback) + } + _, err := gorm.G[User](DB).Where("\"name\" = ?", user.Name).First(ctx) + if err == nil { + t.Errorf("Expected no user after rollback, but found one") + } +} diff --git a/tests/gorm_test.go b/tests/gorm_test.go index b1935b8..acd9149 100644 --- a/tests/gorm_test.go +++ b/tests/gorm_test.go @@ -46,10 +46,20 @@ import ( ) func TestOpen(t *testing.T) { - dsn := "gorm:gorm@tcp(localhost:9910)/gorm?loc=Asia%2FHongKong" // invalid loc - _, err := gorm.Open(oracle.Open(dsn), &gorm.Config{}) - if err == nil { - t.Fatalf("should returns error but got nil") + dsns := []string{ + "gorm@localhost:9910/invalid", + "gorm:gorm@localhost:9910", + "invalid_dsn_string", + "gorm:gorm@tcp(localhost:9910)/gorm?loc=Asia%2FHongKong", + "gorm@localhost:2300/invalid_service", + "gorm@localhost:invalid_port/cdb1_pdb1", + "gorm@invalid_host:1111/cdb1_pdb1", + } + for _, dsn := range dsns { + _, err := gorm.Open(oracle.Open(dsn), &gorm.Config{}) + if err == nil { + t.Errorf("should return error for dsn=%q but got nil", dsn) + } } } @@ -130,3 +140,113 @@ func TestReturningWithNullToZeroValues(t *testing.T) { t.Fatalf("rows affected expects: %v, got %v", 1, results.RowsAffected) } } + +func TestReturningWithNullAndAdditionalFields(t *testing.T) { + type userWithFields struct { + gorm.Model + Name string `gorm:"default:noname"` + Age int + Comment string + } + + DB.Migrator().DropTable(&userWithFields{}) + DB.Migrator().AutoMigrate(&userWithFields{}) + + // Insert a user and verify defaults + u := userWithFields{} + if results := DB.Create(&u); results.Error != nil { + t.Fatalf("errors happened on create: %v", results.Error) + } else if results.RowsAffected != 1 { + t.Fatalf("rows affected expects: %v, got %v", 1, results.RowsAffected) + } else if u.ID == 0 || u.Name != "noname" || u.Age != 0 || u.Comment != "" { + t.Fatalf("create expects ID!=0, Name='noname', Age=0, Comment='', got %+v", u) + } + + // Update all fields and verify + u.Name = "TestName" + u.Age = 42 + u.Comment = "Hello" + if results := DB.Save(&u); results.Error != nil { + t.Fatalf("errors happened on update: %v", results.Error) + } else if results.RowsAffected != 1 { + t.Fatalf("rows affected expects: %v, got %v", 1, results.RowsAffected) + } else if u.Name != "TestName" || u.Age != 42 || u.Comment != "Hello" { + t.Fatalf("update expects Name='TestName', Age=42, Comment='Hello', got %+v", u) + } + + // Fetch and verify + got := userWithFields{} + results := DB.First(&got, "\"id\" = ?", u.ID) + if results.Error != nil { + t.Fatalf("errors happened on first: %v", results.Error) + } else if results.RowsAffected != 1 { + t.Fatalf("rows affected expects: %v, got %v", 1, results.RowsAffected) + } else if got.ID != u.ID || got.Name != "TestName" || got.Age != 42 || got.Comment != "Hello" { + t.Fatalf("first expects %+v, got %+v", u, got) + } + + // Batch create and check + u1 := userWithFields{} + u2 := userWithFields{Name: "foo"} + u3 := userWithFields{Name: "bar", Age: 99, Comment: "bar-comment"} + db := DB.Session(&gorm.Session{CreateBatchSize: 10}) + if results := db.Create([]*userWithFields{&u1, &u2, &u3}); results.Error != nil { + t.Fatalf("errors happened on create: %v", results.Error) + } else if results.RowsAffected != 3 { + t.Fatalf("rows affected expects: %v, got %v", 3, results.RowsAffected) + } else if u1.ID == 0 || u2.ID == 0 || u3.ID == 0 { + t.Fatalf("ID expects : not equal 0, got %v,%v,%v", u1.ID, u2.ID, u3.ID) + } else if u1.Name != "noname" || u2.Name != "foo" || u3.Name != "bar" { + t.Fatalf("names expect: noname, foo, bar, got: %q,%q,%q", u1.Name, u2.Name, u3.Name) + } else if u1.Age != 0 || u2.Age != 0 || u3.Age != 99 { + t.Fatalf("ages expect: 0,0,99, got: %v,%v,%v", u1.Age, u2.Age, u3.Age) + } else if u1.Comment != "" || u2.Comment != "" || u3.Comment != "bar-comment" { + t.Fatalf("comments expect: '', '', 'bar-comment', got: %q,%q,%q", u1.Comment, u2.Comment, u3.Comment) + } + + // Batch update and check + u1.Name = "A" + u2.Name = "B" + u3.Comment = "updated" + if results := DB.Save([]*userWithFields{&u1, &u2, &u3}); results.Error != nil { + t.Fatalf("errors happened on update: %v", results.Error) + } else if results.RowsAffected != 3 { + t.Fatalf("rows affected expects: %v, got %v", 3, results.RowsAffected) + } else if u1.Name != "A" || u2.Name != "B" || u3.Name != "bar" { + t.Fatalf("names expect: A, B, bar, got: %q,%q,%q", u1.Name, u2.Name, u3.Name) + } else if u1.Age != 0 || u2.Age != 0 || u3.Age != 99 { + t.Fatalf("ages expect: 0,0,99, got: %v,%v,%v", u1.Age, u2.Age, u3.Age) + } else if u1.Comment != "" || u2.Comment != "" || u3.Comment != "updated" { + t.Fatalf("comments expect: '', '', 'updated', got: %q,%q,%q", u1.Comment, u2.Comment, u3.Comment) + } + + // Batch fetch and verify + updated := []userWithFields{} + results = DB.Where("\"id\" in (?, ?, ?)", u1.ID, u2.ID, u3.ID).Order("\"id\" asc").Find(&updated) + if results.Error != nil { + t.Fatalf("errors happened on batch find: %v", results.Error) + } else if results.RowsAffected != 3 { + t.Fatalf("rows affected expects: %v, got %v", 3, results.RowsAffected) + } else if len(updated) != 3 { + t.Fatalf("batch find expects: %v records, got %v", 3, len(updated)) + } else if updated[0].ID != u1.ID || updated[1].ID != u2.ID || updated[2].ID != u3.ID { + t.Fatalf("batch find expects IDs: %v,%v,%v, got: %v,%v,%v", u1.ID, u2.ID, u3.ID, updated[0].ID, updated[1].ID, updated[2].ID) + } else if updated[0].Name != "A" || updated[1].Name != "B" || updated[2].Name != "bar" { + t.Fatalf("batch find expects Names: A,B,bar, got: %q,%q,%q", updated[0].Name, updated[1].Name, updated[2].Name) + } else if updated[0].Age != 0 || updated[1].Age != 0 || updated[2].Age != 99 { + t.Fatalf("batch find expects Ages: 0,0,99, got: %v,%v,%v", updated[0].Age, updated[1].Age, updated[2].Age) + } else if updated[0].Comment != "" || updated[1].Comment != "" || updated[2].Comment != "updated" { + t.Fatalf("batch find expects Comments: '', '', 'updated', got: %q,%q,%q", updated[0].Comment, updated[1].Comment, updated[2].Comment) + } + + // Delete and verify one user + if err := DB.Delete(&userWithFields{}, u2.ID).Error; err != nil { + t.Fatalf("Delete failed: %v", err) + } + var deleted userWithFields + if err := DB.First(&deleted, u2.ID).Error; err == nil { + t.Fatalf("Deleted user with ID=%d found", u2.ID) + } + + DB.Migrator().DropTable(&userWithFields{}) +}