-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmock.go
223 lines (175 loc) · 5.46 KB
/
mock.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package mocha
import (
"fmt"
"net/http"
"sync"
"github.com/vitorsalgado/mocha/v3/expect"
"github.com/vitorsalgado/mocha/v3/internal/autoid"
"github.com/vitorsalgado/mocha/v3/params"
"github.com/vitorsalgado/mocha/v3/reply"
)
type (
// Mock holds metadata and expectations to be matched against HTTP requests in order to serve mocked responses.
// This is core entity of this project, mostly features works based on it.
Mock struct {
// ID is unique identifier for a Mock
ID int
// Name is an optional metadata. It helps to find and debug mocks.
Name string
// Priority sets the priority for a Mock.
Priority int
// Expectations are a list of Expectation. These will run on every request to find the correct Mock.
Expectations []Expectation
// Reply is the responder that will be used to serve the HTTP response stub, once matched against an
// HTTP request.
Reply reply.Reply
// Enabled indicates if the Mock is enabled or disabled. Only enabled mocks are matched.
Enabled bool
// PostActions holds PostAction list to be executed after the Mock was matched and served.
PostActions []PostAction
// Repeat indicates how many times a mocked response should be served.
// If value is equal or lower than zero, it will not be considered.
Repeat int
ScenarioName string
ScenarioState string
ScenarioRequiredState string
ScenarioNewState string
mu *sync.Mutex
hits int
}
// PostActionArgs represents the arguments that will be passed to every PostAction implementation
PostActionArgs struct {
Request *http.Request
Response *reply.Response
Mock *Mock
Params params.P
}
// PostAction defines the contract for an action that will be executed after serving a mocked HTTP response.
PostAction interface {
// Run runs the PostAction implementation.
Run(args PostActionArgs) error
}
// ValueSelector defines a function that will be used to extract RequestInfo value and provide it to Matcher instances.
ValueSelector func(r *expect.RequestInfo) any
// Expectation holds metadata related to one http.Request Matcher.
Expectation struct {
// Target is an optional metadata that describes the target of the matcher.
// Example: the target could have the "header", meaning that the matcher will be applied to one request header.
Target string
// Matcher associated with this Expectation.
Matcher expect.Matcher
// ValueSelector will extract the http.Request or a portion of it and feed it to the associated Matcher.
ValueSelector ValueSelector
// Weight of this Expectation.
Weight weight
}
)
type (
// weight helps to detect the closest mock match.
weight int
// matchResult holds information related to a matching operation.
matchResult struct {
// MismatchDetails is the list of non matches messages.
MismatchDetails []mismatchDetail
// Weight for the Matcher. It helps determine the closest match.
Weight int
// IsMatch indicates whether it matched or not.
IsMatch bool
}
// mismatchDetail gives more context about why a matcher did not match.
mismatchDetail struct {
Name string
Target string
Description string
}
)
// Enums of weight.
const (
_weightNone weight = iota
_weightVeryLow
_weightLow
_weightRegular
_weightHigh
)
// newMock returns a new Mock with default values set.
func newMock() *Mock {
return &Mock{
ID: autoid.Next(),
Enabled: true,
Expectations: make([]Expectation, 0),
PostActions: make([]PostAction, 0),
mu: &sync.Mutex{},
}
}
// Hit notify that the Mock was called.
func (m *Mock) Hit() {
m.mu.Lock()
defer m.mu.Unlock()
m.hits++
}
// Hits returns the amount of time this Mock was matched to a request and served.
func (m *Mock) Hits() int {
return m.hits
}
// Dec reduce one Mock call.
func (m *Mock) Dec() {
m.mu.Lock()
defer m.mu.Unlock()
m.hits--
}
// Called checks if the Mock was called at least once.
func (m *Mock) Called() bool {
return m.hits > 0
}
// Enable enables the Mock.
// The Mock will be eligible to be matched.
func (m *Mock) Enable() {
m.mu.Lock()
defer m.mu.Unlock()
m.Enabled = true
}
// Disable disables the Mock.
// The Mock will not be eligible to be matched.
func (m *Mock) Disable() {
m.mu.Lock()
defer m.mu.Unlock()
m.Enabled = false
}
// matches checks if current Mock matches against a list of expectations.
// Will iterate through all expectations even if it doesn't match early.
func (m *Mock) matches(params expect.Args, expectations []Expectation) (matchResult, error) {
w := 0
hasMatched := true
details := make([]mismatchDetail, 0)
for _, exp := range expectations {
matched, detail, err := matches(exp, params)
// fail fast if an error occurs
if err != nil {
return matchResult{IsMatch: false, Weight: w},
fmt.Errorf("matcher %s returned an error=%v", exp.Target, err)
}
if !matched {
details = append(details, detail)
hasMatched = matched
}
w += int(exp.Weight)
}
return matchResult{IsMatch: hasMatched, Weight: w, MismatchDetails: details}, nil
}
func matches(e Expectation, params expect.Args) (bool, mismatchDetail, error) {
val := e.ValueSelector(params.RequestInfo)
res, err := e.Matcher.Matches(val, params)
if err != nil {
return false,
mismatchDetail{Name: e.Matcher.Name, Target: e.Target},
err
}
if !res {
desc := ""
if e.Matcher.DescribeMismatch != nil {
desc = e.Matcher.DescribeMismatch(e.Target, val)
}
return res, mismatchDetail{Name: e.Matcher.Name, Target: e.Target, Description: desc}, err
}
return res, mismatchDetail{}, err
}