@@ -40,13 +40,15 @@ package tests
4040
4141import (
4242 "database/sql"
43+ "errors"
4344 "testing"
4445
4546 "time"
4647
4748 . "github.com/oracle-samples/gorm-oracle/tests/utils"
4849
4950 "gorm.io/gorm"
51+ "gorm.io/gorm/clause"
5052 "gorm.io/gorm/utils/tests"
5153)
5254
@@ -128,6 +130,87 @@ func TestUpdateHasOne(t *testing.T) {
128130 CheckPetSkipUpdatedAt (t , pet4 , pet )
129131 })
130132
133+ t .Run ("ReplaceAssociation" , func (t * testing.T ) {
134+ user := * GetUser ("replace-has-one" , Config {})
135+
136+ if err := DB .Create (& user ).Error ; err != nil {
137+ t .Fatalf ("errors happened when create user: %v" , err )
138+ }
139+
140+ acc1 := Account {AccountNumber : "first-account" }
141+ user .Account = acc1
142+
143+ if err := DB .Save (& user ).Error ; err != nil {
144+ t .Fatalf ("errors happened when saving user with first account: %v" , err )
145+ }
146+
147+ acc2 := Account {AccountNumber : "second-account" }
148+ user .Account = acc2
149+ if err := DB .Session (& gorm.Session {FullSaveAssociations : true }).Save (& user ).Error ; err != nil {
150+ t .Fatalf ("errors happened when replacing association: %v" , err )
151+ }
152+
153+ var result User
154+ DB .Preload ("Account" ).First (& result , user .ID )
155+ if result .Account .AccountNumber != "second-account" {
156+ t .Fatalf ("expected replaced account to have AccountNumber 'second-account', got %v" , result .Account .AccountNumber )
157+ }
158+ })
159+
160+ t .Run ("ClearHasOneAssociation" , func (t * testing.T ) {
161+ user := * GetUser ("nullify-has-one" , Config {})
162+
163+ if err := DB .Create (& user ).Error ; err != nil {
164+ t .Fatalf ("errors happened when create user: %v" , err )
165+ }
166+
167+ user .Account = Account {AccountNumber : "to-be-nullified" }
168+ if err := DB .Save (& user ).Error ; err != nil {
169+ t .Fatalf ("errors happened when saving user: %v" , err )
170+ }
171+
172+ DB .Model (& user ).Association ("Account" ).Clear ()
173+
174+ var result User
175+ DB .Preload ("Account" ).First (& result , user .ID )
176+ if result .Account .AccountNumber != "" {
177+ t .Fatalf ("expected account to be nullified/empty, got %v" , result .Account .AccountNumber )
178+ }
179+ })
180+
181+ t .Run ("ClearPolymorphicAssociation" , func (t * testing.T ) {
182+ pet := Pet {Name : "clear-poly" }
183+ pet .Toy = Toy {Name : "polytoy" }
184+ DB .Create (& pet )
185+
186+ DB .Model (& pet ).Association ("Toy" ).Clear ()
187+
188+ var pet2 Pet
189+ DB .Preload ("Toy" ).First (& pet2 , pet .ID )
190+ if pet2 .Toy .Name != "" {
191+ t .Fatalf ("expected Toy cleared, got %v" , pet2 .Toy .Name )
192+ }
193+ })
194+
195+ t .Run ("UpdateWithoutAssociation" , func (t * testing.T ) {
196+ user := * GetUser ("no-assoc-update" , Config {})
197+ if err := DB .Create (& user ).Error ; err != nil {
198+ t .Fatalf ("errors happened when create user: %v" , err )
199+ }
200+ newName := user .Name + "-updated"
201+ if err := DB .Model (& user ).Update ("name" , newName ).Error ; err != nil {
202+ t .Fatalf ("errors happened when updating only parent: %v" , err )
203+ }
204+ var result User
205+ DB .Preload ("Account" ).First (& result , user .ID )
206+ if result .Name != newName {
207+ t .Fatalf ("user name not updated as expected" )
208+ }
209+ if result .Account .ID != 0 {
210+ t .Fatalf ("expected no Account associated, got ID %v" , result .Account .ID )
211+ }
212+ })
213+
131214 t .Run ("Restriction" , func (t * testing.T ) {
132215 type CustomizeAccount struct {
133216 gorm.Model
@@ -175,4 +258,201 @@ func TestUpdateHasOne(t *testing.T) {
175258 tests .AssertEqual (t , account2 .Number , number )
176259 tests .AssertEqual (t , account2 .Number2 , cusUser .Account .Number2 )
177260 })
261+
262+ t .Run ("AssociationWithoutPreload" , func (t * testing.T ) {
263+ user := * GetUser ("no-preload" , Config {})
264+ user .Account = Account {AccountNumber : "np-account" }
265+ DB .Create (& user )
266+
267+ var result User
268+ DB .First (& result , user .ID ) // no preload
269+ if result .Account .AccountNumber != "" {
270+ t .Fatalf ("expected Account field empty without preload, got %v" , result .Account .AccountNumber )
271+ }
272+
273+ var acc Account
274+ DB .First (& acc , "\" user_id\" = ?" , user .ID )
275+ if acc .AccountNumber != "np-account" {
276+ t .Fatalf ("account not found as expected" )
277+ }
278+ })
279+
280+ t .Run ("SkipFullSaveAssociations" , func (t * testing.T ) {
281+ user := * GetUser ("skip-fsa" , Config {})
282+ user .Account = Account {AccountNumber : "skipfsa" }
283+ DB .Create (& user )
284+
285+ user .Account .AccountNumber = "should-not-update"
286+ if err := DB .Session (& gorm.Session {FullSaveAssociations : false }).Save (& user ).Error ; err != nil {
287+ t .Fatalf ("error saving with FSA false: %v" , err )
288+ }
289+
290+ var acc Account
291+ DB .First (& acc , "\" user_id\" = ?" , user .ID )
292+ if acc .AccountNumber != "skipfsa" {
293+ t .Fatalf ("account should not have updated, got %v" , acc .AccountNumber )
294+ }
295+ })
296+
297+ t .Run ("HasOneZeroForeignKey" , func (t * testing.T ) {
298+ now := time .Now ()
299+ user := User {Name : "zero-value-clear" , Age : 18 , Birthday : & now }
300+ DB .Create (& user )
301+
302+ account := Account {AccountNumber : "to-clear" , UserID : sql.NullInt64 {Int64 : int64 (user .ID ), Valid : true }}
303+ DB .Create (& account )
304+
305+ account .UserID = sql.NullInt64 {Int64 : 0 , Valid : false }
306+ DB .Model (& account ).Select ("UserID" ).Updates (account )
307+
308+ var result User
309+ DB .Preload ("Account" ).First (& result , user .ID )
310+ if result .Account .AccountNumber != "" {
311+ t .Fatalf ("expected account cleared, got %v" , result .Account .AccountNumber )
312+ }
313+ })
314+
315+ t .Run ("PolymorphicZeroForeignKey" , func (t * testing.T ) {
316+ pet := Pet {Name : "poly-zero" }
317+ pet .Toy = Toy {Name : "polytoy-zero" }
318+ DB .Create (& pet )
319+
320+ pet .Toy .OwnerID = ""
321+ DB .Model (& pet .Toy ).Select ("OwnerID" ).Updates (& pet .Toy )
322+
323+ var pet2 Pet
324+ DB .Preload ("Toy" ).First (& pet2 , pet .ID )
325+ if pet2 .Toy .Name != "" {
326+ t .Fatalf ("expected polymorphic association cleared, got %v" , pet2 .Toy .Name )
327+ }
328+ })
329+
330+ t .Run ("InvalidForeignKey" , func (t * testing.T ) {
331+ acc := Account {AccountNumber : "badfk" , UserID : sql.NullInt64 {Int64 : 99999999 , Valid : true }}
332+ err := DB .Create (& acc ).Error
333+ if err == nil {
334+ t .Fatalf ("expected foreign key constraint error, got nil" )
335+ }
336+ })
337+
338+ t .Run ("UpdateWithSelectOmit" , func (t * testing.T ) {
339+ user := * GetUser ("select-omit" , Config {})
340+ user .Account = Account {AccountNumber : "selomit" }
341+ DB .Create (& user )
342+
343+ user .Name = "selomit-updated"
344+ user .Account .AccountNumber = "selomit-updated"
345+ if err := DB .Select ("Name" ).Omit ("Account" ).Save (& user ).Error ; err != nil {
346+ t .Fatalf ("error on select/omit: %v" , err )
347+ }
348+
349+ var acc Account
350+ DB .First (& acc , "\" user_id\" = ?" , user .ID )
351+ if acc .AccountNumber != "selomit" {
352+ t .Fatalf ("account should not update with Omit(Account), got %v" , acc .AccountNumber )
353+ }
354+ })
355+
356+ t .Run ("NestedUpdate" , func (t * testing.T ) {
357+ user := * GetUser ("nested-update" , Config {})
358+ user .Account = Account {AccountNumber : "nested" }
359+ DB .Create (& user )
360+
361+ user .Name = "nested-updated"
362+ user .Account .AccountNumber = "nested-updated"
363+ if err := DB .Session (& gorm.Session {FullSaveAssociations : true }).Updates (& user ).Error ; err != nil {
364+ t .Fatalf ("nested update failed: %v" , err )
365+ }
366+
367+ var result User
368+ DB .Preload ("Account" ).First (& result , user .ID )
369+ if result .Name != "nested-updated" || result .Account .AccountNumber != "nested-updated" {
370+ t .Fatalf ("nested update didn't apply: %v / %v" , result .Name , result .Account .AccountNumber )
371+ }
372+ })
373+
374+ t .Run ("EmptyStructNoFullSave" , func (t * testing.T ) {
375+ user := * GetUser ("empty-nofsa" , Config {})
376+ user .Account = Account {AccountNumber : "keep" }
377+ DB .Create (& user )
378+
379+ user .Account = Account {}
380+ if err := DB .Save (& user ).Error ; err != nil {
381+ t .Fatalf ("save failed: %v" , err )
382+ }
383+
384+ var result User
385+ DB .Preload ("Account" ).First (& result , user .ID )
386+ if result .Account .AccountNumber != "keep" {
387+ t .Fatalf ("account should not be cleared without FullSaveAssociations" )
388+ }
389+ })
390+
391+ t .Run ("DeleteParentCascade" , func (t * testing.T ) {
392+ type AccountCascadeDelete struct {
393+ gorm.Model
394+ AccountNumber string
395+ UserID uint
396+ }
397+
398+ type UserCascadeDelete struct {
399+ gorm.Model
400+ Name string
401+ Account AccountCascadeDelete `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE;"`
402+ }
403+
404+ DB .Migrator ().DropTable (& AccountCascadeDelete {}, & UserCascadeDelete {})
405+ if err := DB .AutoMigrate (& UserCascadeDelete {}, & AccountCascadeDelete {}); err != nil {
406+ t .Fatalf ("failed to migrate: %v" , err )
407+ }
408+
409+ user := UserCascadeDelete {
410+ Name : "delete-parent" ,
411+ Account : AccountCascadeDelete {
412+ AccountNumber : "cascade" ,
413+ },
414+ }
415+
416+ if err := DB .Create (& user ).Error ; err != nil {
417+ t .Fatalf ("failed to create user: %v" , err )
418+ }
419+
420+ if err := DB .Unscoped ().Delete (& user ).Error ; err != nil {
421+ t .Fatalf ("delete parent failed: %v" , err )
422+ }
423+
424+ var acc AccountCascadeDelete
425+ err := DB .First (& acc , "\" user_id\" = ?" , user .ID ).Error
426+ if ! errors .Is (err , gorm .ErrRecordNotFound ) {
427+ t .Fatalf ("expected account deleted, got %v" , acc )
428+ }
429+ })
430+
431+ t .Run ("OmitAllAssociations" , func (t * testing.T ) {
432+ user := * GetUser ("omit-assoc" , Config {})
433+ user .Account = Account {AccountNumber : "original-child" }
434+ if err := DB .Create (& user ).Error ; err != nil {
435+ t .Fatalf ("failed to create user: %v" , err )
436+ }
437+
438+ newName := "parent-updated"
439+ user .Name = newName
440+ user .Account .AccountNumber = "child-updated"
441+
442+ if err := DB .Model (& user ).Omit (clause .Associations ).Updates (user ).Error ; err != nil {
443+ t .Fatalf ("update with omit associations failed: %v" , err )
444+ }
445+
446+ var result User
447+ DB .Preload ("Account" ).First (& result , user .ID )
448+
449+ if result .Name != newName {
450+ t .Fatalf ("expected parent name updated to %v, got %v" , newName , result .Name )
451+ }
452+
453+ if result .Account .AccountNumber != "original-child" {
454+ t .Fatalf ("expected child to remain unchanged, got %v" , result .Account .AccountNumber )
455+ }
456+ })
457+
178458}
0 commit comments