@@ -40,13 +40,15 @@ package tests
40
40
41
41
import (
42
42
"database/sql"
43
+ "errors"
43
44
"testing"
44
45
45
46
"time"
46
47
47
48
. "github.com/oracle-samples/gorm-oracle/tests/utils"
48
49
49
50
"gorm.io/gorm"
51
+ "gorm.io/gorm/clause"
50
52
"gorm.io/gorm/utils/tests"
51
53
)
52
54
@@ -128,6 +130,87 @@ func TestUpdateHasOne(t *testing.T) {
128
130
CheckPetSkipUpdatedAt (t , pet4 , pet )
129
131
})
130
132
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
+
131
214
t .Run ("Restriction" , func (t * testing.T ) {
132
215
type CustomizeAccount struct {
133
216
gorm.Model
@@ -175,4 +258,201 @@ func TestUpdateHasOne(t *testing.T) {
175
258
tests .AssertEqual (t , account2 .Number , number )
176
259
tests .AssertEqual (t , account2 .Number2 , cusUser .Account .Number2 )
177
260
})
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
+
178
458
}
0 commit comments