-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstubbed_request.go
132 lines (112 loc) · 3.58 KB
/
stubbed_request.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package simular
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
)
// Option is a functional configurator allowing non-standard configuration to be
// added to the StubRequest without the previous chained function call approach.
type Option func(*StubRequest)
// NewStubRequest is a constructor function that returns a StubRequest for the
// given method and url. We also supply a responder which actually generates
// the response should the stubbed request match the request.
func NewStubRequest(method, url string, responder Responder, options ...Option) *StubRequest {
r := &StubRequest{
Method: method,
URL: url,
Responder: responder,
}
for _, option := range options {
option(r)
}
return r
}
// StubRequest is used to capture data about a new stubbed request. It wraps up
// the Method and URL along with optional http.Header struct, holds the
// Responder used to generate a response, and also has a flag indicating
// whether or not this stubbed request has actually been called.
type StubRequest struct {
Method string
URL string
Header *http.Header
Body io.Reader
Responder Responder
Called bool
}
// WithHeader is a functional configuration option used to add http headers onto
// a stubbed request
func WithHeader(header *http.Header) Option {
return func(r *StubRequest) {
r.Header = header
}
}
// WithBody is a functional configuration option used to add a body to a stubbed
// request
func WithBody(body io.Reader) Option {
return func(r *StubRequest) {
r.Body = body
}
}
// Matches is a function that returns true if an incoming request is matched by
// this fetcher. Should an incoming request URL cause an error when normalized,
// we return false.
func (r *StubRequest) Matches(req *http.Request) error {
if !strings.EqualFold(req.Method, r.Method) {
return fmt.Errorf("Unexpected method, expected %s, got %s", r.Method, req.Method)
}
normalizedURL, err := normalizeURL(r.URL)
if err != nil {
return err
}
normalizedReqURL, err := normalizeURL(req.URL.String())
if err != nil {
return err
}
if normalizedURL != normalizedReqURL {
return fmt.Errorf("Unexpected URL, expected %s, got %s", normalizedURL, normalizedReqURL)
}
// only check headers if the stubbed request has set headers to some not nil
// value
if r.Header != nil {
// for each header defined on the stub, iterate through all the values and
// make sure they are present in the corresponding header on the request
for header, stubValues := range map[string][]string(*r.Header) {
// get the values for this header on the request
reqValues := req.Header[http.CanonicalHeaderKey(header)]
for _, v := range stubValues {
if !contains(reqValues, v) {
return fmt.Errorf("Unexpected request headers, expected: %v, got %v", r.Header, req.Header)
}
}
}
}
// if our stub includes a body, then it should equal the actual request body
// to match
if r.Body != nil {
stubBody, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
requestBody, err := ioutil.ReadAll(req.Body)
if err != nil {
return err
}
if bytes.Compare(stubBody, requestBody) != 0 {
return fmt.Errorf("Unexpected request body, expected %s, got %s", stubBody, requestBody)
}
}
return nil
}
// String is our implementation of Stringer for our StubRequest types. Returns a
// simple string representation of the stubbed response, included method, URL
// and any headers.
func (r *StubRequest) String() string {
str := fmt.Sprintf("%s %s", r.Method, r.URL)
if r.Header != nil {
str = str + fmt.Sprintf(" with headers %v", r.Header)
}
return str
}