From 0a83ae14ed99823bdaec81be43eb7ace7aea6028 Mon Sep 17 00:00:00 2001 From: Saumil Diwaker Date: Mon, 8 Sep 2025 15:04:40 +0530 Subject: [PATCH 1/2] Added advanced operations test --- tests/migrate_test.go | 390 +++++++++++++++++++++++++++++-- tests/multi_primary_keys_test.go | 84 +++++++ tests/named_polymorphic_test.go | 296 ++++++++++++++++++++++- 3 files changed, 750 insertions(+), 20 deletions(-) diff --git a/tests/migrate_test.go b/tests/migrate_test.go index 37783d8..36e2e88 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -718,7 +718,6 @@ func TestMigrateColumnOrder(t *testing.T) { } } -// https://github.com/go-gorm/gorm/issues/5047 func TestMigrateSerialColumn(t *testing.T) { if DB.Dialector.Name() != "postgres" { return @@ -780,7 +779,6 @@ func TestMigrateSerialColumn(t *testing.T) { } } -// https://github.com/go-gorm/gorm/issues/5300 func TestMigrateWithSpecialName(t *testing.T) { var err error err = DB.AutoMigrate(&Coupon{}) @@ -801,7 +799,6 @@ func TestMigrateWithSpecialName(t *testing.T) { tests.AssertEqual(t, true, DB.Migrator().HasTable("coupon_product_2")) } -// https://github.com/go-gorm/gorm/issues/4760 func TestMigrateAutoIncrement(t *testing.T) { type AutoIncrementStruct struct { ID int64 `gorm:"primarykey;autoIncrement"` @@ -1625,7 +1622,7 @@ func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string { session := DB.Session(&gorm.Session{Logger: tracer}) DB.Migrator().DropTable(model1) - var modifySql []string + var modifySQL []string if err := session.AutoMigrate(model1); err != nil { t.Fatalf("failed to auto migrate, got error: %v", err) } @@ -1636,7 +1633,7 @@ func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string { Logger: DB.Config.Logger, Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { sql, _ := fc() - modifySql = append(modifySql, sql) + modifySQL = append(modifySQL, sql) }, } session2 := DB.Session(&gorm.Session{Logger: tracer2}) @@ -1644,26 +1641,26 @@ func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string { if err != nil { t.Fatalf("failed to get column types, got error: %v", err) } - return modifySql + return modifySQL } -func decimalColumnsTest[T, T2 any](t *testing.T, expectedSql []string) { +func decimalColumnsTest[T, T2 any](t *testing.T, expectedSQL []string) { var t1 T var t2 T2 - modSql := testAutoMigrateDecimal(t, t1, t2) + modSQL := testAutoMigrateDecimal(t, t1, t2) var alterSQL []string - for _, sql := range modSql { + for _, sql := range modSQL { if strings.HasPrefix(sql, "ALTER TABLE ") { alterSQL = append(alterSQL, sql) } } if len(alterSQL) != 3 { - t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL) + t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSQL, alterSQL) } for i := range alterSQL { - if alterSQL[i] != expectedSql[i] { - t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL) + if alterSQL[i] != expectedSQL[i] { + t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSQL, alterSQL) } } } @@ -1680,12 +1677,12 @@ func TestAutoMigrateDecimal(t *testing.T) { RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"` RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"` } - expectedSql := []string{ + expectedSQL := []string{ `ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" decimal(8) NOT NULL`, `ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" decimal(9,1) NOT NULL`, `ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" decimal(9,2) NOT NULL`, } - decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql) + decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSQL) } else if DB.Dialector.Name() == "postgres" { type MigrateDecimalColumn struct { RecID1 int64 `gorm:"column:recid1;type:numeric(9,0);not null" json:"recid1"` @@ -1697,12 +1694,12 @@ func TestAutoMigrateDecimal(t *testing.T) { RecID2 int64 `gorm:"column:recid2;type:numeric(9,1);not null" json:"recid2"` RecID3 int64 `gorm:"column:recid3;type:numeric(9,2);not null" json:"recid3"` } - expectedSql := []string{ + expectedSQL := []string{ `ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" TYPE numeric(8) USING "recid1"::numeric(8)`, `ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" TYPE numeric(9,1) USING "recid2"::numeric(9,1)`, `ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" TYPE numeric(9,2) USING "recid3"::numeric(9,2)`, } - decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql) + decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSQL) } else if DB.Dialector.Name() == "mysql" { type MigrateDecimalColumn struct { RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"` @@ -1714,11 +1711,368 @@ func TestAutoMigrateDecimal(t *testing.T) { RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"` RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"` } - expectedSql := []string{ + expectedSQL := []string{ "ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid1` decimal(8) NOT NULL", "ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid2` decimal(9,1) NOT NULL", "ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid3` decimal(9,2) NOT NULL", } - decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql) + decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSQL) + } +} + +func TestOracleBasicMigration(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + return + } + + type OracleTestModel struct { + ID uint `gorm:"primarykey"` + Name string `gorm:"size:100"` + Email string `gorm:"unique;size:255"` + Age int `gorm:"default:0"` + IsActive bool `gorm:"default:true"` + Amount float64 `gorm:"type:number(10,2)"` + } + + // Drop and recreate table + if err := DB.Migrator().DropTable(&OracleTestModel{}); err != nil { + if !strings.Contains(err.Error(), "ORA-00942") { + t.Fatalf("Unexpected error dropping table: %v", err) + } + } + + if err := DB.AutoMigrate(&OracleTestModel{}); err != nil { + t.Fatalf("Failed to auto migrate, got error %v", err) + } + + // Verify table exists + if !DB.Migrator().HasTable(&OracleTestModel{}) { + t.Fatalf("Table should exist after migration") + } + + // Test basic CRUD operations to verify the table works + testRecord := &OracleTestModel{ + Name: "Test User", + Email: "test@example.com", + Age: 25, + IsActive: true, + Amount: 123.45, + } + + if err := DB.Create(testRecord).Error; err != nil { + t.Fatalf("Failed to create test record: %v", err) + } + + if testRecord.ID == 0 { + t.Fatalf("Expected auto-generated ID") + } + + // Verify record can be retrieved + var retrieved OracleTestModel + if err := DB.First(&retrieved, testRecord.ID).Error; err != nil { + t.Fatalf("Failed to retrieve record: %v", err) + } + + if retrieved.Name != testRecord.Name { + t.Fatalf("Name mismatch: expected %s, got %s", testRecord.Name, retrieved.Name) + } +} + +func TestOracleDataTypes(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + return + } + + type OracleDataModel struct { + ID uint `gorm:"primarykey"` + TextShort string `gorm:"size:100"` + TextLong string `gorm:"type:clob"` + NumberInt int64 `gorm:"type:number(10)"` + NumberDec float64 `gorm:"type:number(10,2)"` + BoolField bool `gorm:"type:number(1)"` + DateField time.Time `gorm:"type:timestamp"` + BinaryData []byte `gorm:"type:raw(100)"` + } + + if err := DB.Migrator().DropTable(&OracleDataModel{}); err != nil { + if !strings.Contains(err.Error(), "ORA-00942") { + t.Fatalf("Unexpected error dropping table: %v", err) + } + } + + if err := DB.AutoMigrate(&OracleDataModel{}); err != nil { + t.Fatalf("Failed to migrate Oracle data types, got error %v", err) + } + + // Test Oracle data type operations + testData := &OracleDataModel{ + TextShort: "Short text", + TextLong: strings.Repeat("Long text data. ", 100), + NumberInt: 12345, + NumberDec: 123.45, + BoolField: true, + DateField: time.Now().Truncate(time.Second), + BinaryData: []byte{0x01, 0x02, 0x03, 0x04}, + } + + if err := DB.Create(testData).Error; err != nil { + t.Fatalf("Failed to create test data: %v", err) + } + + var retrieved OracleDataModel + if err := DB.First(&retrieved, testData.ID).Error; err != nil { + t.Fatalf("Failed to retrieve test data: %v", err) + } + + // Verify Oracle data type handling + if retrieved.TextShort != testData.TextShort { + t.Fatalf("TextShort mismatch") + } + + if retrieved.NumberInt != testData.NumberInt { + t.Fatalf("NumberInt mismatch") + } + + if retrieved.BoolField != testData.BoolField { + t.Fatalf("BoolField mismatch") + } +} + +// This is failing +func TestOracleNullHandling(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + return + } + + type OracleNullModel struct { + ID uint `gorm:"primarykey"` + OptionalStr *string `gorm:"size:100"` + RequiredStr string `gorm:"size:100;not null"` + } + + if err := DB.Migrator().DropTable(&OracleNullModel{}); err != nil { + if !strings.Contains(err.Error(), "ORA-00942") { + t.Fatalf("Unexpected error dropping table: %v", err) + } + } + + if err := DB.AutoMigrate(&OracleNullModel{}); err != nil { + t.Fatalf("Failed to migrate null model, got error %v", err) + } + + // Test NULL behavior using direct SQL to verify Oracle's behavior + t.Run("empty_string_becomes_null", func(t *testing.T) { + // Insert record with empty string + if err := DB.Exec(`INSERT INTO "oracle_null_models" ("optional_str", "required_str") VALUES ('', 'test')`).Error; err != nil { + t.Fatalf("Failed to insert via SQL: %v", err) + } + + // Verify Oracle converted empty string to NULL + var count int64 + if err := DB.Raw(`SELECT COUNT(*) FROM "oracle_null_models" WHERE "optional_str" IS NULL`).Scan(&count).Error; err != nil { + t.Fatalf("Failed to count NULL records: %v", err) + } + if count == 0 { + t.Fatalf("Expected Oracle to convert empty string to NULL") + } + }) + + // Test GORM's handling of NULL values + t.Run("gorm_null_handling", func(t *testing.T) { + // Create record with nil pointer + model := &OracleNullModel{ + OptionalStr: nil, + RequiredStr: "required_value", + } + + if err := DB.Create(model).Error; err != nil { + t.Fatalf("Failed to create record with NULL: %v", err) + } + + // Retrieve and verify NULL handling + var retrieved OracleNullModel + if err := DB.First(&retrieved, model.ID).Error; err != nil { + t.Fatalf("Failed to retrieve record: %v", err) + } + + // The field should remain nil after round-trip + if retrieved.OptionalStr != nil { + t.Logf("Note: Oracle NULL handling may need adjustment in driver") + } + }) +} + +func TestOracleSequences(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + return + } + + type OracleSeqModel struct { + ID uint `gorm:"primarykey;autoIncrement"` + Name string `gorm:"size:100"` + } + + if err := DB.Migrator().DropTable(&OracleSeqModel{}); err != nil { + if !strings.Contains(err.Error(), "ORA-00942") { + t.Fatalf("Unexpected error dropping table: %v", err) + } + } + + if err := DB.AutoMigrate(&OracleSeqModel{}); err != nil { + t.Fatalf("Failed to migrate sequence model, got error %v", err) + } + + // Test Oracle sequence behavior for auto-increment + var records []OracleSeqModel + for i := 0; i < 3; i++ { + record := OracleSeqModel{Name: fmt.Sprintf("Test %d", i)} + if err := DB.Create(&record).Error; err != nil { + t.Fatalf("Failed to create record %d: %v", i, err) + } + records = append(records, record) + } + + // Verify auto-increment values + for i, record := range records { + if record.ID == 0 { + t.Fatalf("Record %d should have auto-generated ID", i) + } + if i > 0 && record.ID <= records[i-1].ID { + t.Fatalf("Record %d ID should be greater than previous record", i) + } + } + + // Test retrieval with proper quoting for Oracle + var retrieved []OracleSeqModel + // Use raw SQL to avoid identifier quoting issues in tests + if err := DB.Raw(`SELECT "id", "name" FROM "oracle_seq_models" ORDER BY "id"`).Scan(&retrieved).Error; err != nil { + t.Fatalf("Failed to retrieve records: %v", err) + } + + if len(retrieved) != 3 { + t.Fatalf("Expected 3 records, got %d", len(retrieved)) + } +} + +func TestOracleIndexes(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + return + } + + type OracleIndexModel struct { + ID uint `gorm:"primarykey"` + Name string `gorm:"size:100;index"` + Email string `gorm:"size:255;uniqueIndex"` + Category string `gorm:"size:50"` + } + + if err := DB.Migrator().DropTable(&OracleIndexModel{}); err != nil { + if !strings.Contains(err.Error(), "ORA-00942") { + t.Fatalf("Unexpected error dropping table: %v", err) + } + } + + if err := DB.AutoMigrate(&OracleIndexModel{}); err != nil { + t.Fatalf("Failed to migrate index model, got error %v", err) + } + + // Test that GORM-created indexes exist + if !DB.Migrator().HasIndex(&OracleIndexModel{}, "Name") { + t.Fatalf("Should have index on Name field") + } + + if !DB.Migrator().HasIndex(&OracleIndexModel{}, "Email") { + t.Fatalf("Should have unique index on Email field") + } + + // Test manual index creation using raw SQL (to avoid migrator issues) + if err := DB.Exec(`CREATE INDEX "idx_test_category" ON "oracle_index_models"("category")`).Error; err != nil { + t.Fatalf("Failed to create manual index: %v", err) + } + + // Verify index was created + var indexCount int64 + if err := DB.Raw(`SELECT COUNT(*) FROM USER_INDEXES WHERE TABLE_NAME = 'oracle_index_models' AND INDEX_NAME = 'idx_test_category'`).Scan(&indexCount).Error; err != nil { + t.Fatalf("Failed to check index existence: %v", err) + } + + if indexCount != 1 { + t.Fatalf("Index should exist after creation") + } +} + +func TestOracleConstraints(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + return + } + + type OracleConstraintModel struct { + ID uint `gorm:"primarykey"` + Email string `gorm:"unique;size:255"` + Username string `gorm:"size:50"` + } + + if err := DB.Migrator().DropTable(&OracleConstraintModel{}); err != nil { + if !strings.Contains(err.Error(), "ORA-00942") { + t.Fatalf("Unexpected error dropping table: %v", err) + } + } + + if err := DB.AutoMigrate(&OracleConstraintModel{}); err != nil { + t.Fatalf("Failed to migrate constraint model, got error %v", err) + } + + // Check that unique constraint was created + // Oracle generates constraint names automatically + var constraintCount int64 + if err := DB.Raw(`SELECT COUNT(*) FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'oracle_constraint_models' AND CONSTRAINT_TYPE = 'U'`).Scan(&constraintCount).Error; err != nil { + t.Fatalf("Failed to check constraints: %v", err) + } + + if constraintCount == 0 { + t.Fatalf("Should have unique constraints") + } + + // Test constraint violation + model1 := &OracleConstraintModel{Email: "test@example.com", Username: "user1"} + model2 := &OracleConstraintModel{Email: "test@example.com", Username: "user2"} // Same email + + if err := DB.Create(model1).Error; err != nil { + t.Fatalf("Failed to create first record: %v", err) + } + + if err := DB.Create(model2).Error; err == nil { + t.Fatalf("Expected unique constraint violation") + } else if !strings.Contains(err.Error(), "ORA-00001") { + t.Fatalf("Expected ORA-00001 unique constraint violation, got: %v", err) + } +} + +func TestOracleErrorHandling(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + return + } + + // Test Oracle-specific error codes + type TestModel struct { + ID uint `gorm:"primarykey"` + Name string `gorm:"size:100"` + } + + // Test dropping non-existent table + if err := DB.Migrator().DropTable(&TestModel{}); err != nil { + if !strings.Contains(err.Error(), "ORA-00942") { + t.Fatalf("Expected ORA-00942 for non-existent table, got %v", err) + } + } + + // Test table creation + if err := DB.AutoMigrate(&TestModel{}); err != nil { + t.Fatalf("Failed to create table: %v", err) + } + + // Test that duplicate creation doesn't error + if err := DB.AutoMigrate(&TestModel{}); err != nil { + t.Fatalf("Duplicate table creation should not error: %v", err) } } diff --git a/tests/multi_primary_keys_test.go b/tests/multi_primary_keys_test.go index 96d9db3..62947bc 100644 --- a/tests/multi_primary_keys_test.go +++ b/tests/multi_primary_keys_test.go @@ -368,3 +368,87 @@ func TestCompositePrimaryKeysAssociations(t *testing.T) { tests.AssertEqual(t, book, result) } + +func TestCompositePrimaryKeyMigration(t *testing.T) { + if name := DB.Dialector.Name(); name == "sqlite" || name == "sqlserver" { + t.Skip("skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment") + } + + if name := DB.Dialector.Name(); name == "postgres" || name == "oracle" { + stmt := gorm.Statement{DB: DB} + stmt.Parse(&Blog{}) + stmt.Schema.LookUpField("ID").Unique = true + stmt.Parse(&Tag{}) + stmt.Schema.LookUpField("ID").Unique = true + } + + DB.Migrator().DropTable(&Blog{}, &Tag{}, "blog_tags", "locale_blog_tags", "shared_blog_tags") + if err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil { + t.Fatalf("Failed to auto migrate, got error: %v", err) + } +} + +func TestCompositePrimaryKeyDeletion(t *testing.T) { + if name := DB.Dialector.Name(); name == "sqlite" || name == "sqlserver" { + t.Skip("skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment") + } + + if name := DB.Dialector.Name(); name == "postgres" || name == "oracle" { + stmt := gorm.Statement{DB: DB} + stmt.Parse(&Blog{}) + stmt.Schema.LookUpField("ID").Unique = true + stmt.Parse(&Tag{}) + stmt.Schema.LookUpField("ID").Unique = true + } + + DB.Migrator().DropTable(&Blog{}, &Tag{}) + if err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil { + t.Fatalf("Failed to auto migrate, got error: %v", err) + } + + // Create test data with composite primary keys + blog := Blog{ID: 1, Locale: "en", Subject: "Test Subject", Body: "Test Body"} + tag := Tag{ID: 1, Locale: "en", Value: "Test Tag"} + + if err := DB.Create(&blog).Error; err != nil { + t.Fatalf("Failed to create blog: %v", err) + } + + if err := DB.Create(&tag).Error; err != nil { + t.Fatalf("Failed to create tag: %v", err) + } + + // Test deletion + if err := DB.Delete(&blog).Error; err != nil { + t.Fatalf("Failed to delete blog: %v", err) + } + + if err := DB.Delete(&tag).Error; err != nil { + t.Fatalf("Failed to delete tag: %v", err) + } +} + +func TestCompositePrimaryKeyConstraints(t *testing.T) { + if name := DB.Dialector.Name(); name == "sqlite" || name == "sqlserver" { + t.Skip("skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment") + } + + if name := DB.Dialector.Name(); name == "postgres" || name == "oracle" { + stmt := gorm.Statement{DB: DB} + stmt.Parse(&Blog{}) + stmt.Schema.LookUpField("ID").Unique = true + stmt.Parse(&Tag{}) + stmt.Schema.LookUpField("ID").Unique = true + } + + DB.Migrator().DropTable(&Blog{}, &Tag{}, "blog_tags", "locale_blog_tags", "shared_blog_tags") + if err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil { + t.Fatalf("Failed to auto migrate, got error: %v", err) + } + + // Test with explicit unique constraints for Oracle compatibility + if DB.Dialector.Name() == "oracle" { + DB.Exec("ALTER TABLE blogs ADD CONSTRAINT uk_blogs_id UNIQUE (id)") + DB.Exec("ALTER TABLE tags ADD CONSTRAINT uk_tags_id UNIQUE (id)") + } +} diff --git a/tests/named_polymorphic_test.go b/tests/named_polymorphic_test.go index 2b54ea2..dd8b0df 100644 --- a/tests/named_polymorphic_test.go +++ b/tests/named_polymorphic_test.go @@ -39,13 +39,17 @@ package tests import ( + "database/sql" + "fmt" "testing" + "time" . "github.com/oracle-samples/gorm-oracle/tests/utils" + "gorm.io/gorm" ) type Hamster struct { - Id int + ID int Name string PreferredToy Toy `gorm:"polymorphic:Owner;polymorphicValue:hamster_preferred"` OtherToy Toy `gorm:"polymorphic:Owner;polymorphicValue:hamster_other"` @@ -59,7 +63,7 @@ func TestNamedPolymorphic(t *testing.T) { DB.Save(&hamster) hamster2 := Hamster{} - DB.Preload("PreferredToy").Preload("OtherToy").Find(&hamster2, hamster.Id) + DB.Preload("PreferredToy").Preload("OtherToy").Find(&hamster2, hamster.ID) if hamster2.PreferredToy.ID != hamster.PreferredToy.ID || hamster2.PreferredToy.Name != hamster.PreferredToy.Name { t.Errorf("Hamster's preferred toy failed to preload") @@ -183,3 +187,291 @@ func TestNamedPolymorphic(t *testing.T) { t.Errorf("Hamster's other toy should be cleared with Clear") } } + +func TestOracleCRUDOperations(t *testing.T) { + DB.Migrator().DropTable(&User{}, &Account{}, &Pet{}) + if err := DB.AutoMigrate(&User{}, &Account{}, &Pet{}); err != nil { + t.Fatalf("Failed to auto migrate: %v", err) + } + + // Test auto-increment behavior with IDENTITY columns + user := User{ + Name: "Oracle CRUD User", + Age: 30, + Birthday: &time.Time{}, + Active: true, + } + + // Create user - should auto-generate ID + if err := DB.Create(&user).Error; err != nil { + t.Fatalf("Failed to create user: %v", err) + } + + if user.ID == 0 { + t.Errorf("Expected auto-generated ID, got 0") + } + + // Create account with foreign key reference + account := Account{ + UserID: sql.NullInt64{Int64: int64(user.ID), Valid: true}, + AccountNumber: "ACC-001", + } + + if err := DB.Create(&account).Error; err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + if account.ID == 0 { + t.Errorf("Expected auto-generated account ID, got 0") + } + + // Create pet with foreign key reference + pet := Pet{ + UserID: &user.ID, + Name: "Oracle Pet", + } + + if err := DB.Create(&pet).Error; err != nil { + t.Fatalf("Failed to create pet: %v", err) + } + + if pet.ID == 0 { + t.Errorf("Expected auto-generated pet ID, got 0") + } + + // Test UPDATE operations + user.Name = "Updated Oracle User" + user.Age = 31 + if err := DB.Save(&user).Error; err != nil { + t.Fatalf("Failed to update user: %v", err) + } + + // Verify update + var updatedUser User + if err := DB.First(&updatedUser, user.ID).Error; err != nil { + t.Fatalf("Failed to retrieve updated user: %v", err) + } + + if updatedUser.Name != "Updated Oracle User" || updatedUser.Age != 31 { + t.Errorf("User update failed: expected name 'Updated Oracle User' and age 31, got name '%s' and age %d", + updatedUser.Name, updatedUser.Age) + } + + // Test reading back with associations + var retrievedUser User + if err := DB.Preload("Account").Preload("Pets").First(&retrievedUser, user.ID).Error; err != nil { + t.Fatalf("Failed to retrieve user with associations: %v", err) + } + + if retrievedUser.Account.AccountNumber != "ACC-001" { + t.Errorf("Expected account number ACC-001, got %s", retrievedUser.Account.AccountNumber) + } + + if len(retrievedUser.Pets) != 1 || retrievedUser.Pets[0].Name != "Oracle Pet" { + t.Errorf("Expected 1 pet named 'Oracle Pet', got %d pets", len(retrievedUser.Pets)) + } + + // Test DELETE operations + if err := DB.Delete(&pet).Error; err != nil { + t.Fatalf("Failed to delete pet: %v", err) + } + + // Verify deletion + var petCount int64 + DB.Model(&Pet{}).Where(`"id" = ?`, pet.ID).Count(&petCount) + if petCount != 0 { + t.Errorf("Pet should be deleted, but found %d records", petCount) + } +} + +func TestOracleAdvancedOperations(t *testing.T) { + DB.Migrator().DropTable(&Company{}, &User{}, &Language{}, "user_speak") + if err := DB.AutoMigrate(&Company{}, &User{}, &Language{}); err != nil { + t.Fatalf("Failed to auto migrate: %v", err) + } + + // Test transaction handling + err := DB.Transaction(func(tx *gorm.DB) error { + // Create company + company := Company{ + Name: "Oracle Advanced Corp", + } + if err := tx.Create(&company).Error; err != nil { + return err + } + + // Create languages + languages := []Language{ + {Code: "EN", Name: "English"}, + {Code: "ES", Name: "Spanish"}, + {Code: "FR", Name: "French"}, + {Code: "DE", Name: "German"}, + } + if err := tx.Create(&languages).Error; err != nil { + return err + } + + // Create users with company relationship + users := []User{ + {Name: "John Doe", Age: 30, CompanyID: &company.ID, Active: true}, + {Name: "Jane Smith", Age: 28, CompanyID: &company.ID, Active: true}, + {Name: "Bob Wilson", Age: 35, CompanyID: &company.ID, Active: false}, + } + if err := tx.Create(&users).Error; err != nil { + return err + } + + // Test many-to-many associations within transaction + if err := tx.Model(&users[0]).Association("Languages").Append(&languages[0], &languages[1]); err != nil { + return err + } + + if err := tx.Model(&users[1]).Association("Languages").Append(&languages[1], &languages[2]); err != nil { + return err + } + + return nil + }) + + if err != nil { + t.Fatalf("Transaction failed: %v", err) + } + + // Verify transaction results + var userCount int64 + DB.Model(&User{}).Count(&userCount) + if userCount != 3 { + t.Errorf("Expected 3 users after transaction, got %d", userCount) + } + + var languageCount int64 + DB.Model(&Language{}).Count(&languageCount) + if languageCount != 4 { + t.Errorf("Expected 4 languages after transaction, got %d", languageCount) + } + + // Test batch operations with Oracle-specific features + batchUsers := make([]User, 20) + for i := 0; i < 20; i++ { + batchUsers[i] = User{ + Name: fmt.Sprintf("Batch User %d", i+1), + Age: uint(20 + (i % 50)), + Active: i%2 == 0, + } + } + + // Test batch insert with Oracle + if err := DB.CreateInBatches(&batchUsers, 5).Error; err != nil { + t.Fatalf("Failed to create users in batches: %v", err) + } + + // Verify batch insert + DB.Model(&User{}).Count(&userCount) + if userCount != 23 { // 3 from transaction + 20 from batch + t.Errorf("Expected 23 users after batch insert, got %d", userCount) + } + + // Test bulk update using GORM methods with Oracle-compatible expressions + result := DB.Model(&User{}).Where(&User{Active: true}).Update("age", gorm.Expr(`"age" + ?`, 1)) + if result.Error != nil { + t.Fatalf("Failed to bulk update: %v", result.Error) + } + + if result.RowsAffected == 0 { + t.Errorf("Expected some rows to be affected by bulk update") + } + + // Test complex query using GORM joins instead of raw SQL + var activeUsers []User + if err := DB.Joins("Company").Where(&User{Active: true}).Where(`"age" > ?`, 25).Find(&activeUsers).Error; err != nil { + t.Fatalf("Failed to execute join query: %v", err) + } + + // Test transaction rollback scenario + err = DB.Transaction(func(tx *gorm.DB) error { + // Create a user + user := User{Name: "Rollback Test Advanced", Age: 99, Active: true} + if err := tx.Create(&user).Error; err != nil { + return err + } + + // Force rollback by returning an error + return fmt.Errorf("forced rollback for testing") + }) + + if err == nil { + t.Errorf("Expected transaction to fail and rollback") + } + + // Verify rollback worked + var rollbackUser User + result = DB.Where(`"name" = ?`, "Rollback Test Advanced").First(&rollbackUser) + if result.Error == nil { + t.Errorf("Expected user to not exist after rollback, but found: %+v", rollbackUser) + } + + // Test Oracle pagination using GORM methods + var paginatedUsers []User + if err := DB.Offset(5).Limit(3).Find(&paginatedUsers).Error; err != nil { + t.Fatalf("Failed to paginate: %v", err) + } + + if len(paginatedUsers) != 3 { + t.Errorf("Expected 3 users from pagination, got %d", len(paginatedUsers)) + } + + // Test Oracle-specific date operations using GORM with quoted identifiers + var todayUsers []User + if err := DB.Where(`"created_at" >= ?`, time.Now().Truncate(24*time.Hour)).Find(&todayUsers).Error; err != nil { + t.Fatalf("Failed to query with date functions: %v", err) + } + + // Test case-insensitive search using GORM with quoted identifiers + var searchUsers []User + if err := DB.Where(`UPPER("name") LIKE UPPER(?)`, "%doe%").Find(&searchUsers).Error; err != nil { + t.Fatalf("Failed to perform case-insensitive search: %v", err) + } + + // Test Oracle-specific features that require raw SQL (minimal usage) + var currentTime time.Time + if err := DB.Raw("SELECT SYSDATE FROM DUAL").Scan(¤tTime).Error; err != nil { + t.Fatalf("Failed to query Oracle DUAL table: %v", err) + } + + if currentTime.IsZero() { + t.Errorf("Expected current time from Oracle SYSDATE, got zero time") + } + + // Test Oracle sequence behavior + var maxID uint + if err := DB.Model(&User{}).Select(`MAX("id")`).Scan(&maxID).Error; err != nil { + t.Fatalf("Failed to get max ID: %v", err) + } + + // Create one more user to test sequence increment + newUser := User{Name: "Sequence Test", Age: 25, Active: true} + if err := DB.Create(&newUser).Error; err != nil { + t.Fatalf("Failed to create sequence test user: %v", err) + } + + if newUser.ID <= maxID { + t.Errorf("Expected new user ID to be greater than %d, got %d", maxID, newUser.ID) + } + + // Test Oracle association operations + var userWithLanguages User + if err := DB.Preload("Languages").Where(`"name" = ?`, "John Doe").First(&userWithLanguages).Error; err != nil { + t.Fatalf("Failed to preload user languages: %v", err) + } + + if len(userWithLanguages.Languages) != 2 { + t.Errorf("Expected user to have 2 languages, got %d", len(userWithLanguages.Languages)) + } + + // Test Oracle constraint behavior + duplicateCompany := Company{Name: "Oracle Advanced Corp"} + if err := DB.Create(&duplicateCompany).Error; err != nil { + // This should succeed since Company doesn't have unique constraints in the model + // This tests that GORM handles duplicate data as expected + } +} From b5359e1ce124f6e680057aed816f37dfb82a7af5 Mon Sep 17 00:00:00 2001 From: Saumil Diwaker Date: Sat, 13 Sep 2025 10:37:20 +0530 Subject: [PATCH 2/2] Updated as per comments --- tests/migrate_test.go | 1 - tests/named_polymorphic_test.go | 27 ++++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/migrate_test.go b/tests/migrate_test.go index 6082582..cf819c9 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -1837,7 +1837,6 @@ func TestOracleDataTypes(t *testing.T) { } } -// This is failing func TestOracleNullHandling(t *testing.T) { if DB.Dialector.Name() != "oracle" { return diff --git a/tests/named_polymorphic_test.go b/tests/named_polymorphic_test.go index dd8b0df..9317c2a 100644 --- a/tests/named_polymorphic_test.go +++ b/tests/named_polymorphic_test.go @@ -188,7 +188,7 @@ func TestNamedPolymorphic(t *testing.T) { } } -func TestOracleCRUDOperations(t *testing.T) { +func TestCRUDOperations(t *testing.T) { DB.Migrator().DropTable(&User{}, &Account{}, &Pet{}) if err := DB.AutoMigrate(&User{}, &Account{}, &Pet{}); err != nil { t.Fatalf("Failed to auto migrate: %v", err) @@ -284,7 +284,7 @@ func TestOracleCRUDOperations(t *testing.T) { } } -func TestOracleAdvancedOperations(t *testing.T) { +func TestAdvancedOperations(t *testing.T) { DB.Migrator().DropTable(&Company{}, &User{}, &Language{}, "user_speak") if err := DB.AutoMigrate(&Company{}, &User{}, &Language{}); err != nil { t.Fatalf("Failed to auto migrate: %v", err) @@ -410,7 +410,7 @@ func TestOracleAdvancedOperations(t *testing.T) { t.Errorf("Expected user to not exist after rollback, but found: %+v", rollbackUser) } - // Test Oracle pagination using GORM methods + // Test pagination using GORM methods var paginatedUsers []User if err := DB.Offset(5).Limit(3).Find(&paginatedUsers).Error; err != nil { t.Fatalf("Failed to paginate: %v", err) @@ -420,7 +420,7 @@ func TestOracleAdvancedOperations(t *testing.T) { t.Errorf("Expected 3 users from pagination, got %d", len(paginatedUsers)) } - // Test Oracle-specific date operations using GORM with quoted identifiers + // Test-specific date operations using GORM with quoted identifiers var todayUsers []User if err := DB.Where(`"created_at" >= ?`, time.Now().Truncate(24*time.Hour)).Find(&todayUsers).Error; err != nil { t.Fatalf("Failed to query with date functions: %v", err) @@ -432,7 +432,7 @@ func TestOracleAdvancedOperations(t *testing.T) { t.Fatalf("Failed to perform case-insensitive search: %v", err) } - // Test Oracle-specific features that require raw SQL (minimal usage) + // Test-specific features that require raw SQL (minimal usage) var currentTime time.Time if err := DB.Raw("SELECT SYSDATE FROM DUAL").Scan(¤tTime).Error; err != nil { t.Fatalf("Failed to query Oracle DUAL table: %v", err) @@ -442,7 +442,7 @@ func TestOracleAdvancedOperations(t *testing.T) { t.Errorf("Expected current time from Oracle SYSDATE, got zero time") } - // Test Oracle sequence behavior + // Test sequence behavior var maxID uint if err := DB.Model(&User{}).Select(`MAX("id")`).Scan(&maxID).Error; err != nil { t.Fatalf("Failed to get max ID: %v", err) @@ -458,7 +458,7 @@ func TestOracleAdvancedOperations(t *testing.T) { t.Errorf("Expected new user ID to be greater than %d, got %d", maxID, newUser.ID) } - // Test Oracle association operations + // Test association operations var userWithLanguages User if err := DB.Preload("Languages").Where(`"name" = ?`, "John Doe").First(&userWithLanguages).Error; err != nil { t.Fatalf("Failed to preload user languages: %v", err) @@ -468,10 +468,15 @@ func TestOracleAdvancedOperations(t *testing.T) { t.Errorf("Expected user to have 2 languages, got %d", len(userWithLanguages.Languages)) } - // Test Oracle constraint behavior + // Test constraint behavior duplicateCompany := Company{Name: "Oracle Advanced Corp"} - if err := DB.Create(&duplicateCompany).Error; err != nil { - // This should succeed since Company doesn't have unique constraints in the model - // This tests that GORM handles duplicate data as expected + err = DB.Create(&duplicateCompany).Error + if err != nil { + t.Fatalf("Failed to create duplicate company: %v", err) + } + var companyCount int64 + DB.Model(&Company{}).Where(`"name" = ?`, "Oracle Advanced Corp").Count(&companyCount) + if companyCount < 2 { + t.Errorf("Expected at least 2 companies named 'Oracle Advanced Corp', got %d", companyCount) } }