~nskaggs/+junk/juju-packaging-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/httprequest/marshal_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-27 20:23:11 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161027202311-sux4jk2o73p1d6rg
Re-add src

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENCE file for details.
 
3
 
 
4
package httprequest_test
 
5
 
 
6
import (
 
7
        "io/ioutil"
 
8
        "net/http"
 
9
 
 
10
        gc "gopkg.in/check.v1"
 
11
        "gopkg.in/errgo.v1"
 
12
 
 
13
        "github.com/juju/httprequest"
 
14
)
 
15
 
 
16
type marshalSuite struct{}
 
17
 
 
18
var _ = gc.Suite(&marshalSuite{})
 
19
 
 
20
type embedded struct {
 
21
        F1 string  `json:"name"`
 
22
        F2 int     `json:"age"`
 
23
        F3 *string `json:"address"`
 
24
}
 
25
 
 
26
var marshalTests = []struct {
 
27
        about           string
 
28
        urlString       string
 
29
        method          string
 
30
        val             interface{}
 
31
        expectURLString string
 
32
        expectBody      *string
 
33
        expectHeader    http.Header
 
34
        expectError     string
 
35
}{{
 
36
        about:     "struct with simple fields",
 
37
        urlString: "http://localhost:8081/:F1",
 
38
        val: &struct {
 
39
                F1 int    `httprequest:",path"`
 
40
                F2 string `httprequest:",form"`
 
41
        }{
 
42
                F1: 99,
 
43
                F2: "some text",
 
44
        },
 
45
        expectURLString: "http://localhost:8081/99?F2=some+text",
 
46
}, {
 
47
        about:     "struct with renamed fields",
 
48
        urlString: "http://localhost:8081/:name",
 
49
        val: &struct {
 
50
                F1 string `httprequest:"name,path"`
 
51
                F2 int    `httprequest:"age,form"`
 
52
        }{
 
53
                F1: "some random user",
 
54
                F2: 42,
 
55
        },
 
56
        expectURLString: "http://localhost:8081/some%20random%20user?age=42",
 
57
}, {
 
58
        about:     "fields without httprequest tags are ignored",
 
59
        urlString: "http://localhost:8081/:name",
 
60
        val: &struct {
 
61
                F1 string `httprequest:"name,path"`
 
62
                F2 int    `httprequest:"age,form"`
 
63
                F3 string
 
64
        }{
 
65
                F1: "some random user",
 
66
                F2: 42,
 
67
                F3: "some more random text",
 
68
        },
 
69
        expectURLString: "http://localhost:8081/some%20random%20user?age=42",
 
70
}, {
 
71
        about:     "pointer fields are correctly handled",
 
72
        urlString: "http://localhost:8081/:name",
 
73
        val: &struct {
 
74
                F1 *string `httprequest:"name,path"`
 
75
                F2 *string `httprequest:"age,form"`
 
76
                F3 *string `httprequest:"address,form"`
 
77
        }{
 
78
                F1: newString("some random user"),
 
79
                F2: newString("42"),
 
80
        },
 
81
        expectURLString: "http://localhost:8081/some%20random%20user?age=42",
 
82
}, {
 
83
        about:     "MarshalText called on TextMarshalers",
 
84
        urlString: "http://localhost:8081/:param1/:param2",
 
85
        val: &struct {
 
86
                F1 testMarshaler  `httprequest:"param1,path"`
 
87
                F2 *testMarshaler `httprequest:"param2,path"`
 
88
                F3 testMarshaler  `httprequest:"param3,form"`
 
89
                F4 *testMarshaler `httprequest:"param4,form"`
 
90
        }{
 
91
                F1: "test1",
 
92
                F2: (*testMarshaler)(newString("test2")),
 
93
                F3: "test3",
 
94
                F4: (*testMarshaler)(newString("test4")),
 
95
        },
 
96
        expectURLString: "http://localhost:8081/test_test1/test_test2?param3=test_test3&param4=test_test4",
 
97
}, {
 
98
        about:     "MarshalText not called on values that do not implement TextMarshaler",
 
99
        urlString: "http://localhost:8081/user/:name/:surname",
 
100
        val: &struct {
 
101
                F1 notTextMarshaler  `httprequest:"name,path"`
 
102
                F2 *notTextMarshaler `httprequest:"surname,path"`
 
103
        }{
 
104
                F1: "name",
 
105
                F2: (*notTextMarshaler)(newString("surname")),
 
106
        },
 
107
        expectURLString: "http://localhost:8081/user/name/surname",
 
108
}, {
 
109
        about:     "MarshalText returns an error",
 
110
        urlString: "http://localhost:8081/user/:name/:surname",
 
111
        val: &struct {
 
112
                F1 testMarshaler  `httprequest:"name,path"`
 
113
                F2 *testMarshaler `httprequest:"surname,path"`
 
114
        }{
 
115
                F1: "",
 
116
                F2: (*testMarshaler)(newString("surname")),
 
117
        },
 
118
        expectError: "cannot marshal field: empty string",
 
119
}, {
 
120
        about:     "[]string field form value",
 
121
        urlString: "http://localhost:8081/user",
 
122
        val: &struct {
 
123
                F1 []string `httprequest:"users,form"`
 
124
        }{
 
125
                F1: []string{"user1", "user2", "user3"},
 
126
        },
 
127
        expectURLString: "http://localhost:8081/user?users=user1&users=user2&users=user3",
 
128
}, {
 
129
        about:     "nil []string field form value",
 
130
        urlString: "http://localhost:8081/user",
 
131
        val: &struct {
 
132
                F1 *[]string `httprequest:"users,form"`
 
133
        }{
 
134
                F1: nil,
 
135
        },
 
136
        expectURLString: "http://localhost:8081/user",
 
137
}, {
 
138
        about:     "cannot marshal []string field to path",
 
139
        urlString: "http://localhost:8081/:users",
 
140
        val: &struct {
 
141
                F1 []string `httprequest:"users,path"`
 
142
        }{
 
143
                F1: []string{"user1", "user2"},
 
144
        },
 
145
        expectError: `bad type \*struct { F1 \[\]string "httprequest:\\"users,path\\"" }: invalid target type \[\]string for path parameter`,
 
146
}, {
 
147
        about:     "[]string field fails to marshal to path",
 
148
        urlString: "http://localhost:8081/user/:users",
 
149
        val: &struct {
 
150
                F1 []string `httprequest:"users,path"`
 
151
        }{
 
152
                F1: []string{"user1", "user2", "user3"},
 
153
        },
 
154
        expectError: "bad type .*: invalid target type.*",
 
155
}, {
 
156
        about:     "more than one field with body tag",
 
157
        urlString: "http://localhost:8081/user",
 
158
        method:    "POST",
 
159
        val: &struct {
 
160
                F1 string `httprequest:"user,body"`
 
161
                F2 int    `httprequest:"age,body"`
 
162
        }{
 
163
                F1: "test user",
 
164
                F2: 42,
 
165
        },
 
166
        expectError: "bad type .*: more than one body field specified",
 
167
}, {
 
168
        about:     "required path parameter, but not specified",
 
169
        urlString: "http://localhost:8081/u/:username",
 
170
        method:    "POST",
 
171
        val: &struct {
 
172
                F1 string `httprequest:"user,body"`
 
173
        }{
 
174
                F1: "test user",
 
175
        },
 
176
        expectError: `missing value for path parameter "username"`,
 
177
}, {
 
178
        about:     "marshal to body",
 
179
        urlString: "http://localhost:8081/u",
 
180
        method:    "POST",
 
181
        val: &struct {
 
182
                F1 embedded `httprequest:"info,body"`
 
183
        }{
 
184
                F1: embedded{
 
185
                        F1: "test user",
 
186
                        F2: 42,
 
187
                        F3: newString("test address"),
 
188
                },
 
189
        },
 
190
        expectBody: newString(`{"name":"test user","age":42,"address":"test address"}`),
 
191
}, {
 
192
        about:     "empty path wildcard",
 
193
        urlString: "http://localhost:8081/u/:",
 
194
        method:    "POST",
 
195
        val: &struct {
 
196
                F1 string `httprequest:"user,body"`
 
197
        }{
 
198
                F1: "test user",
 
199
        },
 
200
        expectError: "empty path parameter",
 
201
}, {
 
202
        about:     "nil field to form",
 
203
        urlString: "http://localhost:8081/u",
 
204
        val: &struct {
 
205
                F1 *string `httprequest:"user,form"`
 
206
        }{},
 
207
        expectURLString: "http://localhost:8081/u",
 
208
}, {
 
209
        about:     "nil field to path",
 
210
        urlString: "http://localhost:8081/u",
 
211
        val: &struct {
 
212
                F1 *string `httprequest:"user,path"`
 
213
        }{},
 
214
        expectURLString: "http://localhost:8081/u",
 
215
}, {
 
216
        about:     "marshal to body of a GET request",
 
217
        urlString: "http://localhost:8081/u",
 
218
        val: &struct {
 
219
                F1 string `httprequest:",body"`
 
220
        }{
 
221
                F1: "hello test",
 
222
        },
 
223
        // Providing a body to a GET request is unusual but
 
224
        // some people do it anyway.
 
225
 
 
226
        expectBody: newString(`"hello test"`),
 
227
}, {
 
228
        about:     "marshal to nil value to body",
 
229
        urlString: "http://localhost:8081/u",
 
230
        val: &struct {
 
231
                F1 *string `httprequest:",body"`
 
232
        }{
 
233
                F1: nil,
 
234
        },
 
235
        expectBody: newString(""),
 
236
}, {
 
237
        about:     "nil TextMarshaler",
 
238
        urlString: "http://localhost:8081/u",
 
239
        val: &struct {
 
240
                F1 *testMarshaler `httprequest:"surname,form"`
 
241
        }{
 
242
                F1: (*testMarshaler)(nil),
 
243
        },
 
244
        expectURLString: "http://localhost:8081/u",
 
245
}, {
 
246
        about:     "marshal nil with Sprint",
 
247
        urlString: "http://localhost:8081/u",
 
248
        val: &struct {
 
249
                F1 *int `httprequest:"surname,form"`
 
250
        }{
 
251
                F1: (*int)(nil),
 
252
        },
 
253
        expectURLString: "http://localhost:8081/u",
 
254
}, {
 
255
        about:     "marshal to path with * placeholder",
 
256
        urlString: "http://localhost:8081/u/*name",
 
257
        val: &struct {
 
258
                F1 string `httprequest:"name,path"`
 
259
        }{
 
260
                F1: "/test",
 
261
        },
 
262
        expectURLString: "http://localhost:8081/u/test",
 
263
}, {
 
264
        about:     "marshal to path with * placeholder, but the marshaled value does not start with /",
 
265
        urlString: "http://localhost:8081/u/*name",
 
266
        val: &struct {
 
267
                F1 string `httprequest:"name,path"`
 
268
        }{
 
269
                F1: "test",
 
270
        },
 
271
        expectError: `value \"test\" for path parameter \"\*name\" does not start with required /`,
 
272
}, {
 
273
        about:     "* placeholder allowed only at the end",
 
274
        urlString: "http://localhost:8081/u/*name/document",
 
275
        val: &struct {
 
276
                F1 string `httprequest:"name,path"`
 
277
        }{
 
278
                F1: "test",
 
279
        },
 
280
        expectError: "star path parameter is not at end of path",
 
281
}, {
 
282
        about:     "unparsable base url string",
 
283
        urlString: "%%",
 
284
        val: &struct {
 
285
                F1 string `httprequest:"name,form"`
 
286
        }{
 
287
                F1: "test",
 
288
        },
 
289
        expectError: `parse %%: invalid URL escape \"%%\"`,
 
290
}, {
 
291
        about:     "value cannot be marshaled to json",
 
292
        urlString: "http://localhost",
 
293
        method:    "POST",
 
294
        val: &struct {
 
295
                F1 failJSONMarshaler `httprequest:"field,body"`
 
296
        }{
 
297
                F1: "test",
 
298
        },
 
299
        expectError: `cannot marshal field: cannot marshal request body: json: error calling MarshalJSON for type \*httprequest_test.failJSONMarshaler: marshal error`,
 
300
}, {
 
301
        about:     "url with query parameters",
 
302
        urlString: "http://localhost?a=b",
 
303
        method:    "POST",
 
304
        val: &struct {
 
305
                F1 failJSONMarshaler `httprequest:"f1,form"`
 
306
        }{
 
307
                F1: "test",
 
308
        },
 
309
        expectURLString: "http://localhost?a=b&f1=test",
 
310
}, {
 
311
        about:           "url with query parameters no form",
 
312
        urlString:       "http://localhost?a=b",
 
313
        method:          "POST",
 
314
        val:             &struct{}{},
 
315
        expectURLString: "http://localhost?a=b",
 
316
}, {
 
317
        about:     "struct with headers",
 
318
        urlString: "http://localhost:8081/",
 
319
        val: &struct {
 
320
                F1 string `httprequest:",header"`
 
321
                F2 int    `httprequest:",header"`
 
322
                F3 bool   `httprequest:",header"`
 
323
        }{
 
324
                F1: "some text",
 
325
                F2: 99,
 
326
                F3: true,
 
327
        },
 
328
        expectURLString: "http://localhost:8081/",
 
329
        expectHeader: http.Header{
 
330
                "F1": []string{"some text"},
 
331
                "F2": []string{"99"},
 
332
                "F3": []string{"true"},
 
333
        },
 
334
}, {
 
335
        about:     "struct with header slice",
 
336
        urlString: "http://localhost:8081/:F1",
 
337
        val: &struct {
 
338
                F1 int      `httprequest:",path"`
 
339
                F2 string   `httprequest:",form"`
 
340
                F3 []string `httprequest:",header"`
 
341
        }{
 
342
                F1: 99,
 
343
                F2: "some text",
 
344
                F3: []string{"A", "B", "C"},
 
345
        },
 
346
        expectURLString: "http://localhost:8081/99?F2=some+text",
 
347
        expectHeader:    http.Header{"F3": []string{"A", "B", "C"}},
 
348
}, {
 
349
        about:     "SetHeader called after marshaling",
 
350
        urlString: "http://localhost:8081/",
 
351
        val: &httprequest.CustomHeader{
 
352
                Body: &struct {
 
353
                        F1 string `httprequest:",header"`
 
354
                        F2 int    `httprequest:",header"`
 
355
                        F3 bool   `httprequest:",header"`
 
356
                }{
 
357
                        F1: "some text",
 
358
                        F2: 99,
 
359
                        F3: false,
 
360
                },
 
361
                SetHeaderFunc: func(h http.Header) {
 
362
                        h.Set("F2", "some other text")
 
363
                },
 
364
        },
 
365
        expectURLString: "http://localhost:8081/",
 
366
        expectHeader: http.Header{
 
367
                "F1": []string{"some text"},
 
368
                "F2": []string{"some other text"},
 
369
                "F3": []string{"false"},
 
370
        },
 
371
}}
 
