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