ss/schema/encoder_test.go

525 lines
12 KiB
Go

package schema
import (
"fmt"
"reflect"
"testing"
"time"
)
type E1 struct {
F01 int `schema:"f01"`
F02 int `schema:"-"`
F03 string `schema:"f03"`
F04 string `schema:"f04,omitempty"`
F05 bool `schema:"f05"`
F06 bool `schema:"f06"`
F07 *string `schema:"f07"`
F08 *int8 `schema:"f08"`
F09 float64 `schema:"f09"`
F10 func() `schema:"f10"`
F11 inner
}
type inner struct {
F12 int
}
func TestFilled(t *testing.T) {
f07 := "seven"
var f08 int8 = 8
s := &E1{
F01: 1,
F02: 2,
F03: "three",
F04: "four",
F05: true,
F06: false,
F07: &f07,
F08: &f08,
F09: 1.618,
F10: func() {},
F11: inner{12},
}
vals := make(map[string][]string)
errs := NewEncoder().Encode(s, vals)
valExists(t, "f01", "1", vals)
valNotExists(t, "f02", vals)
valExists(t, "f03", "three", vals)
valExists(t, "f05", "true", vals)
valExists(t, "f06", "false", vals)
valExists(t, "f07", "seven", vals)
valExists(t, "f08", "8", vals)
valExists(t, "f09", "1.618000", vals)
valExists(t, "F12", "12", vals)
emptyErr := MultiError{}
if errs.Error() == emptyErr.Error() {
t.Errorf("Expected error got %v", errs)
}
}
type Aa int
type E3 struct {
F01 bool `schema:"f01"`
F02 float32 `schema:"f02"`
F03 float64 `schema:"f03"`
F04 int `schema:"f04"`
F05 int8 `schema:"f05"`
F06 int16 `schema:"f06"`
F07 int32 `schema:"f07"`
F08 int64 `schema:"f08"`
F09 string `schema:"f09"`
F10 uint `schema:"f10"`
F11 uint8 `schema:"f11"`
F12 uint16 `schema:"f12"`
F13 uint32 `schema:"f13"`
F14 uint64 `schema:"f14"`
F15 Aa `schema:"f15"`
}
// Test compatibility with default decoder types.
func TestCompat(t *testing.T) {
src := &E3{
F01: true,
F02: 4.2,
F03: 4.3,
F04: -42,
F05: -43,
F06: -44,
F07: -45,
F08: -46,
F09: "foo",
F10: 42,
F11: 43,
F12: 44,
F13: 45,
F14: 46,
F15: 1,
}
dst := &E3{}
vals := make(map[string][]string)
encoder := NewEncoder()
decoder := NewDecoder()
encoder.RegisterEncoder(src.F15, func(reflect.Value) string { return "1" })
decoder.RegisterConverter(src.F15, func(string) reflect.Value { return reflect.ValueOf(1) })
err := encoder.Encode(src, vals)
if err != nil {
t.Errorf("Encoder has non-nil error: %v", err)
}
err = decoder.Decode(dst, vals)
if err != nil {
t.Errorf("Decoder has non-nil error: %v", err)
}
if *src != *dst {
t.Errorf("Decoder-Encoder compatibility: expected %v, got %v\n", src, dst)
}
}
func TestEmpty(t *testing.T) {
s := &E1{
F01: 1,
F02: 2,
F03: "three",
}
estr := "schema: encoder not found for <nil>"
vals := make(map[string][]string)
err := NewEncoder().Encode(s, vals)
if err.Error() != estr {
t.Errorf("Expected: %s, got %v", estr, err)
}
valExists(t, "f03", "three", vals)
valNotExists(t, "f04", vals)
}
func TestStruct(t *testing.T) {
estr := "schema: interface must be a struct"
vals := make(map[string][]string)
err := NewEncoder().Encode("hello world", vals)
if err.Error() != estr {
t.Errorf("Expected: %s, got %v", estr, err)
}
}
func TestSlices(t *testing.T) {
type oneAsWord int
ones := []oneAsWord{1, 2}
s1 := &struct {
ones []oneAsWord `schema:"ones"`
ints []int `schema:"ints"`
nonempty []int `schema:"nonempty"`
empty []int `schema:"empty,omitempty"`
}{ones, []int{1, 1}, []int{}, []int{}}
vals := make(map[string][]string)
encoder := NewEncoder()
encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" })
err := encoder.Encode(s1, vals)
if err != nil {
t.Errorf("Encoder has non-nil error: %v", err)
}
valsExist(t, "ones", []string{"one", "one"}, vals)
valsExist(t, "ints", []string{"1", "1"}, vals)
valsExist(t, "nonempty", []string{}, vals)
valNotExists(t, "empty", vals)
}
func TestCompatSlices(t *testing.T) {
type oneAsWord int
type s1 struct {
Ones []oneAsWord `schema:"ones"`
Ints []int `schema:"ints"`
}
ones := []oneAsWord{1, 1}
src := &s1{ones, []int{1, 1}}
vals := make(map[string][]string)
dst := &s1{}
encoder := NewEncoder()
encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" })
decoder := NewDecoder()
decoder.RegisterConverter(ones[0], func(s string) reflect.Value {
if s == "one" {
return reflect.ValueOf(1)
}
return reflect.ValueOf(2)
})
err := encoder.Encode(src, vals)
if err != nil {
t.Errorf("Encoder has non-nil error: %v", err)
}
err = decoder.Decode(dst, vals)
if err != nil {
t.Errorf("Dncoder has non-nil error: %v", err)
}
if len(src.Ints) != len(dst.Ints) || len(src.Ones) != len(dst.Ones) {
t.Fatalf("Expected %v, got %v", src, dst)
}
for i, v := range src.Ones {
if dst.Ones[i] != v {
t.Fatalf("Expected %v, got %v", v, dst.Ones[i])
}
}
for i, v := range src.Ints {
if dst.Ints[i] != v {
t.Fatalf("Expected %v, got %v", v, dst.Ints[i])
}
}
}
func TestRegisterEncoder(t *testing.T) {
type oneAsWord int
type twoAsWord int
type oneSliceAsWord []int
s1 := &struct {
oneAsWord
twoAsWord
oneSliceAsWord
}{1, 2, []int{1, 1}}
v1 := make(map[string][]string)
encoder := NewEncoder()
encoder.RegisterEncoder(s1.oneAsWord, func(v reflect.Value) string { return "one" })
encoder.RegisterEncoder(s1.twoAsWord, func(v reflect.Value) string { return "two" })
encoder.RegisterEncoder(s1.oneSliceAsWord, func(v reflect.Value) string { return "one" })
err := encoder.Encode(s1, v1)
if err != nil {
t.Errorf("Encoder has non-nil error: %v", err)
}
valExists(t, "oneAsWord", "one", v1)
valExists(t, "twoAsWord", "two", v1)
valExists(t, "oneSliceAsWord", "one", v1)
}
func TestEncoderOrder(t *testing.T) {
type builtinEncoderSimple int
type builtinEncoderSimpleOverridden int
type builtinEncoderSlice []int
type builtinEncoderSliceOverridden []int
type builtinEncoderStruct struct{ nr int }
type builtinEncoderStructOverridden struct{ nr int }
s1 := &struct {
builtinEncoderSimple `schema:"simple"`
builtinEncoderSimpleOverridden `schema:"simple_overridden"`
builtinEncoderSlice `schema:"slice"`
builtinEncoderSliceOverridden `schema:"slice_overridden"`
builtinEncoderStruct `schema:"struct"`
builtinEncoderStructOverridden `schema:"struct_overridden"`
}{
1,
1,
[]int{2},
[]int{2},
builtinEncoderStruct{3},
builtinEncoderStructOverridden{3},
}
v1 := make(map[string][]string)
encoder := NewEncoder()
encoder.RegisterEncoder(s1.builtinEncoderSimpleOverridden, func(v reflect.Value) string { return "one" })
encoder.RegisterEncoder(s1.builtinEncoderSliceOverridden, func(v reflect.Value) string { return "two" })
encoder.RegisterEncoder(s1.builtinEncoderStructOverridden, func(v reflect.Value) string { return "three" })
err := encoder.Encode(s1, v1)
if err != nil {
t.Errorf("Encoder has non-nil error: %v", err)
}
valExists(t, "simple", "1", v1)
valExists(t, "simple_overridden", "one", v1)
valExists(t, "slice", "2", v1)
valExists(t, "slice_overridden", "two", v1)
valExists(t, "nr", "3", v1)
valExists(t, "struct_overridden", "three", v1)
}
func valExists(t *testing.T, key string, expect string, result map[string][]string) {
valsExist(t, key, []string{expect}, result)
}
func valsExist(t *testing.T, key string, expect []string, result map[string][]string) {
vals, ok := result[key]
if !ok {
t.Fatalf("Key not found. Expected: %s", key)
}
if len(expect) != len(vals) {
t.Fatalf("Expected: %v, got: %v", expect, vals)
}
for i, v := range expect {
if vals[i] != v {
t.Fatalf("Unexpected value. Expected: %v, got %v", v, vals[i])
}
}
}
func valNotExists(t *testing.T, key string, result map[string][]string) {
if val, ok := result[key]; ok {
t.Error("Key not omitted. Expected: empty; got: " + val[0] + ".")
}
}
func valsLength(t *testing.T, expectedLength int, result map[string][]string) {
length := len(result)
if length != expectedLength {
t.Errorf("Expected length of %v, but got %v", expectedLength, length)
}
}
func noError(t *testing.T, err error) {
if err != nil {
t.Errorf("Unexpected error. Got %v", err)
}
}
type E4 struct {
ID string `json:"id"`
}
func TestEncoderSetAliasTag(t *testing.T) {
data := map[string][]string{}
s := E4{
ID: "foo",
}
encoder := NewEncoder()
encoder.SetAliasTag("json")
err := encoder.Encode(&s, data)
if err != nil {
t.Fatalf("Failed to encode: %v", err)
}
valExists(t, "id", "foo", data)
}
type E5 struct {
F01 int `schema:"f01,omitempty"`
F02 string `schema:"f02,omitempty"`
F03 *string `schema:"f03,omitempty"`
F04 *int8 `schema:"f04,omitempty"`
F05 float64 `schema:"f05,omitempty"`
F06 E5F06 `schema:"f06,omitempty"`
F07 E5F06 `schema:"f07,omitempty"`
F08 []string `schema:"f08,omitempty"`
F09 []string `schema:"f09,omitempty"`
}
type E5F06 struct {
F0601 string `schema:"f0601,omitempty"`
}
func TestEncoderWithOmitempty(t *testing.T) {
vals := map[string][]string{}
s := E5{
F02: "test",
F07: E5F06{
F0601: "test",
},
F09: []string{"test"},
}
encoder := NewEncoder()
err := encoder.Encode(&s, vals)
if err != nil {
t.Fatalf("Failed to encode: %v", err)
}
valNotExists(t, "f01", vals)
valExists(t, "f02", "test", vals)
valNotExists(t, "f03", vals)
valNotExists(t, "f04", vals)
valNotExists(t, "f05", vals)
valNotExists(t, "f06", vals)
valExists(t, "f0601", "test", vals)
valNotExists(t, "f08", vals)
valsExist(t, "f09", []string{"test"}, vals)
}
type E6 struct {
F01 *inner
F02 *inner
F03 *inner `schema:",omitempty"`
}
func TestStructPointer(t *testing.T) {
vals := map[string][]string{}
s := E6{
F01: &inner{2},
}
encoder := NewEncoder()
err := encoder.Encode(&s, vals)
if err != nil {
t.Fatalf("Failed to encode: %v", err)
}
valExists(t, "F12", "2", vals)
valExists(t, "F02", "null", vals)
valNotExists(t, "F03", vals)
}
func TestRegisterEncoderCustomArrayType(t *testing.T) {
type CustomInt []int
type S1 struct {
SomeInts CustomInt `schema:",omitempty"`
}
ss := []S1{
{},
{CustomInt{}},
{CustomInt{1, 2, 3}},
}
for s := range ss {
vals := map[string][]string{}
encoder := NewEncoder()
encoder.RegisterEncoder(CustomInt{}, func(value reflect.Value) string {
return fmt.Sprint(value.Interface())
})
err := encoder.Encode(ss[s], vals)
if err != nil {
t.Fatalf("Failed to encode: %v", err)
}
}
}
func TestRegisterEncoderStructIsZero(t *testing.T) {
type S1 struct {
SomeTime1 time.Time `schema:"tim1,omitempty"`
SomeTime2 time.Time `schema:"tim2,omitempty"`
}
ss := []*S1{
{
SomeTime1: time.Date(2020, 8, 4, 13, 30, 1, 0, time.UTC),
},
}
for s := range ss {
vals := map[string][]string{}
encoder := NewEncoder()
encoder.RegisterEncoder(time.Time{}, func(value reflect.Value) string {
return value.Interface().(time.Time).Format(time.RFC3339Nano)
})
err := encoder.Encode(ss[s], vals)
if err != nil {
t.Errorf("Encoder has non-nil error: %v", err)
}
ta, ok := vals["tim1"]
if !ok {
t.Error("expected tim1 to be present")
}
if len(ta) != 1 {
t.Error("expected tim1 to be present")
}
if ta[0] != "2020-08-04T13:30:01Z" {
t.Error("expected correct tim1 time")
}
_, ok = vals["tim2"]
if ok {
t.Error("expected tim1 not to be present")
}
}
}
func TestRegisterEncoderWithPtrType(t *testing.T) {
type CustomTime struct {
time time.Time
}
type S1 struct {
DateStart *CustomTime
DateEnd *CustomTime
Empty *CustomTime `schema:"empty,omitempty"`
}
ss := S1{
DateStart: &CustomTime{time: time.Now()},
DateEnd: nil,
}
encoder := NewEncoder()
encoder.RegisterEncoder(&CustomTime{}, func(value reflect.Value) string {
if value.IsNil() {
return ""
}
custom := value.Interface().(*CustomTime)
return custom.time.String()
})
vals := map[string][]string{}
err := encoder.Encode(ss, vals)
noError(t, err)
valsLength(t, 2, vals)
valExists(t, "DateStart", ss.DateStart.time.String(), vals)
valExists(t, "DateEnd", "", vals)
}