Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / binder_test.go
1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package revel
6
7 import (
8         "encoding/json"
9         "fmt"
10         "io"
11         "io/ioutil"
12         "os"
13         "reflect"
14         "sort"
15         "strings"
16         "testing"
17         "time"
18 )
19
20 type A struct {
21         ID      int
22         Name    string
23         B       B
24         private int
25 }
26
27 type B struct {
28         Extra string
29 }
30
31 var (
32         ParamTestValues = map[string][]string{
33                 "int":                            {"1"},
34                 "int8":                           {"1"},
35                 "int16":                          {"1"},
36                 "int32":                          {"1"},
37                 "int64":                          {"1"},
38                 "uint":                           {"1"},
39                 "uint8":                          {"1"},
40                 "uint16":                         {"1"},
41                 "uint32":                         {"1"},
42                 "uint64":                         {"1"},
43                 "float32":                        {"1.000000"},
44                 "float64":                        {"1.000000"},
45                 "str":                            {"hello"},
46                 "bool-true":                      {"true"},
47                 "bool-1":                         {"1"},
48                 "bool-on":                        {"on"},
49                 "bool-false":                     {"false"},
50                 "bool-0":                         {"0"},
51                 "bool-0.0":                       {"0.0"},
52                 "bool-off":                       {"off"},
53                 "bool-f":                         {"f"},
54                 "date":                           {"1982-07-09"},
55                 "datetime":                       {"1982-07-09 21:30"},
56                 "customDate":                     {"07/09/1982"},
57                 "arr[0]":                         {"1"},
58                 "arr[1]":                         {"2"},
59                 "arr[3]":                         {"3"},
60                 "uarr[]":                         {"1", "2"},
61                 "arruarr[0][]":                   {"1", "2"},
62                 "arruarr[1][]":                   {"3", "4"},
63                 "2darr[0][0]":                    {"0"},
64                 "2darr[0][1]":                    {"1"},
65                 "2darr[1][0]":                    {"10"},
66                 "2darr[1][1]":                    {"11"},
67                 "A.ID":                           {"123"},
68                 "A.Name":                         {"rob"},
69                 "B.ID":                           {"123"},
70                 "B.Name":                         {"rob"},
71                 "B.B.Extra":                      {"hello"},
72                 "pB.ID":                          {"123"},
73                 "pB.Name":                        {"rob"},
74                 "pB.B.Extra":                     {"hello"},
75                 "priv.private":                   {"123"},
76                 "arrC[0].ID":                     {"5"},
77                 "arrC[0].Name":                   {"rob"},
78                 "arrC[0].B.Extra":                {"foo"},
79                 "arrC[1].ID":                     {"8"},
80                 "arrC[1].Name":                   {"bill"},
81                 "m[a]":                           {"foo"},
82                 "m[b]":                           {"bar"},
83                 "m2[1]":                          {"foo"},
84                 "m2[2]":                          {"bar"},
85                 "m3[a]":                          {"1"},
86                 "m3[b]":                          {"2"},
87                 "m4[a].ID":                       {"1"},
88                 "m4[a].Name":                     {"foo"},
89                 "m4[b].ID":                       {"2"},
90                 "m4[b].Name":                     {"bar"},
91                 "mapWithAMuchLongerName[a].ID":   {"1"},
92                 "mapWithAMuchLongerName[a].Name": {"foo"},
93                 "mapWithAMuchLongerName[b].ID":   {"2"},
94                 "mapWithAMuchLongerName[b].Name": {"bar"},
95                 "invalidInt":                     {"xyz"},
96                 "invalidInt2":                    {""},
97                 "invalidBool":                    {"xyz"},
98                 "invalidArr":                     {"xyz"},
99                 "int8-overflow":                  {"1024"},
100                 "uint8-overflow":                 {"1024"},
101         }
102
103         testDate     = time.Date(1982, time.July, 9, 0, 0, 0, 0, time.UTC)
104         testDatetime = time.Date(1982, time.July, 9, 21, 30, 0, 0, time.UTC)
105 )
106
107 var binderTestCases = map[string]interface{}{
108         "int":        1,
109         "int8":       int8(1),
110         "int16":      int16(1),
111         "int32":      int32(1),
112         "int64":      int64(1),
113         "uint":       1,
114         "uint8":      uint8(1),
115         "uint16":     uint16(1),
116         "uint32":     uint32(1),
117         "uint64":     uint64(1),
118         "float32":    float32(1.0),
119         "float64":    float64(1.0),
120         "str":        "hello",
121         "bool-true":  true,
122         "bool-1":     true,
123         "bool-on":    true,
124         "bool-false": false,
125         "bool-0":     false,
126         "bool-0.0":   false,
127         "bool-off":   false,
128         "bool-f":     false,
129         "date":       testDate,
130         "datetime":   testDatetime,
131         "customDate": testDate,
132         "arr":        []int{1, 2, 0, 3},
133         "uarr":       []int{1, 2},
134         "arruarr":    [][]int{{1, 2}, {3, 4}},
135         "2darr":      [][]int{{0, 1}, {10, 11}},
136         "A":          A{ID: 123, Name: "rob"},
137         "B":          A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
138         "pB":         &A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
139         "arrC": []A{
140                 {
141                         ID:   5,
142                         Name: "rob",
143                         B:    B{"foo"},
144                 },
145                 {
146                         ID:   8,
147                         Name: "bill",
148                 },
149         },
150         "m":  map[string]string{"a": "foo", "b": "bar"},
151         "m2": map[int]string{1: "foo", 2: "bar"},
152         "m3": map[string]int{"a": 1, "b": 2},
153         "m4": map[string]A{"a": {ID: 1, Name: "foo"}, "b": {ID: 2, Name: "bar"}},
154
155         // NOTE: We also include a map with a longer name than the others since this has caused problems
156         // described in github issue #1285, resolved in pull request #1344. This test case should
157         // prevent regression.
158         "mapWithAMuchLongerName": map[string]A{"a": {ID: 1, Name: "foo"}, "b": {ID: 2, Name: "bar"}},
159
160         // TODO: Tests that use TypeBinders
161
162         // Invalid value tests (the result should always be the zero value for that type)
163         // The point of these is to ensure that invalid user input does not cause panics.
164         "invalidInt":     0,
165         "invalidInt2":    0,
166         "invalidBool":    true,
167         "invalidArr":     []int{},
168         "priv":           A{},
169         "int8-overflow":  int8(0),
170         "uint8-overflow": uint8(0),
171 }
172
173 // Types that files may be bound to, and a func that can read the content from
174 // that type.
175 // TODO: Is there any way to create a slice, given only the element Type?
176 var fileBindings = []struct{ val, arrval, f interface{} }{
177         {(**os.File)(nil), []*os.File{}, ioutil.ReadAll},
178         {(*[]byte)(nil), [][]byte{}, func(b []byte) []byte { return b }},
179         {(*io.Reader)(nil), []io.Reader{}, ioutil.ReadAll},
180         {(*io.ReadSeeker)(nil), []io.ReadSeeker{}, ioutil.ReadAll},
181 }
182
183 func TestJsonBinder(t *testing.T) {
184         // create a structure to be populated
185         {
186                 d, _ := json.Marshal(map[string]int{"a": 1})
187                 params := &Params{JSON: d}
188                 foo := struct{ A int }{}
189                 c := NewTestController(nil, getMultipartRequest())
190
191                 ParseParams(params, NewRequest(c.Request.In))
192                 actual := Bind(params, "test", reflect.TypeOf(foo))
193                 valEq(t, "TestJsonBinder", reflect.ValueOf(actual.Interface().(struct{ A int }).A), reflect.ValueOf(1))
194         }
195         {
196                 d, _ := json.Marshal(map[string]interface{}{"a": map[string]int{"b": 45}})
197                 params := &Params{JSON: d}
198                 testMap := map[string]interface{}{}
199                 actual := Bind(params, "test", reflect.TypeOf(testMap)).Interface().(map[string]interface{})
200                 if actual["a"].(map[string]interface{})["b"].(float64) != 45 {
201                         t.Errorf("Failed to fetch map value %#v", actual["a"])
202                 }
203                 // Check to see if a named map works
204                 actualb := Bind(params, "test", reflect.TypeOf(map[string]map[string]float64{})).Interface().(map[string]map[string]float64)
205                 if actualb["a"]["b"] != 45 {
206                         t.Errorf("Failed to fetch map value %#v", actual["a"])
207                 }
208
209         }
210 }
211
212 func TestBinder(t *testing.T) {
213         // Reuse the mvc_test.go multipart request to test the binder.
214         params := &Params{}
215         c := NewTestController(nil, getMultipartRequest())
216         ParseParams(params, NewRequest(c.Request.In))
217         params.Values = ParamTestValues
218
219         // Values
220         for k, v := range binderTestCases {
221                 actual := Bind(params, k, reflect.TypeOf(v))
222                 expected := reflect.ValueOf(v)
223                 valEq(t, k, actual, expected)
224         }
225
226         // Files
227
228         // Get the keys in sorted order to make the expectation right.
229         keys := []string{}
230         for k := range expectedFiles {
231                 keys = append(keys, k)
232         }
233         sort.Strings(keys)
234
235         expectedBoundFiles := make(map[string][]fh)
236         for _, k := range keys {
237                 fhs := expectedFiles[k]
238                 k := nextKey(k)
239                 expectedBoundFiles[k] = append(expectedBoundFiles[k], fhs...)
240         }
241
242         for k, fhs := range expectedBoundFiles {
243
244                 if len(fhs) == 1 {
245                         // Test binding single files to: *os.File, []byte, io.Reader, io.ReadSeeker
246                         for _, binding := range fileBindings {
247                                 typ := reflect.TypeOf(binding.val).Elem()
248                                 actual := Bind(params, k, typ)
249                                 if !actual.IsValid() || (actual.Kind() == reflect.Interface && actual.IsNil()) {
250                                         t.Errorf("%s (%s) - Returned nil.", k, typ)
251                                         continue
252                                 }
253                                 returns := reflect.ValueOf(binding.f).Call([]reflect.Value{actual})
254                                 valEq(t, k, returns[0], reflect.ValueOf(fhs[0].content))
255                         }
256
257                 } else {
258                         // Test binding multi to:
259                         // []*os.File, [][]byte, []io.Reader, []io.ReadSeeker
260                         for _, binding := range fileBindings {
261                                 typ := reflect.TypeOf(binding.arrval)
262                                 actual := Bind(params, k, typ)
263                                 if actual.Len() != len(fhs) {
264                                         t.Fatalf("%s (%s) - Number of files: (expected) %d != %d (actual)",
265                                                 k, typ, len(fhs), actual.Len())
266                                 }
267                                 for i := range fhs {
268                                         returns := reflect.ValueOf(binding.f).Call([]reflect.Value{actual.Index(i)})
269                                         if !returns[0].IsValid() {
270                                                 t.Errorf("%s (%s) - Returned nil.", k, typ)
271                                                 continue
272                                         }
273                                         valEq(t, k, returns[0], reflect.ValueOf(fhs[i].content))
274                                 }
275                         }
276                 }
277         }
278 }
279
280 // Unbinding tests
281
282 var unbinderTestCases = map[string]interface{}{
283         "int":        1,
284         "int8":       int8(1),
285         "int16":      int16(1),
286         "int32":      int32(1),
287         "int64":      int64(1),
288         "uint":       1,
289         "uint8":      uint8(1),
290         "uint16":     uint16(1),
291         "uint32":     uint32(1),
292         "uint64":     uint64(1),
293         "float32":    float32(1.0),
294         "float64":    float64(1.0),
295         "str":        "hello",
296         "bool-true":  true,
297         "bool-false": false,
298         "date":       testDate,
299         "datetime":   testDatetime,
300         "arr":        []int{1, 2, 0, 3},
301         "2darr":      [][]int{{0, 1}, {10, 11}},
302         "A":          A{ID: 123, Name: "rob"},
303         "B":          A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
304         "pB":         &A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
305         "arrC": []A{
306                 {
307                         ID:   5,
308                         Name: "rob",
309                         B:    B{"foo"},
310                 },
311                 {
312                         ID:   8,
313                         Name: "bill",
314                 },
315         },
316         "m":  map[string]string{"a": "foo", "b": "bar"},
317         "m2": map[int]string{1: "foo", 2: "bar"},
318         "m3": map[string]int{"a": 1, "b": 2},
319 }
320
321 // Some of the unbinding results are not exactly what is in ParamTestValues, since it
322 // serializes implicit zero values explicitly.
323 var unbinderOverrideAnswers = map[string]map[string]string{
324         "arr": {
325                 "arr[0]": "1",
326                 "arr[1]": "2",
327                 "arr[2]": "0",
328                 "arr[3]": "3",
329         },
330         "A": {
331                 "A.ID":      "123",
332                 "A.Name":    "rob",
333                 "A.B.Extra": "",
334         },
335         "arrC": {
336                 "arrC[0].ID":      "5",
337                 "arrC[0].Name":    "rob",
338                 "arrC[0].B.Extra": "foo",
339                 "arrC[1].ID":      "8",
340                 "arrC[1].Name":    "bill",
341                 "arrC[1].B.Extra": "",
342         },
343         "m":  {"m[a]": "foo", "m[b]": "bar"},
344         "m2": {"m2[1]": "foo", "m2[2]": "bar"},
345         "m3": {"m3[a]": "1", "m3[b]": "2"},
346 }
347
348 func TestUnbinder(t *testing.T) {
349         for k, v := range unbinderTestCases {
350                 actual := make(map[string]string)
351                 Unbind(actual, k, v)
352
353                 // Get the expected key/values.
354                 expected, ok := unbinderOverrideAnswers[k]
355                 if !ok {
356                         expected = make(map[string]string)
357                         for k2, v2 := range ParamTestValues {
358                                 if k == k2 || strings.HasPrefix(k2, k+".") || strings.HasPrefix(k2, k+"[") {
359                                         expected[k2] = v2[0]
360                                 }
361                         }
362                 }
363
364                 // Compare length and values.
365                 if len(actual) != len(expected) {
366                         t.Errorf("Length mismatch\nExpected length %d, actual %d\nExpected: %s\nActual: %s",
367                                 len(expected), len(actual), expected, actual)
368                 }
369                 for k, v := range actual {
370                         if expected[k] != v {
371                                 t.Errorf("Value mismatch.\nExpected: %s\nActual: %s", expected, actual)
372                         }
373                 }
374         }
375 }
376
377 // Helpers
378
379 func valEq(t *testing.T, name string, actual, expected reflect.Value) {
380         switch expected.Kind() {
381         case reflect.Slice:
382                 // Check the type/length/element type
383                 if !eq(t, name+" (type)", actual.Kind(), expected.Kind()) ||
384                         !eq(t, name+" (len)", actual.Len(), expected.Len()) ||
385                         !eq(t, name+" (elem)", actual.Type().Elem(), expected.Type().Elem()) {
386                         return
387                 }
388
389                 // Check value equality for each element.
390                 for i := 0; i < actual.Len(); i++ {
391                         valEq(t, fmt.Sprintf("%s[%d]", name, i), actual.Index(i), expected.Index(i))
392                 }
393
394         case reflect.Ptr:
395                 // Check equality on the element type.
396                 valEq(t, name, actual.Elem(), expected.Elem())
397         case reflect.Map:
398                 if !eq(t, name+" (len)", actual.Len(), expected.Len()) {
399                         return
400                 }
401                 for _, key := range expected.MapKeys() {
402                         expectedValue := expected.MapIndex(key)
403                         actualValue := actual.MapIndex(key)
404                         if actualValue.IsValid() {
405                                 valEq(t, fmt.Sprintf("%s[%s]", name, key), actualValue, expectedValue)
406                         } else {
407                                 t.Errorf("Expected key %s not found", key)
408                         }
409                 }
410         default:
411                 eq(t, name, actual.Interface(), expected.Interface())
412         }
413 }
414
415 func init() {
416         DateFormat = DefaultDateFormat
417         DateTimeFormat = DefaultDateTimeFormat
418         TimeFormats = append(TimeFormats, DefaultDateFormat, DefaultDateTimeFormat, "01/02/2006")
419 }