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  }