1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENCE file for details.
4
package httprequest_test
15
jc "github.com/juju/testing/checkers"
16
"github.com/julienschmidt/httprouter"
17
gc "gopkg.in/check.v1"
19
"github.com/juju/httprequest"
22
type unmarshalSuite struct{}
24
var _ = gc.Suite(&unmarshalSuite{})
26
var unmarshalTests = []struct {
30
params httprequest.Params
32
// TODO expectErrorCause func(error) bool
34
about: "struct with simple fields",
36
F1 int `httprequest:",form"`
37
F2 int `httprequest:",form"`
38
G1 string `httprequest:",path"`
39
G2 string `httprequest:",path"`
40
H string `httprequest:",body"`
41
UnknownForm string `httprequest:",form"`
42
UnknownPath string `httprequest:",path"`
50
params: httprequest.Params{
51
Request: &http.Request{
52
Header: http.Header{"Content-Type": {"application/json"}},
55
"F2": {"-35", "not a number"},
57
Body: body(`"h val"`),
59
PathVar: httprouter.Params{{
67
Value: "g1 wrong val",
71
about: "struct with renamed fields",
73
F1 int `httprequest:"x1,form"`
74
F2 int `httprequest:"x2,form"`
75
G1 string `httprequest:"g1,path"`
76
G2 string `httprequest:"g2,path"`
83
params: httprequest.Params{
84
Request: &http.Request{
87
"x2": {"-35", "not a number"},
90
PathVar: httprouter.Params{{
98
Value: "g1 wrong val",
102
about: "unexported fields are ignored",
104
f int `httprequest:",form"`
105
G int `httprequest:",form"`
109
params: httprequest.Params{
110
Request: &http.Request{
118
about: "unexported embedded type works ok",
127
params: httprequest.Params{
128
Request: &http.Request{
136
about: "unexported type for body is ignored",
138
foo sFG `httprequest:",body"`
140
params: httprequest.Params{
141
Request: &http.Request{
142
Header: http.Header{"Content-Type": {"application/json"}},
143
Body: body(`{"F": 99, "G": 100}`),
147
about: "fields without httprequest tags are ignored",
151
params: httprequest.Params{
152
Request: &http.Request{
157
PathVar: httprouter.Params{{
163
about: "pointer fields are filled out",
165
F *int `httprequest:",form"`
167
S *string `httprequest:",form"`
168
T *string `httprequest:",form"`
175
S: newString("s val"),
177
params: httprequest.Params{
178
Request: &http.Request{
187
about: "UnmarshalText called on TextUnmarshalers",
189
F exclamationUnmarshaler `httprequest:",form"`
190
G exclamationUnmarshaler `httprequest:",path"`
191
FP *exclamationUnmarshaler `httprequest:",form"`
195
FP: (*exclamationUnmarshaler)(newString("maybe!")),
197
params: httprequest.Params{
198
Request: &http.Request{
204
PathVar: httprouter.Params{{
210
about: "UnmarshalText not called on values with a non-TextUnmarshaler UnmarshalText method",
212
F notTextUnmarshaler `httprequest:",form"`
216
params: httprequest.Params{
217
Request: &http.Request{
224
about: "UnmarshalText returning an error",
226
F exclamationUnmarshaler `httprequest:",form"`
228
params: httprequest.Params{
229
Request: &http.Request{},
231
expectError: "cannot unmarshal into field: empty string!",
233
about: "all field form values",
235
A []string `httprequest:",form"`
236
B *[]string `httprequest:",form"`
237
C []string `httprequest:",form"`
238
D *[]string `httprequest:",form"`
240
A: []string{"a1", "a2"},
241
B: func() *[]string {
242
x := []string{"b1", "b2", "b3"}
246
params: httprequest.Params{
247
Request: &http.Request{
250
"B": {"b1", "b2", "b3"},
255
about: "invalid scan field",
257
A int `httprequest:",form"`
259
params: httprequest.Params{
260
Request: &http.Request{
266
expectError: `cannot unmarshal into field: cannot parse "not an int" into int: expected integer`,
268
about: "scan field not present",
270
A int `httprequest:",form"`
272
params: httprequest.Params{
273
Request: &http.Request{},
276
about: "invalid JSON body",
278
A string `httprequest:",body"`
280
params: httprequest.Params{
281
Request: &http.Request{
282
Header: http.Header{"Content-Type": {"application/json"}},
283
Body: body("invalid JSON"),
286
expectError: "cannot unmarshal into field: cannot unmarshal request body: invalid character 'i' looking for beginning of value",
288
about: "body with read error",
290
A string `httprequest:",body"`
292
params: httprequest.Params{
293
Request: &http.Request{
294
Header: http.Header{"Content-Type": {"application/json"}},
295
Body: errorReader("some error"),
298
expectError: "cannot unmarshal into field: cannot read request body: some error",
300
about: "[]string not allowed for URL source",
302
A []string `httprequest:",path"`
304
expectError: `bad type .*: invalid target type \[]string for path parameter`,
306
about: "duplicated body",
308
B1 int `httprequest:",body"`
309
B2 string `httprequest:",body"`
311
expectError: "bad type .*: more than one body field specified",
313
about: "body tag name is ignored",
315
B string `httprequest:"foo,body"`
319
params: httprequest.Params{
320
Request: &http.Request{
321
Header: http.Header{"Content-Type": {"application/json"}},
322
Body: body(`"hello"`),
326
about: "tag with invalid source",
328
B1 int `httprequest:",xxx"`
330
expectError: `bad type .*: bad tag "httprequest:\\",xxx\\"" in field B1: unknown tag flag "xxx"`,
332
about: "non-struct pointer",
334
expectError: `bad type \*int: type is not pointer to struct`,
336
about: "unmarshaling with wrong request content type",
338
A string `httprequest:",body"`
340
params: httprequest.Params{
341
Request: &http.Request{
342
Header: http.Header{"Content-Type": {"text/html"}},
343
Body: body("invalid JSON"),
346
expectError: `cannot unmarshal into field: unexpected content type text/html; want application/json; content: invalid JSON`,
348
about: "struct with header fields",
350
F1 int `httprequest:"x1,header"`
351
G1 string `httprequest:"g1,header"`
356
params: httprequest.Params{
357
Request: &http.Request{
365
about: "all field header values",
367
A []string `httprequest:",header"`
368
B *[]string `httprequest:",header"`
369
C []string `httprequest:",header"`
370
D *[]string `httprequest:",header"`
372
A: []string{"a1", "a2"},
373
B: func() *[]string {
374
x := []string{"b1", "b2", "b3"}
378
params: httprequest.Params{
379
Request: &http.Request{
382
"B": {"b1", "b2", "b3"},
389
F int `httprequest:",form"`
390
G int `httprequest:",form"`
394
F int `httprequest:",form"`
395
G int `httprequest:",form"`
398
func (*unmarshalSuite) TestUnmarshal(c *gc.C) {
399
for i, test := range unmarshalTests {
400
c.Logf("%d: %s", i, test.about)
401
t := reflect.TypeOf(test.val)
402
fillv := reflect.New(t)
403
err := httprequest.Unmarshal(test.params, fillv.Interface())
404
if test.expectError != "" {
405
c.Assert(err, gc.ErrorMatches, test.expectError)
408
c.Assert(fillv.Elem().Interface(), jc.DeepEquals, test.val)
412
// TODO non-pointer struct
414
type notTextUnmarshaler string
416
// UnmarshalText does *not* implement encoding.TextUnmarshaler
417
// (it has no arguments or error return value)
418
func (t *notTextUnmarshaler) UnmarshalText() {
419
panic("unexpected call")
422
type exclamationUnmarshaler string
424
func (t *exclamationUnmarshaler) UnmarshalText(b []byte) error {
426
return fmt.Errorf("empty string!")
428
*t = exclamationUnmarshaler(b) + "!"
432
func newInt(i int) *int {
436
func newString(s string) *string {
440
type errorReader string
442
func (r errorReader) Read([]byte) (int, error) {
443
return 0, fmt.Errorf("%s", r)
446
func (r errorReader) Close() error {
450
func body(s string) io.ReadCloser {
451
return ioutil.NopCloser(strings.NewReader(s))