1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package middleware
16
17 import (
18 "errors"
19 "reflect"
20 "strings"
21 "testing"
22 "time"
23
24 "github.com/go-openapi/spec"
25 "github.com/go-openapi/strfmt"
26 "github.com/go-openapi/swag"
27 "github.com/stretchr/testify/assert"
28 "github.com/stretchr/testify/require"
29 )
30
31 var evaluatesAsTrue = []string{"true", "1", "yes", "ok", "y", "on", "selected", "checked", "t", "enabled"}
32
33 type unmarshallerSlice []string
34
35 func (u *unmarshallerSlice) UnmarshalText(data []byte) error {
36 if len(data) == 0 {
37 return errors.New("an error")
38 }
39 *u = strings.Split(string(data), ",")
40 return nil
41 }
42
43 type SomeOperationParams struct {
44 Name string
45 ID int64
46 Confirmed bool
47 Age int
48 Visits int32
49 Count int16
50 Seq int8
51 UID uint64
52 UAge uint
53 UVisits uint32
54 UCount uint16
55 USeq uint8
56 Score float32
57 Rate float64
58 Timestamp strfmt.DateTime
59 Birthdate strfmt.Date
60 LastFailure *strfmt.DateTime
61 Unsupported struct{}
62 Tags []string
63 Prefs []int32
64 Categories unmarshallerSlice
65 }
66
67 func FloatParamTest(t *testing.T, _, pName, _ string, val reflect.Value, defVal, expectedDef interface{}, actual func() interface{}) {
68 fld := val.FieldByName(pName)
69 binder := &untypedParamBinder{
70 parameter: spec.QueryParam(pName).Typed("number", "double").WithDefault(defVal),
71 Name: pName,
72 }
73
74 err := binder.setFieldValue(fld, defVal, "5", true)
75 require.NoError(t, err)
76 assert.EqualValues(t, 5, actual())
77
78 err = binder.setFieldValue(fld, defVal, "", true)
79 require.NoError(t, err)
80 assert.EqualValues(t, expectedDef, actual())
81
82 err = binder.setFieldValue(fld, defVal, "yada", true)
83 require.Error(t, err)
84 }
85
86 func IntParamTest(t *testing.T, pName string, val reflect.Value, defVal, expectedDef interface{}, actual func() interface{}) {
87 fld := val.FieldByName(pName)
88
89 binder := &untypedParamBinder{
90 parameter: spec.QueryParam(pName).Typed("integer", "int64").WithDefault(defVal),
91 Name: pName,
92 }
93 err := binder.setFieldValue(fld, defVal, "5", true)
94 require.NoError(t, err)
95 assert.EqualValues(t, 5, actual())
96
97 err = binder.setFieldValue(fld, defVal, "", true)
98 require.NoError(t, err)
99 assert.EqualValues(t, expectedDef, actual())
100
101 err = binder.setFieldValue(fld, defVal, "yada", true)
102 require.Error(t, err)
103 }
104
105 func TestParamBinding(t *testing.T) {
106 actual := new(SomeOperationParams)
107 val := reflect.ValueOf(actual).Elem()
108 pName := "Name"
109 fld := val.FieldByName(pName)
110
111 binder := &untypedParamBinder{
112 parameter: spec.QueryParam(pName).Typed(typeString, "").WithDefault("some-name"),
113 Name: pName,
114 }
115
116 err := binder.setFieldValue(fld, "some-name", "the name value", true)
117 require.NoError(t, err)
118 assert.Equal(t, "the name value", actual.Name)
119
120 err = binder.setFieldValue(fld, "some-name", "", true)
121 require.NoError(t, err)
122 assert.Equal(t, "some-name", actual.Name)
123
124 IntParamTest(t, "ID", val, 1, 1, func() interface{} { return actual.ID })
125 IntParamTest(t, "ID", val, nil, 0, func() interface{} { return actual.ID })
126 IntParamTest(t, "Age", val, 1, 1, func() interface{} { return actual.Age })
127 IntParamTest(t, "Age", val, nil, 0, func() interface{} { return actual.Age })
128 IntParamTest(t, "Visits", val, 1, 1, func() interface{} { return actual.Visits })
129 IntParamTest(t, "Visits", val, nil, 0, func() interface{} { return actual.Visits })
130 IntParamTest(t, "Count", val, 1, 1, func() interface{} { return actual.Count })
131 IntParamTest(t, "Count", val, nil, 0, func() interface{} { return actual.Count })
132 IntParamTest(t, "Seq", val, 1, 1, func() interface{} { return actual.Seq })
133 IntParamTest(t, "Seq", val, nil, 0, func() interface{} { return actual.Seq })
134 IntParamTest(t, "UID", val, uint64(1), 1, func() interface{} { return actual.UID })
135 IntParamTest(t, "UID", val, uint64(0), 0, func() interface{} { return actual.UID })
136 IntParamTest(t, "UAge", val, uint(1), 1, func() interface{} { return actual.UAge })
137 IntParamTest(t, "UAge", val, nil, 0, func() interface{} { return actual.UAge })
138 IntParamTest(t, "UVisits", val, uint32(1), 1, func() interface{} { return actual.UVisits })
139 IntParamTest(t, "UVisits", val, nil, 0, func() interface{} { return actual.UVisits })
140 IntParamTest(t, "UCount", val, uint16(1), 1, func() interface{} { return actual.UCount })
141 IntParamTest(t, "UCount", val, nil, 0, func() interface{} { return actual.UCount })
142 IntParamTest(t, "USeq", val, uint8(1), 1, func() interface{} { return actual.USeq })
143 IntParamTest(t, "USeq", val, nil, 0, func() interface{} { return actual.USeq })
144
145 FloatParamTest(t, "score", "Score", "float", val, 1.0, 1, func() interface{} { return actual.Score })
146 FloatParamTest(t, "score", "Score", "float", val, nil, 0, func() interface{} { return actual.Score })
147 FloatParamTest(t, "rate", "Rate", "double", val, 1.0, 1, func() interface{} { return actual.Rate })
148 FloatParamTest(t, "rate", "Rate", "double", val, nil, 0, func() interface{} { return actual.Rate })
149
150 pName = "Confirmed"
151 confirmedField := val.FieldByName(pName)
152 binder = &untypedParamBinder{
153 parameter: spec.QueryParam(pName).Typed("boolean", "").WithDefault(true),
154 Name: pName,
155 }
156
157 for _, tv := range evaluatesAsTrue {
158 err = binder.setFieldValue(confirmedField, true, tv, true)
159 require.NoError(t, err)
160 assert.True(t, actual.Confirmed)
161 }
162
163 err = binder.setFieldValue(confirmedField, true, "", true)
164 require.NoError(t, err)
165 assert.True(t, actual.Confirmed)
166
167 err = binder.setFieldValue(confirmedField, true, "0", true)
168 require.NoError(t, err)
169 assert.False(t, actual.Confirmed)
170
171 pName = "Timestamp"
172 timeField := val.FieldByName(pName)
173 dt := strfmt.DateTime(time.Date(2014, 3, 19, 2, 9, 0, 0, time.UTC))
174 binder = &untypedParamBinder{
175 parameter: spec.QueryParam(pName).Typed(typeString, "date-time").WithDefault(dt),
176 Name: pName,
177 }
178 exp := strfmt.DateTime(time.Date(2014, 5, 14, 2, 9, 0, 0, time.UTC))
179
180 err = binder.setFieldValue(timeField, dt, exp.String(), true)
181 require.NoError(t, err)
182 assert.Equal(t, exp, actual.Timestamp)
183
184 err = binder.setFieldValue(timeField, dt, "", true)
185 require.NoError(t, err)
186 assert.Equal(t, dt, actual.Timestamp)
187
188 err = binder.setFieldValue(timeField, dt, "yada", true)
189 require.Error(t, err)
190
191 ddt := strfmt.Date(time.Date(2014, 3, 19, 0, 0, 0, 0, time.UTC))
192 pName = "Birthdate"
193 dateField := val.FieldByName(pName)
194 binder = &untypedParamBinder{
195 parameter: spec.QueryParam(pName).Typed(typeString, "date").WithDefault(ddt),
196 Name: pName,
197 }
198 expd := strfmt.Date(time.Date(2014, 5, 14, 0, 0, 0, 0, time.UTC))
199
200 err = binder.setFieldValue(dateField, ddt, expd.String(), true)
201 require.NoError(t, err)
202 assert.Equal(t, expd, actual.Birthdate)
203
204 err = binder.setFieldValue(dateField, ddt, "", true)
205 require.NoError(t, err)
206 assert.Equal(t, ddt, actual.Birthdate)
207
208 err = binder.setFieldValue(dateField, ddt, "yada", true)
209 require.Error(t, err)
210
211 dt = strfmt.DateTime(time.Date(2014, 3, 19, 2, 9, 0, 0, time.UTC))
212 fdt := &dt
213 pName = "LastFailure"
214 ftimeField := val.FieldByName(pName)
215 binder = &untypedParamBinder{
216 parameter: spec.QueryParam(pName).Typed(typeString, "date").WithDefault(fdt),
217 Name: pName,
218 }
219 exp = strfmt.DateTime(time.Date(2014, 5, 14, 2, 9, 0, 0, time.UTC))
220 fexp := &exp
221
222 err = binder.setFieldValue(ftimeField, fdt, fexp.String(), true)
223 require.NoError(t, err)
224 assert.Equal(t, fexp, actual.LastFailure)
225
226 err = binder.setFieldValue(ftimeField, fdt, "", true)
227 require.NoError(t, err)
228 assert.Equal(t, fdt, actual.LastFailure)
229
230 err = binder.setFieldValue(ftimeField, fdt, "", true)
231 require.NoError(t, err)
232 assert.Equal(t, fdt, actual.LastFailure)
233
234 actual.LastFailure = nil
235 err = binder.setFieldValue(ftimeField, fdt, "yada", true)
236 require.Error(t, err)
237 assert.Nil(t, actual.LastFailure)
238
239 pName = "Unsupported"
240 unsupportedField := val.FieldByName(pName)
241 binder = &untypedParamBinder{
242 parameter: spec.QueryParam(pName).Typed(typeString, ""),
243 Name: pName,
244 }
245 err = binder.setFieldValue(unsupportedField, nil, "", true)
246 require.Error(t, err)
247 }
248
249 func TestSliceConversion(t *testing.T) {
250 actual := new(SomeOperationParams)
251 val := reflect.ValueOf(actual).Elem()
252
253
254
255
256
257
258 sliced := []string{"some", typeString, "values"}
259 seps := map[string]string{"ssv": " ", "tsv": "\t", "pipes": "|", "csv": ",", "": ","}
260
261 tagsField := val.FieldByName("Tags")
262 for k, sep := range seps {
263 binder := &untypedParamBinder{
264 Name: "Tags",
265 parameter: spec.QueryParam("tags").CollectionOf(stringItems, k),
266 }
267
268 actual.Tags = nil
269 cData := strings.Join(sliced, sep)
270 tags, _, err := binder.readFormattedSliceFieldValue(cData, tagsField)
271 require.NoError(t, err)
272 assert.Equal(t, sliced, tags)
273 cData = strings.Join(sliced, " "+sep+" ")
274 tags, _, err = binder.readFormattedSliceFieldValue(cData, tagsField)
275 require.NoError(t, err)
276 assert.Equal(t, sliced, tags)
277 tags, _, err = binder.readFormattedSliceFieldValue("", tagsField)
278 require.NoError(t, err)
279 assert.Empty(t, tags)
280 }
281
282 assert.Nil(t, swag.SplitByFormat("yada", "multi"))
283 assert.Nil(t, swag.SplitByFormat("", ""))
284
285 categoriesField := val.FieldByName("Categories")
286 binder := &untypedParamBinder{
287 Name: "Categories",
288 parameter: spec.QueryParam("categories").CollectionOf(stringItems, "csv"),
289 }
290 cData := strings.Join(sliced, ",")
291 categories, custom, err := binder.readFormattedSliceFieldValue(cData, categoriesField)
292 require.NoError(t, err)
293 assert.EqualValues(t, sliced, actual.Categories)
294 assert.True(t, custom)
295 assert.Empty(t, categories)
296 categories, custom, err = binder.readFormattedSliceFieldValue("", categoriesField)
297 require.Error(t, err)
298 assert.True(t, custom)
299 assert.Empty(t, categories)
300 }
301
View as plain text