372
 
 
373
func getStruct() interface{} {
 
374
        return &struct {
 
375
                F1 string
 
376
        }{
 
377
                F1: "hello",
 
378
        }
 
379
}
 
380
 
 
381
func (*marshalSuite) TestMarshal(c *gc.C) {
 
382
        for i, test := range marshalTests {
 
383
                c.Logf("%d: %s", i, test.about)
 
384
                method := "GET"
 
385
                if test.method != "" {
 
386
                        method = test.method
 
387
                }
 
388
                req, err := httprequest.Marshal(test.urlString, method, test.val)
 
389
                if test.expectError != "" {
 
390
                        c.Assert(err, gc.ErrorMatches, test.expectError)
 
391
                        continue
 
392
                }
 
393
                c.Assert(err, gc.IsNil)
 
394
                if test.expectURLString != "" {
 
395
                        c.Assert(req.URL.String(), gc.DeepEquals, test.expectURLString)
 
396
                }
 
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")
 
402
                        }
 
403
                        c.Assert(string(data), gc.DeepEquals, *test.expectBody)
 
404
                }
 
405
                for k, v := range test.expectHeader {
 
406
                        c.Assert(req.Header[k], gc.DeepEquals, v)
 
407
                }
 
408
        }
 
409
}
 
410
 
 
411
type testMarshaler string
 
412
 
 
413
func (t *testMarshaler) MarshalText() ([]byte, error) {
 
414
        if len(*t) == 0 {
 
415
                return nil, errgo.New("empty string")
 
416
        }
 
417
        return []byte("test_" + *t), nil
 
418
}
 
419
 
 
420
type notTextMarshaler string
 
421
 
 
422
// MarshalText does *not* implement encoding.TextMarshaler
 
423
func (t *notTextMarshaler) MarshalText() {
 
424
        panic("unexpected call")
 
425
}
 
426
 
 
427
type failJSONMarshaler string
 
428
 
 
429
func (*failJSONMarshaler) MarshalJSON() ([]byte, error) {
 
430
        return nil, errgo.New("marshal error")
 
431
}