1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENCE file for details.
4
package httprequest_test
10
gc "gopkg.in/check.v1"
13
"github.com/juju/httprequest"
16
type marshalSuite struct{}
18
var _ = gc.Suite(&marshalSuite{})
20
type embedded struct {
21
F1 string `json:"name"`
23
F3 *string `json:"address"`
26
var marshalTests = []struct {
31
expectURLString string
33
expectHeader http.Header
36
about: "struct with simple fields",
37
urlString: "http://localhost:8081/:F1",
39
F1 int `httprequest:",path"`
40
F2 string `httprequest:",form"`
45
expectURLString: "http://localhost:8081/99?F2=some+text",
47
about: "struct with renamed fields",
48
urlString: "http://localhost:8081/:name",
50
F1 string `httprequest:"name,path"`
51
F2 int `httprequest:"age,form"`
53
F1: "some random user",
56
expectURLString: "http://localhost:8081/some%20random%20user?age=42",
58
about: "fields without httprequest tags are ignored",
59
urlString: "http://localhost:8081/:name",
61
F1 string `httprequest:"name,path"`
62
F2 int `httprequest:"age,form"`
65
F1: "some random user",
67
F3: "some more random text",
69
expectURLString: "http://localhost:8081/some%20random%20user?age=42",
71
about: "pointer fields are correctly handled",
72
urlString: "http://localhost:8081/:name",
74
F1 *string `httprequest:"name,path"`
75
F2 *string `httprequest:"age,form"`
76
F3 *string `httprequest:"address,form"`
78
F1: newString("some random user"),
81
expectURLString: "http://localhost:8081/some%20random%20user?age=42",
83
about: "MarshalText called on TextMarshalers",
84
urlString: "http://localhost:8081/:param1/:param2",
86
F1 testMarshaler `httprequest:"param1,path"`
87
F2 *testMarshaler `httprequest:"param2,path"`
88
F3 testMarshaler `httprequest:"param3,form"`
89
F4 *testMarshaler `httprequest:"param4,form"`
92
F2: (*testMarshaler)(newString("test2")),
94
F4: (*testMarshaler)(newString("test4")),
96
expectURLString: "http://localhost:8081/test_test1/test_test2?param3=test_test3¶m4=test_test4",
98
about: "MarshalText not called on values that do not implement TextMarshaler",
99
urlString: "http://localhost:8081/user/:name/:surname",
101
F1 notTextMarshaler `httprequest:"name,path"`
102
F2 *notTextMarshaler `httprequest:"surname,path"`
105
F2: (*notTextMarshaler)(newString("surname")),
107
expectURLString: "http://localhost:8081/user/name/surname",
109
about: "MarshalText returns an error",
110
urlString: "http://localhost:8081/user/:name/:surname",
112
F1 testMarshaler `httprequest:"name,path"`
113
F2 *testMarshaler `httprequest:"surname,path"`
116
F2: (*testMarshaler)(newString("surname")),
118
expectError: "cannot marshal field: empty string",
120
about: "[]string field form value",
121
urlString: "http://localhost:8081/user",
123
F1 []string `httprequest:"users,form"`
125
F1: []string{"user1", "user2", "user3"},
127
expectURLString: "http://localhost:8081/user?users=user1&users=user2&users=user3",
129
about: "nil []string field form value",
130
urlString: "http://localhost:8081/user",
132
F1 *[]string `httprequest:"users,form"`
136
expectURLString: "http://localhost:8081/user",
138
about: "cannot marshal []string field to path",
139
urlString: "http://localhost:8081/:users",
141
F1 []string `httprequest:"users,path"`
143
F1: []string{"user1", "user2"},
145
expectError: `bad type \*struct { F1 \[\]string "httprequest:\\"users,path\\"" }: invalid target type \[\]string for path parameter`,
147
about: "[]string field fails to marshal to path",
148
urlString: "http://localhost:8081/user/:users",
150
F1 []string `httprequest:"users,path"`
152
F1: []string{"user1", "user2", "user3"},
154
expectError: "bad type .*: invalid target type.*",
156
about: "more than one field with body tag",
157
urlString: "http://localhost:8081/user",
160
F1 string `httprequest:"user,body"`
161
F2 int `httprequest:"age,body"`
166
expectError: "bad type .*: more than one body field specified",
168
about: "required path parameter, but not specified",
169
urlString: "http://localhost:8081/u/:username",
172
F1 string `httprequest:"user,body"`
176
expectError: `missing value for path parameter "username"`,
178
about: "marshal to body",
179
urlString: "http://localhost:8081/u",
182
F1 embedded `httprequest:"info,body"`
187
F3: newString("test address"),
190
expectBody: newString(`{"name":"test user","age":42,"address":"test address"}`),
192
about: "empty path wildcard",
193
urlString: "http://localhost:8081/u/:",
196
F1 string `httprequest:"user,body"`
200
expectError: "empty path parameter",
202
about: "nil field to form",
203
urlString: "http://localhost:8081/u",
205
F1 *string `httprequest:"user,form"`
207
expectURLString: "http://localhost:8081/u",
209
about: "nil field to path",
210
urlString: "http://localhost:8081/u",
212
F1 *string `httprequest:"user,path"`
214
expectURLString: "http://localhost:8081/u",
216
about: "marshal to body of a GET request",
217
urlString: "http://localhost:8081/u",
219
F1 string `httprequest:",body"`
223
// Providing a body to a GET request is unusual but
224
// some people do it anyway.
226
expectBody: newString(`"hello test"`),
228
about: "marshal to nil value to body",
229
urlString: "http://localhost:8081/u",
231
F1 *string `httprequest:",body"`
235
expectBody: newString(""),
237
about: "nil TextMarshaler",
238
urlString: "http://localhost:8081/u",
240
F1 *testMarshaler `httprequest:"surname,form"`
242
F1: (*testMarshaler)(nil),
244
expectURLString: "http://localhost:8081/u",
246
about: "marshal nil with Sprint",
247
urlString: "http://localhost:8081/u",
249
F1 *int `httprequest:"surname,form"`
253
expectURLString: "http://localhost:8081/u",
255
about: "marshal to path with * placeholder",
256
urlString: "http://localhost:8081/u/*name",
258
F1 string `httprequest:"name,path"`
262
expectURLString: "http://localhost:8081/u/test",
264
about: "marshal to path with * placeholder, but the marshaled value does not start with /",
265
urlString: "http://localhost:8081/u/*name",
267
F1 string `httprequest:"name,path"`
271
expectError: `value \"test\" for path parameter \"\*name\" does not start with required /`,
273
about: "* placeholder allowed only at the end",
274
urlString: "http://localhost:8081/u/*name/document",
276
F1 string `httprequest:"name,path"`
280
expectError: "star path parameter is not at end of path",
282
about: "unparsable base url string",
285
F1 string `httprequest:"name,form"`
289
expectError: `parse %%: invalid URL escape \"%%\"`,
291
about: "value cannot be marshaled to json",
292
urlString: "http://localhost",
295
F1 failJSONMarshaler `httprequest:"field,body"`
299
expectError: `cannot marshal field: cannot marshal request body: json: error calling MarshalJSON for type \*httprequest_test.failJSONMarshaler: marshal error`,
301
about: "url with query parameters",
302
urlString: "http://localhost?a=b",
305
F1 failJSONMarshaler `httprequest:"f1,form"`
309
expectURLString: "http://localhost?a=b&f1=test",
311
about: "url with query parameters no form",
312
urlString: "http://localhost?a=b",
315
expectURLString: "http://localhost?a=b",
317
about: "struct with headers",
318
urlString: "http://localhost:8081/",
320
F1 string `httprequest:",header"`
321
F2 int `httprequest:",header"`
322
F3 bool `httprequest:",header"`
328
expectURLString: "http://localhost:8081/",
329
expectHeader: http.Header{
330
"F1": []string{"some text"},
331
"F2": []string{"99"},
332
"F3": []string{"true"},
335
about: "struct with header slice",
336
urlString: "http://localhost:8081/:F1",
338
F1 int `httprequest:",path"`
339
F2 string `httprequest:",form"`
340
F3 []string `httprequest:",header"`
344
F3: []string{"A", "B", "C"},
346
expectURLString: "http://localhost:8081/99?F2=some+text",
347
expectHeader: http.Header{"F3": []string{"A", "B", "C"}},
349
about: "SetHeader called after marshaling",
350
urlString: "http://localhost:8081/",
351
val: &httprequest.CustomHeader{
353
F1 string `httprequest:",header"`
354
F2 int `httprequest:",header"`
355
F3 bool `httprequest:",header"`
361
SetHeaderFunc: func(h http.Header) {
362
h.Set("F2", "some other text")
365
expectURLString: "http://localhost:8081/",
366
expectHeader: http.Header{
367
"F1": []string{"some text"},
368
"F2": []string{"some other text"},
369
"F3": []string{"false"},
373
func getStruct() interface{} {
381
func (*marshalSuite) TestMarshal(c *gc.C) {
382
for i, test := range marshalTests {
383
c.Logf("%d: %s", i, test.about)
385
if test.method != "" {
388
req, err := httprequest.Marshal(test.urlString, method, test.val)
389
if test.expectError != "" {
390
c.Assert(err, gc.ErrorMatches, test.expectError)
393
c.Assert(err, gc.IsNil)
394
if test.expectURLString != "" {
395
c.Assert(req.URL.String(), gc.DeepEquals, test.expectURLString)
397
if test.expectBody != nil {
398
data, err := ioutil.ReadAll(req.Body)
399
c.Assert(err, gc.IsNil)
400
if *test.expectBody != "" {
401
c.Assert(req.Header.Get("Content-Type"), gc.Equals, "application/json")
403
c.Assert(string(data), gc.DeepEquals, *test.expectBody)
405
for k, v := range test.expectHeader {
406
c.Assert(req.Header[k], gc.DeepEquals, v)
411
type testMarshaler string
413
func (t *testMarshaler) MarshalText() ([]byte, error) {
415
return nil, errgo.New("empty string")
417
return []byte("test_" + *t), nil
420
type notTextMarshaler string
422
// MarshalText does *not* implement encoding.TextMarshaler
423
func (t *notTextMarshaler) MarshalText() {
424
panic("unexpected call")
427
type failJSONMarshaler string
429
func (*failJSONMarshaler) MarshalJSON() ([]byte, error) {
430
return nil, errgo.New("marshal error")