12 | 12 |
"strings"
|
13 | 13 |
|
14 | 14 |
"github.com/golang/protobuf/proto"
|
|
15 |
"github.com/onsi/gomega"
|
15 | 16 |
. "github.com/onsi/gomega"
|
16 | 17 |
"github.com/onsi/gomega/types"
|
17 | 18 |
)
|
18 | 19 |
|
|
20 |
type GHTTPWithGomega struct {
|
|
21 |
gomega Gomega
|
|
22 |
}
|
|
23 |
|
|
24 |
func NewGHTTPWithGomega(gomega Gomega) *GHTTPWithGomega {
|
|
25 |
return &GHTTPWithGomega{
|
|
26 |
gomega: gomega,
|
|
27 |
}
|
|
28 |
}
|
|
29 |
|
19 | 30 |
//CombineHandler takes variadic list of handlers and produces one handler
|
20 | 31 |
//that calls each handler in order.
|
21 | 32 |
func CombineHandlers(handlers ...http.HandlerFunc) http.HandlerFunc {
|
|
31 | 42 |
//
|
32 | 43 |
//For path, you may pass in a string, in which case strict equality will be applied
|
33 | 44 |
//Alternatively you can pass in a matcher (ContainSubstring("/foo") and MatchRegexp("/foo/[a-f0-9]+") for example)
|
34 | |
func VerifyRequest(method string, path interface{}, rawQuery ...string) http.HandlerFunc {
|
35 | |
return func(w http.ResponseWriter, req *http.Request) {
|
36 | |
Expect(req.Method).Should(Equal(method), "Method mismatch")
|
|
45 |
func (g GHTTPWithGomega) VerifyRequest(method string, path interface{}, rawQuery ...string) http.HandlerFunc {
|
|
46 |
return func(w http.ResponseWriter, req *http.Request) {
|
|
47 |
g.gomega.Expect(req.Method).Should(Equal(method), "Method mismatch")
|
37 | 48 |
switch p := path.(type) {
|
38 | 49 |
case types.GomegaMatcher:
|
39 | |
Expect(req.URL.Path).Should(p, "Path mismatch")
|
|
50 |
g.gomega.Expect(req.URL.Path).Should(p, "Path mismatch")
|
40 | 51 |
default:
|
41 | |
Expect(req.URL.Path).Should(Equal(path), "Path mismatch")
|
|
52 |
g.gomega.Expect(req.URL.Path).Should(Equal(path), "Path mismatch")
|
42 | 53 |
}
|
43 | 54 |
if len(rawQuery) > 0 {
|
44 | 55 |
values, err := url.ParseQuery(rawQuery[0])
|
45 | |
Expect(err).ShouldNot(HaveOccurred(), "Expected RawQuery is malformed")
|
46 | |
|
47 | |
Expect(req.URL.Query()).Should(Equal(values), "RawQuery mismatch")
|
|
56 |
g.gomega.Expect(err).ShouldNot(HaveOccurred(), "Expected RawQuery is malformed")
|
|
57 |
|
|
58 |
g.gomega.Expect(req.URL.Query()).Should(Equal(values), "RawQuery mismatch")
|
48 | 59 |
}
|
49 | 60 |
}
|
50 | 61 |
}
|
51 | 62 |
|
52 | 63 |
//VerifyContentType returns a handler that verifies that a request has a Content-Type header set to the
|
53 | 64 |
//specified value
|
54 | |
func VerifyContentType(contentType string) http.HandlerFunc {
|
55 | |
return func(w http.ResponseWriter, req *http.Request) {
|
56 | |
Expect(req.Header.Get("Content-Type")).Should(Equal(contentType))
|
|
65 |
func (g GHTTPWithGomega) VerifyContentType(contentType string) http.HandlerFunc {
|
|
66 |
return func(w http.ResponseWriter, req *http.Request) {
|
|
67 |
g.gomega.Expect(req.Header.Get("Content-Type")).Should(Equal(contentType))
|
57 | 68 |
}
|
58 | 69 |
}
|
59 | 70 |
|
60 | 71 |
//VerifyMimeType returns a handler that verifies that a request has a specified mime type set
|
61 | 72 |
//in Content-Type header
|
62 | |
func VerifyMimeType(mimeType string) http.HandlerFunc {
|
63 | |
return func(w http.ResponseWriter, req *http.Request) {
|
64 | |
Expect(strings.Split(req.Header.Get("Content-Type"), ";")[0]).Should(Equal(mimeType))
|
|
73 |
func (g GHTTPWithGomega) VerifyMimeType(mimeType string) http.HandlerFunc {
|
|
74 |
return func(w http.ResponseWriter, req *http.Request) {
|
|
75 |
g.gomega.Expect(strings.Split(req.Header.Get("Content-Type"), ";")[0]).Should(Equal(mimeType))
|
65 | 76 |
}
|
66 | 77 |
}
|
67 | 78 |
|
68 | 79 |
//VerifyBasicAuth returns a handler that verifies the request contains a BasicAuth Authorization header
|
69 | 80 |
//matching the passed in username and password
|
70 | |
func VerifyBasicAuth(username string, password string) http.HandlerFunc {
|
|
81 |
func (g GHTTPWithGomega) VerifyBasicAuth(username string, password string) http.HandlerFunc {
|
71 | 82 |
return func(w http.ResponseWriter, req *http.Request) {
|
72 | 83 |
auth := req.Header.Get("Authorization")
|
73 | |
Expect(auth).ShouldNot(Equal(""), "Authorization header must be specified")
|
|
84 |
g.gomega.Expect(auth).ShouldNot(Equal(""), "Authorization header must be specified")
|
74 | 85 |
|
75 | 86 |
decoded, err := base64.StdEncoding.DecodeString(auth[6:])
|
76 | |
Expect(err).ShouldNot(HaveOccurred())
|
77 | |
|
78 | |
Expect(string(decoded)).Should(Equal(fmt.Sprintf("%s:%s", username, password)), "Authorization mismatch")
|
|
87 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
|
88 |
|
|
89 |
g.gomega.Expect(string(decoded)).Should(Equal(fmt.Sprintf("%s:%s", username, password)), "Authorization mismatch")
|
79 | 90 |
}
|
80 | 91 |
}
|
81 | 92 |
|
|
84 | 95 |
//
|
85 | 96 |
//The request must contain *all* the passed in headers, but it is allowed to have additional headers
|
86 | 97 |
//beyond the passed in set.
|
87 | |
func VerifyHeader(header http.Header) http.HandlerFunc {
|
|
98 |
func (g GHTTPWithGomega) VerifyHeader(header http.Header) http.HandlerFunc {
|
88 | 99 |
return func(w http.ResponseWriter, req *http.Request) {
|
89 | 100 |
for key, values := range header {
|
90 | 101 |
key = http.CanonicalHeaderKey(key)
|
91 | |
Expect(req.Header[key]).Should(Equal(values), "Header mismatch for key: %s", key)
|
|
102 |
g.gomega.Expect(req.Header[key]).Should(Equal(values), "Header mismatch for key: %s", key)
|
92 | 103 |
}
|
93 | 104 |
}
|
94 | 105 |
}
|
|
96 | 107 |
//VerifyHeaderKV returns a handler that verifies the request contains a header matching the passed in key and values
|
97 | 108 |
//(recall that a `http.Header` is a mapping from string (key) to []string (values))
|
98 | 109 |
//It is a convenience wrapper around `VerifyHeader` that allows you to avoid having to create an `http.Header` object.
|
99 | |
func VerifyHeaderKV(key string, values ...string) http.HandlerFunc {
|
|
110 |
func (g GHTTPWithGomega) VerifyHeaderKV(key string, values ...string) http.HandlerFunc {
|
100 | 111 |
return VerifyHeader(http.Header{key: values})
|
101 | 112 |
}
|
102 | 113 |
|
103 | 114 |
//VerifyBody returns a handler that verifies that the body of the request matches the passed in byte array.
|
104 | 115 |
//It does this using Equal().
|
105 | |
func VerifyBody(expectedBody []byte) http.HandlerFunc {
|
|
116 |
func (g GHTTPWithGomega) VerifyBody(expectedBody []byte) http.HandlerFunc {
|
106 | 117 |
return CombineHandlers(
|
107 | 118 |
func(w http.ResponseWriter, req *http.Request) {
|
108 | 119 |
body, err := ioutil.ReadAll(req.Body)
|
109 | 120 |
req.Body.Close()
|
110 | |
Expect(err).ShouldNot(HaveOccurred())
|
111 | |
Expect(body).Should(Equal(expectedBody), "Body Mismatch")
|
|
121 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
|
122 |
g.gomega.Expect(body).Should(Equal(expectedBody), "Body Mismatch")
|
112 | 123 |
},
|
113 | 124 |
)
|
114 | 125 |
}
|
|
117 | 128 |
//matching the passed in JSON string. It does this using Gomega's MatchJSON method
|
118 | 129 |
//
|
119 | 130 |
//VerifyJSON also verifies that the request's content type is application/json
|
120 | |
func VerifyJSON(expectedJSON string) http.HandlerFunc {
|
|
131 |
func (g GHTTPWithGomega) VerifyJSON(expectedJSON string) http.HandlerFunc {
|
121 | 132 |
return CombineHandlers(
|
122 | 133 |
VerifyMimeType("application/json"),
|
123 | 134 |
func(w http.ResponseWriter, req *http.Request) {
|
124 | 135 |
body, err := ioutil.ReadAll(req.Body)
|
125 | 136 |
req.Body.Close()
|
126 | |
Expect(err).ShouldNot(HaveOccurred())
|
127 | |
Expect(body).Should(MatchJSON(expectedJSON), "JSON Mismatch")
|
|
137 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
|
138 |
g.gomega.Expect(body).Should(MatchJSON(expectedJSON), "JSON Mismatch")
|
128 | 139 |
},
|
129 | 140 |
)
|
130 | 141 |
}
|
|
132 | 143 |
//VerifyJSONRepresenting is similar to VerifyJSON. Instead of taking a JSON string, however, it
|
133 | 144 |
//takes an arbitrary JSON-encodable object and verifies that the requests's body is a JSON representation
|
134 | 145 |
//that matches the object
|
135 | |
func VerifyJSONRepresenting(object interface{}) http.HandlerFunc {
|
|
146 |
func (g GHTTPWithGomega) VerifyJSONRepresenting(object interface{}) http.HandlerFunc {
|
136 | 147 |
data, err := json.Marshal(object)
|
137 | |
Expect(err).ShouldNot(HaveOccurred())
|
|
148 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
138 | 149 |
return CombineHandlers(
|
139 | 150 |
VerifyMimeType("application/json"),
|
140 | 151 |
VerifyJSON(string(data)),
|
|
145 | 156 |
//
|
146 | 157 |
//The request must contain *all* of the specified values, but it is allowed to have additional
|
147 | 158 |
//form values beyond the passed in set.
|
148 | |
func VerifyForm(values url.Values) http.HandlerFunc {
|
|
159 |
func (g GHTTPWithGomega) VerifyForm(values url.Values) http.HandlerFunc {
|
149 | 160 |
return func(w http.ResponseWriter, r *http.Request) {
|
150 | 161 |
err := r.ParseForm()
|
151 | |
Expect(err).ShouldNot(HaveOccurred())
|
|
162 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
152 | 163 |
for key, vals := range values {
|
153 | |
Expect(r.Form[key]).Should(Equal(vals), "Form mismatch for key: %s", key)
|
|
164 |
g.gomega.Expect(r.Form[key]).Should(Equal(vals), "Form mismatch for key: %s", key)
|
154 | 165 |
}
|
155 | 166 |
}
|
156 | 167 |
}
|
|
158 | 169 |
//VerifyFormKV returns a handler that verifies a request contains a form key with the specified values.
|
159 | 170 |
//
|
160 | 171 |
//It is a convenience wrapper around `VerifyForm` that lets you avoid having to create a `url.Values` object.
|
161 | |
func VerifyFormKV(key string, values ...string) http.HandlerFunc {
|
|
172 |
func (g GHTTPWithGomega) VerifyFormKV(key string, values ...string) http.HandlerFunc {
|
162 | 173 |
return VerifyForm(url.Values{key: values})
|
163 | 174 |
}
|
164 | 175 |
|
|
166 | 177 |
//representation of the passed message.
|
167 | 178 |
//
|
168 | 179 |
//VerifyProtoRepresenting also verifies that the request's content type is application/x-protobuf
|
169 | |
func VerifyProtoRepresenting(expected proto.Message) http.HandlerFunc {
|
|
180 |
func (g GHTTPWithGomega) VerifyProtoRepresenting(expected proto.Message) http.HandlerFunc {
|
170 | 181 |
return CombineHandlers(
|
171 | 182 |
VerifyContentType("application/x-protobuf"),
|
172 | 183 |
func(w http.ResponseWriter, req *http.Request) {
|
173 | 184 |
body, err := ioutil.ReadAll(req.Body)
|
174 | |
Expect(err).ShouldNot(HaveOccurred())
|
|
185 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
175 | 186 |
req.Body.Close()
|
176 | 187 |
|
177 | 188 |
expectedType := reflect.TypeOf(expected)
|
178 | 189 |
actualValuePtr := reflect.New(expectedType.Elem())
|
179 | 190 |
|
180 | 191 |
actual, ok := actualValuePtr.Interface().(proto.Message)
|
181 | |
Expect(ok).Should(BeTrue(), "Message value is not a proto.Message")
|
|
192 |
g.gomega.Expect(ok).Should(BeTrue(), "Message value is not a proto.Message")
|
182 | 193 |
|
183 | 194 |
err = proto.Unmarshal(body, actual)
|
184 | |
Expect(err).ShouldNot(HaveOccurred(), "Failed to unmarshal protobuf")
|
185 | |
|
186 | |
Expect(actual).Should(Equal(expected), "ProtoBuf Mismatch")
|
|
195 |
g.gomega.Expect(err).ShouldNot(HaveOccurred(), "Failed to unmarshal protobuf")
|
|
196 |
|
|
197 |
g.gomega.Expect(actual).Should(Equal(expected), "ProtoBuf Mismatch")
|
187 | 198 |
},
|
188 | 199 |
)
|
189 | 200 |
}
|
|
201 | 212 |
|
202 | 213 |
Also, RespondWith can be given an optional http.Header. The headers defined therein will be added to the response headers.
|
203 | 214 |
*/
|
204 | |
func RespondWith(statusCode int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
215 |
func (g GHTTPWithGomega) RespondWith(statusCode int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
205 | 216 |
return func(w http.ResponseWriter, req *http.Request) {
|
206 | 217 |
if len(optionalHeader) == 1 {
|
207 | 218 |
copyHeader(optionalHeader[0], w.Header())
|
|
213 | 224 |
case []byte:
|
214 | 225 |
w.Write(x)
|
215 | 226 |
default:
|
216 | |
Expect(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.")
|
|
227 |
g.gomega.Expect(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.")
|
217 | 228 |
}
|
218 | 229 |
}
|
219 | 230 |
}
|
|
227 | 238 |
Also, RespondWithPtr can be given an optional http.Header. The headers defined therein will be added to the response headers.
|
228 | 239 |
Since the http.Header can be mutated after the fact you don't need to pass in a pointer.
|
229 | 240 |
*/
|
230 | |
func RespondWithPtr(statusCode *int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
241 |
func (g GHTTPWithGomega) RespondWithPtr(statusCode *int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
231 | 242 |
return func(w http.ResponseWriter, req *http.Request) {
|
232 | 243 |
if len(optionalHeader) == 1 {
|
233 | 244 |
copyHeader(optionalHeader[0], w.Header())
|
|
240 | 251 |
case *[]byte:
|
241 | 252 |
w.Write(*x)
|
242 | 253 |
default:
|
243 | |
Expect(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.")
|
|
254 |
g.gomega.Expect(body).Should(BeNil(), "Invalid type for body. Should be string or []byte.")
|
244 | 255 |
}
|
245 | 256 |
}
|
246 | 257 |
}
|
|
252 | 263 |
|
253 | 264 |
Also, RespondWithJSONEncoded can be given an optional http.Header. The headers defined therein will be added to the response headers.
|
254 | 265 |
*/
|
255 | |
func RespondWithJSONEncoded(statusCode int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
266 |
func (g GHTTPWithGomega) RespondWithJSONEncoded(statusCode int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
256 | 267 |
data, err := json.Marshal(object)
|
257 | |
Expect(err).ShouldNot(HaveOccurred())
|
|
268 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
258 | 269 |
|
259 | 270 |
var headers http.Header
|
260 | 271 |
if len(optionalHeader) == 1 {
|
|
278 | 289 |
Also, RespondWithJSONEncodedPtr can be given an optional http.Header. The headers defined therein will be added to the response headers.
|
279 | 290 |
Since the http.Header can be mutated after the fact you don't need to pass in a pointer.
|
280 | 291 |
*/
|
281 | |
func RespondWithJSONEncodedPtr(statusCode *int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
292 |
func (g GHTTPWithGomega) RespondWithJSONEncodedPtr(statusCode *int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
282 | 293 |
return func(w http.ResponseWriter, req *http.Request) {
|
283 | 294 |
data, err := json.Marshal(object)
|
284 | |
Expect(err).ShouldNot(HaveOccurred())
|
|
295 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
285 | 296 |
var headers http.Header
|
286 | 297 |
if len(optionalHeader) == 1 {
|
287 | 298 |
headers = optionalHeader[0]
|
|
301 | 312 |
//containing the protobuf serialization of the provided message.
|
302 | 313 |
//
|
303 | 314 |
//Also, RespondWithProto can be given an optional http.Header. The headers defined therein will be added to the response headers.
|
304 | |
func RespondWithProto(statusCode int, message proto.Message, optionalHeader ...http.Header) http.HandlerFunc {
|
|
315 |
func (g GHTTPWithGomega) RespondWithProto(statusCode int, message proto.Message, optionalHeader ...http.Header) http.HandlerFunc {
|
305 | 316 |
return func(w http.ResponseWriter, req *http.Request) {
|
306 | 317 |
data, err := proto.Marshal(message)
|
307 | |
Expect(err).ShouldNot(HaveOccurred())
|
|
318 |
g.gomega.Expect(err).ShouldNot(HaveOccurred())
|
308 | 319 |
|
309 | 320 |
var headers http.Header
|
310 | 321 |
if len(optionalHeader) == 1 {
|
|
321 | 332 |
w.Write(data)
|
322 | 333 |
}
|
323 | 334 |
}
|
|
335 |
|
|
336 |
func VerifyRequest(method string, path interface{}, rawQuery ...string) http.HandlerFunc {
|
|
337 |
return NewGHTTPWithGomega(gomega.Default).VerifyRequest(method, path, rawQuery...)
|
|
338 |
}
|
|
339 |
|
|
340 |
func VerifyContentType(contentType string) http.HandlerFunc {
|
|
341 |
return NewGHTTPWithGomega(gomega.Default).VerifyContentType(contentType)
|
|
342 |
}
|
|
343 |
|
|
344 |
func VerifyMimeType(mimeType string) http.HandlerFunc {
|
|
345 |
return NewGHTTPWithGomega(gomega.Default).VerifyMimeType(mimeType)
|
|
346 |
}
|
|
347 |
|
|
348 |
func VerifyBasicAuth(username string, password string) http.HandlerFunc {
|
|
349 |
return NewGHTTPWithGomega(gomega.Default).VerifyBasicAuth(username, password)
|
|
350 |
}
|
|
351 |
|
|
352 |
func VerifyHeader(header http.Header) http.HandlerFunc {
|
|
353 |
return NewGHTTPWithGomega(gomega.Default).VerifyHeader(header)
|
|
354 |
}
|
|
355 |
|
|
356 |
func VerifyHeaderKV(key string, values ...string) http.HandlerFunc {
|
|
357 |
return NewGHTTPWithGomega(gomega.Default).VerifyHeaderKV(key, values...)
|
|
358 |
}
|
|
359 |
|
|
360 |
func VerifyBody(expectedBody []byte) http.HandlerFunc {
|
|
361 |
return NewGHTTPWithGomega(gomega.Default).VerifyBody(expectedBody)
|
|
362 |
}
|
|
363 |
|
|
364 |
func VerifyJSON(expectedJSON string) http.HandlerFunc {
|
|
365 |
return NewGHTTPWithGomega(gomega.Default).VerifyJSON(expectedJSON)
|
|
366 |
}
|
|
367 |
|
|
368 |
func VerifyJSONRepresenting(object interface{}) http.HandlerFunc {
|
|
369 |
return NewGHTTPWithGomega(gomega.Default).VerifyJSONRepresenting(object)
|
|
370 |
}
|
|
371 |
|
|
372 |
func VerifyForm(values url.Values) http.HandlerFunc {
|
|
373 |
return NewGHTTPWithGomega(gomega.Default).VerifyForm(values)
|
|
374 |
}
|
|
375 |
|
|
376 |
func VerifyFormKV(key string, values ...string) http.HandlerFunc {
|
|
377 |
return NewGHTTPWithGomega(gomega.Default).VerifyFormKV(key, values...)
|
|
378 |
}
|
|
379 |
|
|
380 |
func VerifyProtoRepresenting(expected proto.Message) http.HandlerFunc {
|
|
381 |
return NewGHTTPWithGomega(gomega.Default).VerifyProtoRepresenting(expected)
|
|
382 |
}
|
|
383 |
|
|
384 |
func RespondWith(statusCode int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
385 |
return NewGHTTPWithGomega(gomega.Default).RespondWith(statusCode, body, optionalHeader...)
|
|
386 |
}
|
|
387 |
|
|
388 |
func RespondWithPtr(statusCode *int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
389 |
return NewGHTTPWithGomega(gomega.Default).RespondWithPtr(statusCode, body, optionalHeader...)
|
|
390 |
}
|
|
391 |
|
|
392 |
func RespondWithJSONEncoded(statusCode int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
393 |
return NewGHTTPWithGomega(gomega.Default).RespondWithJSONEncoded(statusCode, object, optionalHeader...)
|
|
394 |
}
|
|
395 |
|
|
396 |
func RespondWithJSONEncodedPtr(statusCode *int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc {
|
|
397 |
return NewGHTTPWithGomega(gomega.Default).RespondWithJSONEncodedPtr(statusCode, object, optionalHeader...)
|
|
398 |
}
|
|
399 |
|
|
400 |
func RespondWithProto(statusCode int, message proto.Message, optionalHeader ...http.Header) http.HandlerFunc {
|
|
401 |
return NewGHTTPWithGomega(gomega.Default).RespondWithProto(statusCode, message, optionalHeader...)
|
|
402 |
}
|