git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/schema/encoder_test.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 "time" 8 ) 9 10 type 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 } 23 type inner struct { 24 F12 int 25 } 26 27 func 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 63 type Aa int 64 65 type 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. 84 func 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 125 func 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 143 func 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 153 func 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 177 func 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(dst.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 225 func 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 252 func 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 295 func valExists(t *testing.T, key string, expect string, result map[string][]string) { 296 valsExist(t, key, []string{expect}, result) 297 } 298 299 func 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 316 func valNotExists(t *testing.T, key string, result map[string][]string) { 317 if val, ok := result[key]; ok { 318 t.Error("Key not omitted. Expected: empty; got: " + val[0] + ".") 319 } 320 } 321 322 func valsLength(t *testing.T, expectedLength int, result map[string][]string) { 323 length := len(result) 324 if length != expectedLength { 325 t.Errorf("Expected length of %v, but got %v", expectedLength, length) 326 } 327 } 328 329 func noError(t *testing.T, err error) { 330 if err != nil { 331 t.Errorf("Unexpected error. Got %v", err) 332 } 333 } 334 335 type E4 struct { 336 ID string `json:"id"` 337 } 338 339 func TestEncoderSetAliasTag(t *testing.T) { 340 data := map[string][]string{} 341 342 s := E4{ 343 ID: "foo", 344 } 345 encoder := NewEncoder() 346 encoder.SetAliasTag("json") 347 err := encoder.Encode(&s, data) 348 if err != nil { 349 t.Fatalf("Failed to encode: %v", err) 350 } 351 valExists(t, "id", "foo", data) 352 } 353 354 type E5 struct { 355 F01 int `schema:"f01,omitempty"` 356 F02 string `schema:"f02,omitempty"` 357 F03 *string `schema:"f03,omitempty"` 358 F04 *int8 `schema:"f04,omitempty"` 359 F05 float64 `schema:"f05,omitempty"` 360 F06 E5F06 `schema:"f06,omitempty"` 361 F07 E5F06 `schema:"f07,omitempty"` 362 F08 []string `schema:"f08,omitempty"` 363 F09 []string `schema:"f09,omitempty"` 364 } 365 366 type E5F06 struct { 367 F0601 string `schema:"f0601,omitempty"` 368 } 369 370 func TestEncoderWithOmitempty(t *testing.T) { 371 vals := map[string][]string{} 372 373 s := E5{ 374 F02: "test", 375 F07: E5F06{ 376 F0601: "test", 377 }, 378 F09: []string{"test"}, 379 } 380 381 encoder := NewEncoder() 382 err := encoder.Encode(&s, vals) 383 if err != nil { 384 t.Fatalf("Failed to encode: %v", err) 385 } 386 387 valNotExists(t, "f01", vals) 388 valExists(t, "f02", "test", vals) 389 valNotExists(t, "f03", vals) 390 valNotExists(t, "f04", vals) 391 valNotExists(t, "f05", vals) 392 valNotExists(t, "f06", vals) 393 valExists(t, "f0601", "test", vals) 394 valNotExists(t, "f08", vals) 395 valsExist(t, "f09", []string{"test"}, vals) 396 } 397 398 type E6 struct { 399 F01 *inner 400 F02 *inner 401 F03 *inner `schema:",omitempty"` 402 } 403 404 func TestStructPointer(t *testing.T) { 405 vals := map[string][]string{} 406 s := E6{ 407 F01: &inner{2}, 408 } 409 410 encoder := NewEncoder() 411 err := encoder.Encode(&s, vals) 412 if err != nil { 413 t.Fatalf("Failed to encode: %v", err) 414 } 415 valExists(t, "F12", "2", vals) 416 valExists(t, "F02", "null", vals) 417 valNotExists(t, "F03", vals) 418 } 419 420 func TestRegisterEncoderCustomArrayType(t *testing.T) { 421 type CustomInt []int 422 type S1 struct { 423 SomeInts CustomInt `schema:",omitempty"` 424 } 425 426 ss := []S1{ 427 {}, 428 {CustomInt{}}, 429 {CustomInt{1, 2, 3}}, 430 } 431 432 for s := range ss { 433 vals := map[string][]string{} 434 435 encoder := NewEncoder() 436 encoder.RegisterEncoder(CustomInt{}, func(value reflect.Value) string { 437 return fmt.Sprint(value.Interface()) 438 }) 439 440 err := encoder.Encode(ss[s], vals) 441 if err != nil { 442 t.Fatalf("Failed to encode: %v", err) 443 } 444 } 445 } 446 447 func TestRegisterEncoderStructIsZero(t *testing.T) { 448 type S1 struct { 449 SomeTime1 time.Time `schema:"tim1,omitempty"` 450 SomeTime2 time.Time `schema:"tim2,omitempty"` 451 } 452 453 ss := []*S1{ 454 { 455 SomeTime1: time.Date(2020, 8, 4, 13, 30, 1, 0, time.UTC), 456 }, 457 } 458 459 for s := range ss { 460 vals := map[string][]string{} 461 462 encoder := NewEncoder() 463 encoder.RegisterEncoder(time.Time{}, func(value reflect.Value) string { 464 return value.Interface().(time.Time).Format(time.RFC3339Nano) 465 }) 466 467 err := encoder.Encode(ss[s], vals) 468 if err != nil { 469 t.Errorf("Encoder has non-nil error: %v", err) 470 } 471 472 ta, ok := vals["tim1"] 473 if !ok { 474 t.Error("expected tim1 to be present") 475 } 476 477 if len(ta) != 1 { 478 t.Error("expected tim1 to be present") 479 } 480 481 if ta[0] != "2020-08-04T13:30:01Z" { 482 t.Error("expected correct tim1 time") 483 } 484 485 _, ok = vals["tim2"] 486 if ok { 487 t.Error("expected tim1 not to be present") 488 } 489 } 490 } 491 492 func TestRegisterEncoderWithPtrType(t *testing.T) { 493 type CustomTime struct { 494 time time.Time 495 } 496 497 type S1 struct { 498 DateStart *CustomTime 499 DateEnd *CustomTime 500 Empty *CustomTime `schema:"empty,omitempty"` 501 } 502 503 ss := S1{ 504 DateStart: &CustomTime{time: time.Now()}, 505 DateEnd: nil, 506 } 507 508 encoder := NewEncoder() 509 encoder.RegisterEncoder(&CustomTime{}, func(value reflect.Value) string { 510 if value.IsNil() { 511 return "" 512 } 513 514 custom := value.Interface().(*CustomTime) 515 return custom.time.String() 516 }) 517 518 vals := map[string][]string{} 519 err := encoder.Encode(ss, vals) 520 521 noError(t, err) 522 valsLength(t, 2, vals) 523 valExists(t, "DateStart", ss.DateStart.time.String(), vals) 524 valExists(t, "DateEnd", "", vals) 525 }