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.
32 ParamTestValues = map[string][]string{
43 "float32": {"1.000000"},
44 "float64": {"1.000000"},
46 "bool-true": {"true"},
49 "bool-false": {"false"},
54 "date": {"1982-07-09"},
55 "datetime": {"1982-07-09 21:30"},
56 "customDate": {"07/09/1982"},
61 "arruarr[0][]": {"1", "2"},
62 "arruarr[1][]": {"3", "4"},
65 "2darr[1][0]": {"10"},
66 "2darr[1][1]": {"11"},
71 "B.B.Extra": {"hello"},
74 "pB.B.Extra": {"hello"},
75 "priv.private": {"123"},
77 "arrC[0].Name": {"rob"},
78 "arrC[0].B.Extra": {"foo"},
80 "arrC[1].Name": {"bill"},
88 "m4[a].Name": {"foo"},
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"},
97 "invalidBool": {"xyz"},
98 "invalidArr": {"xyz"},
99 "int8-overflow": {"1024"},
100 "uint8-overflow": {"1024"},
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)
107 var binderTestCases = map[string]interface{}{
118 "float32": float32(1.0),
119 "float64": float64(1.0),
130 "datetime": testDatetime,
131 "customDate": testDate,
132 "arr": []int{1, 2, 0, 3},
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"}},
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"}},
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"}},
160 // TODO: Tests that use TypeBinders
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.
167 "invalidArr": []int{},
169 "int8-overflow": int8(0),
170 "uint8-overflow": uint8(0),
173 // Types that files may be bound to, and a func that can read the content from
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},
183 func TestJsonBinder(t *testing.T) {
184 // create a structure to be populated
186 d, _ := json.Marshal(map[string]int{"a": 1})
187 params := &Params{JSON: d}
188 foo := struct{ A int }{}
189 c := NewTestController(nil, getMultipartRequest())
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))
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"])
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"])
212 func TestBinder(t *testing.T) {
213 // Reuse the mvc_test.go multipart request to test the binder.
215 c := NewTestController(nil, getMultipartRequest())
216 ParseParams(params, NewRequest(c.Request.In))
217 params.Values = ParamTestValues
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)
228 // Get the keys in sorted order to make the expectation right.
230 for k := range expectedFiles {
231 keys = append(keys, k)
235 expectedBoundFiles := make(map[string][]fh)
236 for _, k := range keys {
237 fhs := expectedFiles[k]
239 expectedBoundFiles[k] = append(expectedBoundFiles[k], fhs...)
242 for k, fhs := range expectedBoundFiles {
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)
253 returns := reflect.ValueOf(binding.f).Call([]reflect.Value{actual})
254 valEq(t, k, returns[0], reflect.ValueOf(fhs[0].content))
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())
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)
273 valEq(t, k, returns[0], reflect.ValueOf(fhs[i].content))
282 var unbinderTestCases = map[string]interface{}{
293 "float32": float32(1.0),
294 "float64": float64(1.0),
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"}},
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},
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{
337 "arrC[0].Name": "rob",
338 "arrC[0].B.Extra": "foo",
340 "arrC[1].Name": "bill",
341 "arrC[1].B.Extra": "",
343 "m": {"m[a]": "foo", "m[b]": "bar"},
344 "m2": {"m2[1]": "foo", "m2[2]": "bar"},
345 "m3": {"m3[a]": "1", "m3[b]": "2"},
348 func TestUnbinder(t *testing.T) {
349 for k, v := range unbinderTestCases {
350 actual := make(map[string]string)
353 // Get the expected key/values.
354 expected, ok := unbinderOverrideAnswers[k]
356 expected = make(map[string]string)
357 for k2, v2 := range ParamTestValues {
358 if k == k2 || strings.HasPrefix(k2, k+".") || strings.HasPrefix(k2, k+"[") {
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)
369 for k, v := range actual {
370 if expected[k] != v {
371 t.Errorf("Value mismatch.\nExpected: %s\nActual: %s", expected, actual)
379 func valEq(t *testing.T, name string, actual, expected reflect.Value) {
380 switch expected.Kind() {
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()) {
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))
395 // Check equality on the element type.
396 valEq(t, name, actual.Elem(), expected.Elem())
398 if !eq(t, name+" (len)", actual.Len(), expected.Len()) {
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)
407 t.Errorf("Expected key %s not found", key)
411 eq(t, name, actual.Interface(), expected.Interface())
416 DateFormat = DefaultDateFormat
417 DateTimeFormat = DefaultDateTimeFormat
418 TimeFormats = append(TimeFormats, DefaultDateFormat, DefaultDateTimeFormat, "01/02/2006")