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