1package schema 2 3import ( 4 "fmt" 5 "reflect" 6 "testing" 7 "time" 8) 9 10type E1 struct { 11 F01 int `schema:"f01"` 12 F02 int `schema:"-"` 13 F03 string `schema:"f03"` 14 F04 string `schema:"f04,omitempty"` 15 F05 bool `schema:"f05"` 16 F06 bool `schema:"f06"` 17 F07 *string `schema:"f07"` 18 F08 *int8 `schema:"f08"` 19 F09 float64 `schema:"f09"` 20 F10 func() `schema:"f10"` 21 F11 inner 22} 23type inner struct { 24 F12 int 25} 26 27func TestFilled(t *testing.T) { 28 f07 := "seven" 29 var f08 int8 = 8 30 s := &E1{ 31 F01: 1, 32 F02: 2, 33 F03: "three", 34 F04: "four", 35 F05: true, 36 F06: false, 37 F07: &f07, 38 F08: &f08, 39 F09: 1.618, 40 F10: func() {}, 41 F11: inner{12}, 42 } 43 44 vals := make(map[string][]string) 45 errs := NewEncoder().Encode(s, vals) 46 47 valExists(t, "f01", "1", vals) 48 valNotExists(t, "f02", vals) 49 valExists(t, "f03", "three", vals) 50 valExists(t, "f05", "true", vals) 51 valExists(t, "f06", "false", vals) 52 valExists(t, "f07", "seven", vals) 53 valExists(t, "f08", "8", vals) 54 valExists(t, "f09", "1.618000", vals) 55 valExists(t, "F12", "12", vals) 56 57 emptyErr := MultiError{} 58 if errs.Error() == emptyErr.Error() { 59 t.Errorf("Expected error got %v", errs) 60 } 61} 62 63type Aa int 64 65type E3 struct { 66 F01 bool `schema:"f01"` 67 F02 float32 `schema:"f02"` 68 F03 float64 `schema:"f03"` 69 F04 int `schema:"f04"` 70 F05 int8 `schema:"f05"` 71 F06 int16 `schema:"f06"` 72 F07 int32 `schema:"f07"` 73 F08 int64 `schema:"f08"` 74 F09 string `schema:"f09"` 75 F10 uint `schema:"f10"` 76 F11 uint8 `schema:"f11"` 77 F12 uint16 `schema:"f12"` 78 F13 uint32 `schema:"f13"` 79 F14 uint64 `schema:"f14"` 80 F15 Aa `schema:"f15"` 81} 82 83// Test compatibility with default decoder types. 84func TestCompat(t *testing.T) { 85 src := &E3{ 86 F01: true, 87 F02: 4.2, 88 F03: 4.3, 89 F04: -42, 90 F05: -43, 91 F06: -44, 92 F07: -45, 93 F08: -46, 94 F09: "foo", 95 F10: 42, 96 F11: 43, 97 F12: 44, 98 F13: 45, 99 F14: 46, 100 F15: 1, 101 } 102 dst := &E3{} 103 104 vals := make(map[string][]string) 105 encoder := NewEncoder() 106 decoder := NewDecoder() 107 108 encoder.RegisterEncoder(src.F15, func(reflect.Value) string { return "1" }) 109 decoder.RegisterConverter(src.F15, func(string) reflect.Value { return reflect.ValueOf(1) }) 110 111 err := encoder.Encode(src, vals) 112 if err != nil { 113 t.Errorf("Encoder has non-nil error: %v", err) 114 } 115 err = decoder.Decode(dst, vals) 116 if err != nil { 117 t.Errorf("Decoder has non-nil error: %v", err) 118 } 119 120 if *src != *dst { 121 t.Errorf("Decoder-Encoder compatibility: expected %v, got %v\n", src, dst) 122 } 123} 124 125func TestEmpty(t *testing.T) { 126 s := &E1{ 127 F01: 1, 128 F02: 2, 129 F03: "three", 130 } 131 132 estr := "schema: encoder not found for <nil>" 133 vals := make(map[string][]string) 134 err := NewEncoder().Encode(s, vals) 135 if err.Error() != estr { 136 t.Errorf("Expected: %s, got %v", estr, err) 137 } 138 139 valExists(t, "f03", "three", vals) 140 valNotExists(t, "f04", vals) 141} 142 143func TestStruct(t *testing.T) { 144 estr := "schema: interface must be a struct" 145 vals := make(map[string][]string) 146 err := NewEncoder().Encode("hello world", vals) 147 148 if err.Error() != estr { 149 t.Errorf("Expected: %s, got %v", estr, err) 150 } 151} 152 153func TestSlices(t *testing.T) { 154 type oneAsWord int 155 ones := []oneAsWord{1, 2} 156 s1 := &struct { 157 ones []oneAsWord `schema:"ones"` 158 ints []int `schema:"ints"` 159 nonempty []int `schema:"nonempty"` 160 empty []int `schema:"empty,omitempty"` 161 }{ones, []int{1, 1}, []int{}, []int{}} 162 vals := make(map[string][]string) 163 164 encoder := NewEncoder() 165 encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" }) 166 err := encoder.Encode(s1, vals) 167 if err != nil { 168 t.Errorf("Encoder has non-nil error: %v", err) 169 } 170 171 valsExist(t, "ones", []string{"one", "one"}, vals) 172 valsExist(t, "ints", []string{"1", "1"}, vals) 173 valsExist(t, "nonempty", []string{}, vals) 174 valNotExists(t, "empty", vals) 175} 176 177func TestCompatSlices(t *testing.T) { 178 type oneAsWord int 179 type s1 struct { 180 Ones []oneAsWord `schema:"ones"` 181 Ints []int `schema:"ints"` 182 } 183 ones := []oneAsWord{1, 1} 184 src := &s1{ones, []int{1, 1}} 185 vals := make(map[string][]string) 186 dst := &s1{} 187 188 encoder := NewEncoder() 189 encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" }) 190 191 decoder := NewDecoder() 192 decoder.RegisterConverter(ones[0], func(s string) reflect.Value { 193 if s == "one" { 194 return reflect.ValueOf(1) 195 } 196 return reflect.ValueOf(2) 197 }) 198 199 err := encoder.Encode(src, vals) 200 if err != nil { 201 t.Errorf("Encoder has non-nil error: %v", err) 202 } 203 err = decoder.Decode(dst, vals) 204 if err != nil { 205 t.Errorf("Dncoder has non-nil error: %v", err) 206 } 207 208 if len(src.Ints) != len(dst.Ints) || len(src.Ones) != len(src.Ones) { 209 t.Fatalf("Expected %v, got %v", src, dst) 210 } 211 212 for i, v := range src.Ones { 213 if dst.Ones[i] != v { 214 t.Fatalf("Expected %v, got %v", v, dst.Ones[i]) 215 } 216 } 217 218 for i, v := range src.Ints { 219 if dst.Ints[i] != v { 220 t.Fatalf("Expected %v, got %v", v, dst.Ints[i]) 221 } 222 } 223} 224 225func TestRegisterEncoder(t *testing.T) { 226 type oneAsWord int 227 type twoAsWord int 228 type oneSliceAsWord []int 229 230 s1 := &struct { 231 oneAsWord 232 twoAsWord 233 oneSliceAsWord 234 }{1, 2, []int{1, 1}} 235 v1 := make(map[string][]string) 236 237 encoder := NewEncoder() 238 encoder.RegisterEncoder(s1.oneAsWord, func(v reflect.Value) string { return "one" }) 239 encoder.RegisterEncoder(s1.twoAsWord, func(v reflect.Value) string { return "two" }) 240 encoder.RegisterEncoder(s1.oneSliceAsWord, func(v reflect.Value) string { return "one" }) 241 242 err := encoder.Encode(s1, v1) 243 if err != nil { 244 t.Errorf("Encoder has non-nil error: %v", err) 245 } 246 247 valExists(t, "oneAsWord", "one", v1) 248 valExists(t, "twoAsWord", "two", v1) 249 valExists(t, "oneSliceAsWord", "one", v1) 250} 251 252func TestEncoderOrder(t *testing.T) { 253 type builtinEncoderSimple int 254 type builtinEncoderSimpleOverridden int 255 type builtinEncoderSlice []int 256 type builtinEncoderSliceOverridden []int 257 type builtinEncoderStruct struct{ nr int } 258 type builtinEncoderStructOverridden struct{ nr int } 259 260 s1 := &struct { 261 builtinEncoderSimple `schema:"simple"` 262 builtinEncoderSimpleOverridden `schema:"simple_overridden"` 263 builtinEncoderSlice `schema:"slice"` 264 builtinEncoderSliceOverridden `schema:"slice_overridden"` 265 builtinEncoderStruct `schema:"struct"` 266 builtinEncoderStructOverridden `schema:"struct_overridden"` 267 }{ 268 1, 269 1, 270 []int{2}, 271 []int{2}, 272 builtinEncoderStruct{3}, 273 builtinEncoderStructOverridden{3}, 274 } 275 v1 := make(map[string][]string) 276 277 encoder := NewEncoder() 278 encoder.RegisterEncoder(s1.builtinEncoderSimpleOverridden, func(v reflect.Value) string { return "one" }) 279 encoder.RegisterEncoder(s1.builtinEncoderSliceOverridden, func(v reflect.Value) string { return "two" }) 280 encoder.RegisterEncoder(s1.builtinEncoderStructOverridden, func(v reflect.Value) string { return "three" }) 281 282 err := encoder.Encode(s1, v1) 283 if err != nil { 284 t.Errorf("Encoder has non-nil error: %v", err) 285 } 286 287 valExists(t, "simple", "1", v1) 288 valExists(t, "simple_overridden", "one", v1) 289 valExists(t, "slice", "2", v1) 290 valExists(t, "slice_overridden", "two", v1) 291 valExists(t, "nr", "3", v1) 292 valExists(t, "struct_overridden", "three", v1) 293} 294 295func valExists(t *testing.T, key string, expect string, result map[string][]string) { 296 valsExist(t, key, []string{expect}, result) 297} 298 299func valsExist(t *testing.T, key string, expect []string, result map[string][]string) { 300 vals, ok := result[key] 301 if !ok { 302 t.Fatalf("Key not found. Expected: %s", key) 303 } 304 305 if len(expect) != len(vals) { 306 t.Fatalf("Expected: %v, got: %v", expect, vals) 307 } 308 309 for i, v := range expect { 310 if vals[i] != v { 311 t.Fatalf("Unexpected value. Expected: %v, got %v", v, vals[i]) 312 } 313 } 314} 315 316func valNotExists(t *testing.T, key string, result map[string][]string) { 317 if val, ok := result[key]; ok { 318 t.Error("Key not ommited. Expected: empty; got: " + val[0] + ".") 319 } 320} 321 322type E4 struct { 323 ID string `json:"id"` 324} 325 326func TestEncoderSetAliasTag(t *testing.T) { 327 data := map[string][]string{} 328 329 s := E4{ 330 ID: "foo", 331 } 332 encoder := NewEncoder() 333 encoder.SetAliasTag("json") 334 encoder.Encode(&s, data) 335 valExists(t, "id", "foo", data) 336} 337 338type E5 struct { 339 F01 int `schema:"f01,omitempty"` 340 F02 string `schema:"f02,omitempty"` 341 F03 *string `schema:"f03,omitempty"` 342 F04 *int8 `schema:"f04,omitempty"` 343 F05 float64 `schema:"f05,omitempty"` 344 F06 E5F06 `schema:"f06,omitempty"` 345 F07 E5F06 `schema:"f07,omitempty"` 346 F08 []string `schema:"f08,omitempty"` 347 F09 []string `schema:"f09,omitempty"` 348} 349 350type E5F06 struct { 351 F0601 string `schema:"f0601,omitempty"` 352} 353 354func TestEncoderWithOmitempty(t *testing.T) { 355 vals := map[string][]string{} 356 357 s := E5{ 358 F02: "test", 359 F07: E5F06{ 360 F0601: "test", 361 }, 362 F09: []string{"test"}, 363 } 364 365 encoder := NewEncoder() 366 encoder.Encode(&s, vals) 367 368 valNotExists(t, "f01", vals) 369 valExists(t, "f02", "test", vals) 370 valNotExists(t, "f03", vals) 371 valNotExists(t, "f04", vals) 372 valNotExists(t, "f05", vals) 373 valNotExists(t, "f06", vals) 374 valExists(t, "f0601", "test", vals) 375 valNotExists(t, "f08", vals) 376 valsExist(t, "f09", []string{"test"}, vals) 377} 378 379type E6 struct { 380 F01 *inner 381 F02 *inner 382 F03 *inner `schema:",omitempty"` 383} 384 385func TestStructPointer(t *testing.T) { 386 vals := map[string][]string{} 387 s := E6{ 388 F01: &inner{2}, 389 } 390 391 encoder := NewEncoder() 392 encoder.Encode(&s, vals) 393 valExists(t, "F12", "2", vals) 394 valExists(t, "F02", "null", vals) 395 valNotExists(t, "F03", vals) 396} 397 398func TestRegisterEncoderCustomArrayType(t *testing.T) { 399 type CustomInt []int 400 type S1 struct { 401 SomeInts CustomInt `schema:",omitempty"` 402 } 403 404 ss := []S1{ 405 {}, 406 {CustomInt{}}, 407 {CustomInt{1, 2, 3}}, 408 } 409 410 for s := range ss { 411 vals := map[string][]string{} 412 413 encoder := NewEncoder() 414 encoder.RegisterEncoder(CustomInt{}, func(value reflect.Value) string { 415 return fmt.Sprint(value.Interface()) 416 }) 417 418 encoder.Encode(s, vals) 419 } 420} 421 422func TestRegisterEncoderStructIsZero(t *testing.T) { 423 type S1 struct { 424 SomeTime1 time.Time `schema:"tim1,omitempty"` 425 SomeTime2 time.Time `schema:"tim2,omitempty"` 426 } 427 428 ss := []*S1{ 429 { 430 SomeTime1: time.Date(2020, 8, 4, 13, 30, 1, 0, time.UTC), 431 }, 432 } 433 434 for s := range ss { 435 vals := map[string][]string{} 436 437 encoder := NewEncoder() 438 encoder.RegisterEncoder(time.Time{}, func(value reflect.Value) string { 439 return value.Interface().(time.Time).Format(time.RFC3339Nano) 440 }) 441 442 err := encoder.Encode(ss[s], vals) 443 if err != nil { 444 t.Errorf("Encoder has non-nil error: %v", err) 445 } 446 447 ta, ok := vals["tim1"] 448 if !ok { 449 t.Error("expected tim1 to be present") 450 } 451 452 if len(ta) != 1 { 453 t.Error("expected tim1 to be present") 454 } 455 456 if "2020-08-04T13:30:01Z" != ta[0] { 457 t.Error("expected correct tim1 time") 458 } 459 460 _, ok = vals["tim2"] 461 if ok { 462 t.Error("expected tim1 not to be present") 463 } 464 } 465} 466