github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/mapstruct/hooks_test.go (about) 1 package mapstruct 2 3 import ( 4 "errors" 5 "math/big" 6 "net" 7 "reflect" 8 "testing" 9 "time" 10 ) 11 12 func TestComposeDecodeHookFunc(t *testing.T) { 13 f1 := func( 14 f reflect.Kind, 15 t reflect.Kind, 16 data interface{}, 17 ) (interface{}, error) { 18 return data.(string) + "foo", nil 19 } 20 21 f2 := func( 22 f reflect.Kind, 23 t reflect.Kind, 24 data interface{}, 25 ) (interface{}, error) { 26 return data.(string) + "bar", nil 27 } 28 29 f := ComposeDecodeHookFunc(f1, f2) 30 31 result, err := DecodeHookExec( 32 f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 33 if err != nil { 34 t.Fatalf("bad: %s", err) 35 } 36 if result.(string) != "foobar" { 37 t.Fatalf("bad: %#v", result) 38 } 39 } 40 41 func TestComposeDecodeHookFunc_err(t *testing.T) { 42 f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) { 43 return nil, errors.New("foo") 44 } 45 46 f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) { 47 panic("NOPE") 48 } 49 50 f := ComposeDecodeHookFunc(f1, f2) 51 52 _, err := DecodeHookExec( 53 f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 54 if err.Error() != "foo" { 55 t.Fatalf("bad: %s", err) 56 } 57 } 58 59 func TestComposeDecodeHookFunc_kinds(t *testing.T) { 60 var f2From reflect.Kind 61 62 f1 := func( 63 f reflect.Kind, 64 t reflect.Kind, 65 data interface{}, 66 ) (interface{}, error) { 67 return int(42), nil 68 } 69 70 f2 := func( 71 f reflect.Kind, 72 t reflect.Kind, 73 data interface{}, 74 ) (interface{}, error) { 75 f2From = f 76 return data, nil 77 } 78 79 f := ComposeDecodeHookFunc(f1, f2) 80 81 _, err := DecodeHookExec( 82 f, reflect.ValueOf(""), reflect.ValueOf([]byte(""))) 83 if err != nil { 84 t.Fatalf("bad: %s", err) 85 } 86 if f2From != reflect.Int { 87 t.Fatalf("bad: %#v", f2From) 88 } 89 } 90 91 func TestStringToSliceHookFunc(t *testing.T) { 92 f := StringToSliceHookFunc(",") 93 94 strValue := reflect.ValueOf("42") 95 sliceValue := reflect.ValueOf([]byte("42")) 96 cases := []struct { 97 f, t reflect.Value 98 result interface{} 99 err bool 100 }{ 101 {sliceValue, sliceValue, []byte("42"), false}, 102 {strValue, strValue, "42", false}, 103 { 104 reflect.ValueOf("foo,bar,baz"), 105 sliceValue, 106 []string{"foo", "bar", "baz"}, 107 false, 108 }, 109 { 110 reflect.ValueOf(""), 111 sliceValue, 112 []string{}, 113 false, 114 }, 115 } 116 117 for i, tc := range cases { 118 actual, err := DecodeHookExec(f, tc.f, tc.t) 119 if tc.err != (err != nil) { 120 t.Fatalf("case %d: expected err %#v", i, tc.err) 121 } 122 if !reflect.DeepEqual(actual, tc.result) { 123 t.Fatalf( 124 "case %d: expected %#v, got %#v", 125 i, tc.result, actual) 126 } 127 } 128 } 129 130 func TestStringToTimeDurationHookFunc(t *testing.T) { 131 f := StringToTimeDurationHookFunc() 132 133 timeValue := reflect.ValueOf(time.Duration(5)) 134 strValue := reflect.ValueOf("") 135 cases := []struct { 136 f, t reflect.Value 137 result interface{} 138 err bool 139 }{ 140 {reflect.ValueOf("5s"), timeValue, 5 * time.Second, false}, 141 {reflect.ValueOf("5"), timeValue, time.Duration(0), true}, 142 {reflect.ValueOf("5"), strValue, "5", false}, 143 } 144 145 for i, tc := range cases { 146 actual, err := DecodeHookExec(f, tc.f, tc.t) 147 if tc.err != (err != nil) { 148 t.Fatalf("case %d: expected err %#v", i, tc.err) 149 } 150 if !reflect.DeepEqual(actual, tc.result) { 151 t.Fatalf( 152 "case %d: expected %#v, got %#v", 153 i, tc.result, actual) 154 } 155 } 156 } 157 158 func TestStringToTimeHookFunc(t *testing.T) { 159 strValue := reflect.ValueOf("5") 160 timeValue := reflect.ValueOf(time.Time{}) 161 cases := []struct { 162 f, t reflect.Value 163 layout string 164 result interface{} 165 err bool 166 }{ 167 { 168 reflect.ValueOf("2006-01-02T15:04:05Z"), timeValue, time.RFC3339, 169 time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false, 170 }, 171 {strValue, timeValue, time.RFC3339, time.Time{}, true}, 172 {strValue, strValue, time.RFC3339, "5", false}, 173 } 174 175 for i, tc := range cases { 176 f := StringToTimeHookFunc(tc.layout) 177 actual, err := DecodeHookExec(f, tc.f, tc.t) 178 if tc.err != (err != nil) { 179 t.Fatalf("case %d: expected err %#v", i, tc.err) 180 } 181 if !reflect.DeepEqual(actual, tc.result) { 182 t.Fatalf( 183 "case %d: expected %#v, got %#v", 184 i, tc.result, actual) 185 } 186 } 187 } 188 189 func TestStringToIPHookFunc(t *testing.T) { 190 strValue := reflect.ValueOf("5") 191 ipValue := reflect.ValueOf(net.IP{}) 192 cases := []struct { 193 f, t reflect.Value 194 result interface{} 195 err bool 196 }{ 197 { 198 reflect.ValueOf("1.2.3.4"), ipValue, 199 net.IPv4(0x01, 0x02, 0x03, 0x04), false, 200 }, 201 {strValue, ipValue, net.IP{}, true}, 202 {strValue, strValue, "5", false}, 203 } 204 205 for i, tc := range cases { 206 f := StringToIPHookFunc() 207 actual, err := DecodeHookExec(f, tc.f, tc.t) 208 if tc.err != (err != nil) { 209 t.Fatalf("case %d: expected err %#v", i, tc.err) 210 } 211 if !reflect.DeepEqual(actual, tc.result) { 212 t.Fatalf( 213 "case %d: expected %#v, got %#v", 214 i, tc.result, actual) 215 } 216 } 217 } 218 219 func TestStringToIPNetHookFunc(t *testing.T) { 220 strValue := reflect.ValueOf("5") 221 ipNetValue := reflect.ValueOf(net.IPNet{}) 222 var nilNet *net.IPNet = nil 223 224 cases := []struct { 225 f, t reflect.Value 226 result interface{} 227 err bool 228 }{ 229 { 230 reflect.ValueOf("1.2.3.4/24"), ipNetValue, 231 &net.IPNet{ 232 IP: net.IP{0x01, 0x02, 0x03, 0x00}, 233 Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00), 234 }, false, 235 }, 236 {strValue, ipNetValue, nilNet, true}, 237 {strValue, strValue, "5", false}, 238 } 239 240 for i, tc := range cases { 241 f := StringToIPNetHookFunc() 242 actual, err := DecodeHookExec(f, tc.f, tc.t) 243 if tc.err != (err != nil) { 244 t.Fatalf("case %d: expected err %#v", i, tc.err) 245 } 246 if !reflect.DeepEqual(actual, tc.result) { 247 t.Fatalf( 248 "case %d: expected %#v, got %#v", 249 i, tc.result, actual) 250 } 251 } 252 } 253 254 func TestWeaklyTypedHook(t *testing.T) { 255 var f HookFunc = WeaklyTypedHook 256 257 strValue := reflect.ValueOf("") 258 cases := []struct { 259 f, t reflect.Value 260 result interface{} 261 err bool 262 }{ 263 // TO STRING 264 { 265 reflect.ValueOf(false), 266 strValue, 267 "0", 268 false, 269 }, 270 271 { 272 reflect.ValueOf(true), 273 strValue, 274 "1", 275 false, 276 }, 277 278 { 279 reflect.ValueOf(float32(7)), 280 strValue, 281 "7", 282 false, 283 }, 284 285 { 286 reflect.ValueOf(int(7)), 287 strValue, 288 "7", 289 false, 290 }, 291 292 { 293 reflect.ValueOf([]uint8("foo")), 294 strValue, 295 "foo", 296 false, 297 }, 298 299 { 300 reflect.ValueOf(uint(7)), 301 strValue, 302 "7", 303 false, 304 }, 305 } 306 307 for i, tc := range cases { 308 actual, err := DecodeHookExec(f, tc.f, tc.t) 309 if tc.err != (err != nil) { 310 t.Fatalf("case %d: expected err %#v", i, tc.err) 311 } 312 if !reflect.DeepEqual(actual, tc.result) { 313 t.Fatalf( 314 "case %d: expected %#v, got %#v", 315 i, tc.result, actual) 316 } 317 } 318 } 319 320 func TestStructToMapHookFuncTabled(t *testing.T) { 321 var f HookFunc = RecursiveStructToMapHookFunc() 322 323 type b struct { 324 TestKey string 325 } 326 327 type a struct { 328 Sub b 329 } 330 331 testStruct := a{ 332 Sub: b{ 333 TestKey: "testval", 334 }, 335 } 336 337 testMap := map[string]interface{}{ 338 "Sub": map[string]interface{}{ 339 "TestKey": "testval", 340 }, 341 } 342 343 cases := []struct { 344 name string 345 receiver interface{} 346 input interface{} 347 expected interface{} 348 err bool 349 }{ 350 { 351 "map receiver", 352 func() interface{} { 353 var res map[string]interface{} 354 return &res 355 }(), 356 testStruct, 357 &testMap, 358 false, 359 }, 360 { 361 "interface receiver", 362 func() interface{} { 363 var res interface{} 364 return &res 365 }(), 366 testStruct, 367 func() interface{} { 368 var exp interface{} = testMap 369 return &exp 370 }(), 371 false, 372 }, 373 { 374 "slice receiver errors", 375 func() interface{} { 376 var res []string 377 return &res 378 }(), 379 testStruct, 380 new([]string), 381 true, 382 }, 383 { 384 "slice to slice - no change", 385 func() interface{} { 386 var res []string 387 return &res 388 }(), 389 []string{"a", "b"}, 390 &[]string{"a", "b"}, 391 false, 392 }, 393 { 394 "string to string - no change", 395 func() interface{} { 396 var res string 397 return &res 398 }(), 399 "test", 400 func() *string { 401 s := "test" 402 return &s 403 }(), 404 false, 405 }, 406 } 407 408 for _, tc := range cases { 409 t.Run(tc.name, func(t *testing.T) { 410 cfg := &Config{ 411 Hook: f, 412 Result: tc.receiver, 413 } 414 415 d, err := NewDecoder(cfg) 416 if err != nil { 417 t.Fatalf("unexpected err %#v", err) 418 } 419 420 err = d.Decode(tc.input) 421 if tc.err != (err != nil) { 422 t.Fatalf("expected err %#v", err) 423 } 424 425 if !reflect.DeepEqual(tc.expected, tc.receiver) { 426 t.Fatalf("expected %#v, got %#v", 427 tc.expected, tc.receiver) 428 } 429 }) 430 } 431 } 432 433 func TestTextUnmarshallerHookFunc(t *testing.T) { 434 cases := []struct { 435 f, t reflect.Value 436 result interface{} 437 err bool 438 }{ 439 {reflect.ValueOf("42"), reflect.ValueOf(big.Int{}), big.NewInt(42), false}, 440 {reflect.ValueOf("invalid"), reflect.ValueOf(big.Int{}), nil, true}, 441 {reflect.ValueOf("5"), reflect.ValueOf("5"), "5", false}, 442 } 443 444 for i, tc := range cases { 445 f := TextUnmarshallerHookFunc() 446 actual, err := DecodeHookExec(f, tc.f, tc.t) 447 if tc.err != (err != nil) { 448 t.Fatalf("case %d: expected err %#v", i, tc.err) 449 } 450 if !reflect.DeepEqual(actual, tc.result) { 451 t.Fatalf( 452 "case %d: expected %#v, got %#v", 453 i, tc.result, actual) 454 } 455 } 456 }