1package mapstructure 2 3import ( 4 "errors" 5 "net" 6 "reflect" 7 "testing" 8 "time" 9) 10 11func TestComposeDecodeHookFunc(t *testing.T) { 12 f1 := func( 13 f reflect.Kind, 14 t reflect.Kind, 15 data interface{}) (interface{}, error) { 16 return data.(string) + "foo", nil 17 } 18 19 f2 := func( 20 f reflect.Kind, 21 t reflect.Kind, 22 data interface{}) (interface{}, error) { 23 return data.(string) + "bar", nil 24 } 25 26 f := ComposeDecodeHookFunc(f1, f2) 27 28 result, err := DecodeHookExec( 29 f, reflect.TypeOf(""), reflect.TypeOf([]byte("")), "") 30 if err != nil { 31 t.Fatalf("bad: %s", err) 32 } 33 if result.(string) != "foobar" { 34 t.Fatalf("bad: %#v", result) 35 } 36} 37 38func TestComposeDecodeHookFunc_err(t *testing.T) { 39 f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) { 40 return nil, errors.New("foo") 41 } 42 43 f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) { 44 panic("NOPE") 45 } 46 47 f := ComposeDecodeHookFunc(f1, f2) 48 49 _, err := DecodeHookExec( 50 f, reflect.TypeOf(""), reflect.TypeOf([]byte("")), 42) 51 if err.Error() != "foo" { 52 t.Fatalf("bad: %s", err) 53 } 54} 55 56func TestComposeDecodeHookFunc_kinds(t *testing.T) { 57 var f2From reflect.Kind 58 59 f1 := func( 60 f reflect.Kind, 61 t reflect.Kind, 62 data interface{}) (interface{}, error) { 63 return int(42), nil 64 } 65 66 f2 := func( 67 f reflect.Kind, 68 t reflect.Kind, 69 data interface{}) (interface{}, error) { 70 f2From = f 71 return data, nil 72 } 73 74 f := ComposeDecodeHookFunc(f1, f2) 75 76 _, err := DecodeHookExec( 77 f, reflect.TypeOf(""), reflect.TypeOf([]byte("")), "") 78 if err != nil { 79 t.Fatalf("bad: %s", err) 80 } 81 if f2From != reflect.Int { 82 t.Fatalf("bad: %#v", f2From) 83 } 84} 85 86func TestStringToSliceHookFunc(t *testing.T) { 87 f := StringToSliceHookFunc(",") 88 89 strType := reflect.TypeOf("") 90 sliceType := reflect.TypeOf([]byte("")) 91 cases := []struct { 92 f, t reflect.Type 93 data interface{} 94 result interface{} 95 err bool 96 }{ 97 {sliceType, sliceType, 42, 42, false}, 98 {strType, strType, 42, 42, false}, 99 { 100 strType, 101 sliceType, 102 "foo,bar,baz", 103 []string{"foo", "bar", "baz"}, 104 false, 105 }, 106 { 107 strType, 108 sliceType, 109 "", 110 []string{}, 111 false, 112 }, 113 } 114 115 for i, tc := range cases { 116 actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data) 117 if tc.err != (err != nil) { 118 t.Fatalf("case %d: expected err %#v", i, tc.err) 119 } 120 if !reflect.DeepEqual(actual, tc.result) { 121 t.Fatalf( 122 "case %d: expected %#v, got %#v", 123 i, tc.result, actual) 124 } 125 } 126} 127 128func TestStringToTimeDurationHookFunc(t *testing.T) { 129 f := StringToTimeDurationHookFunc() 130 131 strType := reflect.TypeOf("") 132 timeType := reflect.TypeOf(time.Duration(5)) 133 cases := []struct { 134 f, t reflect.Type 135 data interface{} 136 result interface{} 137 err bool 138 }{ 139 {strType, timeType, "5s", 5 * time.Second, false}, 140 {strType, timeType, "5", time.Duration(0), true}, 141 {strType, strType, "5", "5", false}, 142 } 143 144 for i, tc := range cases { 145 actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data) 146 if tc.err != (err != nil) { 147 t.Fatalf("case %d: expected err %#v", i, tc.err) 148 } 149 if !reflect.DeepEqual(actual, tc.result) { 150 t.Fatalf( 151 "case %d: expected %#v, got %#v", 152 i, tc.result, actual) 153 } 154 } 155} 156 157func TestStringToTimeHookFunc(t *testing.T) { 158 strType := reflect.TypeOf("") 159 timeType := reflect.TypeOf(time.Time{}) 160 cases := []struct { 161 f, t reflect.Type 162 layout string 163 data interface{} 164 result interface{} 165 err bool 166 }{ 167 {strType, timeType, time.RFC3339, "2006-01-02T15:04:05Z", 168 time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false}, 169 {strType, timeType, time.RFC3339, "5", time.Time{}, true}, 170 {strType, strType, time.RFC3339, "5", "5", false}, 171 } 172 173 for i, tc := range cases { 174 f := StringToTimeHookFunc(tc.layout) 175 actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data) 176 if tc.err != (err != nil) { 177 t.Fatalf("case %d: expected err %#v", i, tc.err) 178 } 179 if !reflect.DeepEqual(actual, tc.result) { 180 t.Fatalf( 181 "case %d: expected %#v, got %#v", 182 i, tc.result, actual) 183 } 184 } 185} 186 187func TestStringToIPHookFunc(t *testing.T) { 188 strType := reflect.TypeOf("") 189 ipType := reflect.TypeOf(net.IP{}) 190 cases := []struct { 191 f, t reflect.Type 192 data interface{} 193 result interface{} 194 err bool 195 }{ 196 {strType, ipType, "1.2.3.4", 197 net.IPv4(0x01, 0x02, 0x03, 0x04), false}, 198 {strType, ipType, "5", net.IP{}, true}, 199 {strType, strType, "5", "5", false}, 200 } 201 202 for i, tc := range cases { 203 f := StringToIPHookFunc() 204 actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data) 205 if tc.err != (err != nil) { 206 t.Fatalf("case %d: expected err %#v", i, tc.err) 207 } 208 if !reflect.DeepEqual(actual, tc.result) { 209 t.Fatalf( 210 "case %d: expected %#v, got %#v", 211 i, tc.result, actual) 212 } 213 } 214} 215 216func TestStringToIPNetHookFunc(t *testing.T) { 217 strType := reflect.TypeOf("") 218 ipNetType := reflect.TypeOf(net.IPNet{}) 219 var nilNet *net.IPNet = nil 220 221 cases := []struct { 222 f, t reflect.Type 223 data interface{} 224 result interface{} 225 err bool 226 }{ 227 {strType, ipNetType, "1.2.3.4/24", 228 &net.IPNet{ 229 IP: net.IP{0x01, 0x02, 0x03, 0x00}, 230 Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00), 231 }, false}, 232 {strType, ipNetType, "5", nilNet, true}, 233 {strType, strType, "5", "5", false}, 234 } 235 236 for i, tc := range cases { 237 f := StringToIPNetHookFunc() 238 actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data) 239 if tc.err != (err != nil) { 240 t.Fatalf("case %d: expected err %#v", i, tc.err) 241 } 242 if !reflect.DeepEqual(actual, tc.result) { 243 t.Fatalf( 244 "case %d: expected %#v, got %#v", 245 i, tc.result, actual) 246 } 247 } 248} 249 250func TestWeaklyTypedHook(t *testing.T) { 251 var f DecodeHookFunc = WeaklyTypedHook 252 253 boolType := reflect.TypeOf(true) 254 strType := reflect.TypeOf("") 255 sliceType := reflect.TypeOf([]byte("")) 256 cases := []struct { 257 f, t reflect.Type 258 data interface{} 259 result interface{} 260 err bool 261 }{ 262 // TO STRING 263 { 264 boolType, 265 strType, 266 false, 267 "0", 268 false, 269 }, 270 271 { 272 boolType, 273 strType, 274 true, 275 "1", 276 false, 277 }, 278 279 { 280 reflect.TypeOf(float32(1)), 281 strType, 282 float32(7), 283 "7", 284 false, 285 }, 286 287 { 288 reflect.TypeOf(int(1)), 289 strType, 290 int(7), 291 "7", 292 false, 293 }, 294 295 { 296 sliceType, 297 strType, 298 []uint8("foo"), 299 "foo", 300 false, 301 }, 302 303 { 304 reflect.TypeOf(uint(1)), 305 strType, 306 uint(7), 307 "7", 308 false, 309 }, 310 } 311 312 for i, tc := range cases { 313 actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data) 314 if tc.err != (err != nil) { 315 t.Fatalf("case %d: expected err %#v", i, tc.err) 316 } 317 if !reflect.DeepEqual(actual, tc.result) { 318 t.Fatalf( 319 "case %d: expected %#v, got %#v", 320 i, tc.result, actual) 321 } 322 } 323} 324