@@ -14335,3 +14335,301 @@ func TestValidateFn(t *testing.T) {
14335
14335
Equal (t , fe .Tag (), "validateFn" )
14336
14336
})
14337
14337
}
14338
+ func TestMapStructBasicValidation (t * testing.T ) {
14339
+ // Tests basic validation of a map with struct values
14340
+ type Inner struct {
14341
+ Value string `validate:"max=5"`
14342
+ }
14343
+ type Outer struct {
14344
+ Data map [string ]Inner `validate:"dive"`
14345
+ }
14346
+ obj := Outer {
14347
+ Data : map [string ]Inner {
14348
+ "key1" : {Value : "exceeds" }, // Should fail because Value is longer than 5 characters
14349
+ },
14350
+ }
14351
+ validate := New ()
14352
+ errs := validate .Struct (obj )
14353
+ NotEqual (t , errs , nil )
14354
+ }
14355
+ func TestMapStructPointerValidation (t * testing.T ) {
14356
+ // Tests validation of a map with pointer to struct values
14357
+ type Inner struct {
14358
+ Count int `validate:"gt=10"`
14359
+ }
14360
+ type Outer struct {
14361
+ Items map [string ]* Inner `validate:"dive"`
14362
+ }
14363
+ obj := Outer {
14364
+ Items : map [string ]* Inner {
14365
+ "a" : {Count : 5 },
14366
+ },
14367
+ }
14368
+ validate := New ()
14369
+ errs := validate .Struct (obj )
14370
+ NotEqual (t , errs , nil )
14371
+ }
14372
+ func TestMapStructWithKeyValidationOnly (t * testing.T ) {
14373
+ // Tests validation of a map with struct values, focusing on key validation
14374
+ type Inner struct {
14375
+ Name string `validate:"required"`
14376
+ }
14377
+ type Outer struct {
14378
+ Things map [string ]Inner `validate:"dive,keys,min=3,endkeys"`
14379
+ }
14380
+ obj := Outer {
14381
+ Things : map [string ]Inner {
14382
+ "ab" : {Name : "valid" }, //Should fail because key is too short
14383
+ },
14384
+ }
14385
+ validate := New ()
14386
+ errs := validate .Struct (obj )
14387
+ NotEqual (t , errs , nil )
14388
+ }
14389
+ func TestMapStructWithKeyAndValueValidation (t * testing.T ) {
14390
+ // Tests validation of a map with struct values, validating both keys and values
14391
+ type Inner struct {
14392
+ Name string `validate:"min=3"`
14393
+ }
14394
+ type Outer struct {
14395
+ Stuff map [string ]Inner `validate:"dive,keys,min=2,endkeys"`
14396
+ }
14397
+ obj := Outer {
14398
+ Stuff : map [string ]Inner {
14399
+ "ok" : {Name : "xy" },
14400
+ "bad" : {Name : "valid" },
14401
+ },
14402
+ }
14403
+ validate := New ()
14404
+ errs := validate .Struct (obj )
14405
+ NotEqual (t , errs , nil )
14406
+ }
14407
+ func TestMapPointerStructWithNilValue (t * testing.T ) {
14408
+ // Tests validation of a map with pointer to struct values where value is nil
14409
+ type Inner struct {
14410
+ Count int `validate:"min=1"`
14411
+ }
14412
+ type Outer struct {
14413
+ Items map [string ]* Inner `validate:"dive"`
14414
+ }
14415
+ obj := Outer {
14416
+ Items : map [string ]* Inner {
14417
+ "x" : nil ,
14418
+ },
14419
+ }
14420
+ validate := New ()
14421
+ errs := validate .Struct (obj )
14422
+ Equal (t , errs , nil )
14423
+ }
14424
+ func TestThreeLevelNestedStructs (t * testing.T ) {
14425
+ // Tests validation of three levels of nested structs with map keys and values
14426
+ type Level3 struct {
14427
+ Code string `validate:"len=3"`
14428
+ }
14429
+
14430
+ type Level2 struct {
14431
+ Items map [string ]Level3 `validate:"dive,keys,required,endkeys"`
14432
+ }
14433
+
14434
+ type Level1 struct {
14435
+ Levels map [string ]Level2 `validate:"dive,keys,required,endkeys"`
14436
+ }
14437
+
14438
+ validate := New ()
14439
+
14440
+ // Valid case: all Level3.Code are exactly 3 chars
14441
+ valid := Level1 {Levels : map [string ]Level2 {
14442
+ "first" : {Items : map [string ]Level3 {
14443
+ "item1" : {Code : "abc" },
14444
+ "item2" : {Code : "xyz" },
14445
+ },
14446
+ },
14447
+ },
14448
+ }
14449
+
14450
+ errs := validate .Struct (valid )
14451
+ Equal (t , errs , nil )
14452
+
14453
+ // Invalid case: one Level3.Code is wrong length
14454
+ invalid := Level1 {Levels : map [string ]Level2 {
14455
+ "first" : {Items : map [string ]Level3 {
14456
+ "item1" : {Code : "abcd" }, // Should fail here because length is 4
14457
+ "item2" : {Code : "xyz" },
14458
+ },
14459
+ },
14460
+ },
14461
+ }
14462
+
14463
+ errs = validate .Struct (invalid )
14464
+ NotEqual (t , errs , nil )
14465
+ }
14466
+ func TestMapStructFallbackWithKeysOnly (t * testing.T ) {
14467
+ // Tests fallback behavior when validating map keys and struct values
14468
+ type Inner struct {
14469
+ Value string `validate:"max=3"`
14470
+ }
14471
+ type Outer struct {
14472
+ Data map [string ]Inner `validate:"dive,keys,max=2,endkeys"`
14473
+ }
14474
+ validate := New ()
14475
+
14476
+ // Key too long: should fail on key before fallback
14477
+ obj1 := Outer {Data : map [string ]Inner {"toolong" : {Value : "ok" }}}
14478
+ errs := validate .Struct (obj1 )
14479
+ NotEqual (t , errs , nil )
14480
+
14481
+ // Key OK, value too long: should fallback and fail on Inner.Value
14482
+ obj2 := Outer {Data : map [string ]Inner {"ok" : {Value : "toolong" }}}
14483
+ errs = validate .Struct (obj2 )
14484
+ NotEqual (t , errs , nil )
14485
+
14486
+ // Both key and value OK: should pass
14487
+ obj3 := Outer {Data : map [string ]Inner {"ok" : {Value : "abc" }}}
14488
+ errs = validate .Struct (obj3 )
14489
+ Equal (t , errs , nil )
14490
+ }
14491
+
14492
+ func TestMapPointerStructFallback (t * testing.T ) {
14493
+ // Tests fallback behavior when validating map keys and pointer to struct values
14494
+ type Inner struct {
14495
+ Count int `validate:"gt=0"`
14496
+ }
14497
+ type Outer struct {
14498
+ Data map [string ]* Inner `validate:"dive,keys,max=3,endkeys"`
14499
+ }
14500
+ validate := New ()
14501
+
14502
+ // Key OK, pointer is nil: no fallback error
14503
+ obj1 := Outer {Data : map [string ]* Inner {"ok" : nil }}
14504
+ errs := validate .Struct (obj1 )
14505
+ Equal (t , errs , nil )
14506
+
14507
+ // Key OK, pointer non-nil but field invalid: fallback should validate Inner.Count
14508
+ obj2 := Outer {Data : map [string ]* Inner {"ok" : {Count : 0 }}}
14509
+ errs = validate .Struct (obj2 )
14510
+ NotEqual (t , errs , nil )
14511
+
14512
+ // Key OK, pointer non-nil and valid: should pass
14513
+ obj3 := Outer {Data : map [string ]* Inner {"ok" : {Count : 5 }}}
14514
+ errs = validate .Struct (obj3 )
14515
+ Equal (t , errs , nil )
14516
+ }
14517
+
14518
+ func TestMapNonStructValueSkipsFallback (t * testing.T ) {
14519
+ // Tests that fallback is skipped for non-struct values in maps
14520
+ type Outer struct {
14521
+ Data map [string ]int `validate:"dive,keys,min=1,endkeys"`
14522
+ }
14523
+ validate := New ()
14524
+
14525
+ // Key OK, value is primitive: no fallback needed, should pass
14526
+ obj1 := Outer {Data : map [string ]int {"a" : 0 }}
14527
+ errs := validate .Struct (obj1 )
14528
+ Equal (t , errs , nil )
14529
+
14530
+ // Key too short: should fail on key
14531
+ obj2 := Outer {Data : map [string ]int {"" : 0 }}
14532
+ errs = validate .Struct (obj2 )
14533
+ NotEqual (t , errs , nil )
14534
+ }
14535
+
14536
+ func TestMapSliceValueNoFallback (t * testing.T ) {
14537
+ // Tests that fallback is skipped for slice values in maps
14538
+ type Outer struct {
14539
+ Data map [string ][]string `validate:"dive,keys,max=1,endkeys"`
14540
+ }
14541
+ validate := New ()
14542
+
14543
+ // Key OK, value is slice: skip fallback, should pass
14544
+ obj1 := Outer {Data : map [string ][]string {"a" : {"x" , "y" }}}
14545
+ errs := validate .Struct (obj1 )
14546
+ Equal (t , errs , nil )
14547
+
14548
+ // Key too long: should fail on key
14549
+ obj2 := Outer {Data : map [string ][]string {"ab" : {"x" }}}
14550
+ errs = validate .Struct (obj2 )
14551
+ NotEqual (t , errs , nil )
14552
+ }
14553
+ func TestMapEmptyStructValueNoError (t * testing.T ) {
14554
+ // Tests that empty struct values in maps do not trigger validation errors
14555
+ type Inner struct {} // no validation tags
14556
+
14557
+ type Outer struct {
14558
+ Data map [string ]Inner `validate:"dive,keys,max=3,endkeys"`
14559
+ }
14560
+ validate := New ()
14561
+
14562
+ // Key OK, value is empty struct: no fields to validate, should pass
14563
+ obj1 := Outer {Data : map [string ]Inner {"ok" : {}}}
14564
+ errs := validate .Struct (obj1 )
14565
+ Equal (t , errs , nil )
14566
+
14567
+ // Key too long: should fail on key, skip struct
14568
+ obj2 := Outer {Data : map [string ]Inner {"toolong" : {}}}
14569
+ errs = validate .Struct (obj2 )
14570
+ NotEqual (t , errs , nil )
14571
+ }
14572
+
14573
+ func TestMapNestedEmptyStructs (t * testing.T ) {
14574
+ // Tests that nested empty structs in maps validate correctly
14575
+ type Level3 struct {}
14576
+ type Level2 struct {
14577
+ Items map [string ]Level3 `validate:"dive,keys,max=2,endkeys"`
14578
+ }
14579
+ type Level1 struct {
14580
+ Levels map [string ]Level2 `validate:"dive,keys,max=2,endkeys"`
14581
+ }
14582
+
14583
+ validate := New ()
14584
+
14585
+ // All keys within max=2, and inner structs are empty → should pass
14586
+ obj := Level1 {Levels : map [string ]Level2 {
14587
+ "a" : {Items : map [string ]Level3 {"b" : {}}},
14588
+ }}
14589
+ errs := validate .Struct (obj )
14590
+ Equal (t , errs , nil )
14591
+
14592
+ // Top-level key too long: should fail on Level1 key, skip deeper
14593
+ obj2 := Level1 {Levels : map [string ]Level2 {
14594
+ "too" : {Items : map [string ]Level3 {"b" : {}}}, // "too" length=3 > max=2
14595
+ }}
14596
+ errs = validate .Struct (obj2 )
14597
+ NotEqual (t , errs , nil )
14598
+ }
14599
+
14600
+ func TestMapEmptyValueMap (t * testing.T ) {
14601
+ // Tests that an empty map with struct values does not trigger validation errors
14602
+ type Inner struct {
14603
+ Value string `validate:"required"`
14604
+ }
14605
+
14606
+ type Outer struct {
14607
+ Data map [string ]Inner `validate:"dive,keys,max=3,endkeys"`
14608
+ }
14609
+ validate := New ()
14610
+
14611
+ // Empty map: nothing to validate, should pass
14612
+ obj := Outer {Data : map [string ]Inner {}}
14613
+ errs := validate .Struct (obj )
14614
+ Equal (t , errs , nil )
14615
+ }
14616
+
14617
+ func TestMapEmptyPointerStructValueNoError (t * testing.T ) {
14618
+ // Tests that empty pointer struct values in maps do not trigger validation errors
14619
+ type Inner struct {}
14620
+
14621
+ type Outer struct {
14622
+ Data map [string ]* Inner `validate:"dive,keys,max=3,endkeys"`
14623
+ }
14624
+ validate := New ()
14625
+
14626
+ // Key OK, pointer is non-nil empty struct: should pass
14627
+ obj1 := Outer {Data : map [string ]* Inner {"ok" : {}}}
14628
+ errs := validate .Struct (obj1 )
14629
+ Equal (t , errs , nil )
14630
+
14631
+ // Key OK, pointer is nil: no tags on Inner, so should pass
14632
+ obj2 := Outer {Data : map [string ]* Inner {"ok" : nil }}
14633
+ errs = validate .Struct (obj2 )
14634
+ Equal (t , errs , nil )
14635
+ }
0 commit comments