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
|
package gock
import (
"net/http"
"sync"
)
// Mock represents the required interface that must
// be implemented by HTTP mock instances.
type Mock interface {
// Disable disables the current mock manually.
Disable()
// Done returns true if the current mock is disabled.
Done() bool
// Request returns the mock Request instance.
Request() *Request
// Response returns the mock Response instance.
Response() *Response
// Match matches the given http.Request with the current mock.
Match(*http.Request) (bool, error)
// AddMatcher adds a new matcher function.
AddMatcher(MatchFunc)
// SetMatcher uses a new matcher implementation.
SetMatcher(Matcher)
}
// Mocker implements a Mock capable interface providing
// a default mock configuration used internally to store mocks.
type Mocker struct {
// disabler stores a disabler for thread safety checking current mock is disabled
disabler *disabler
// mutex stores the mock mutex for thread safety.
mutex sync.Mutex
// matcher stores a Matcher capable instance to match the given http.Request.
matcher Matcher
// request stores the mock Request to match.
request *Request
// response stores the mock Response to use in case of match.
response *Response
}
type disabler struct {
// disabled stores if the current mock is disabled.
disabled bool
// mutex stores the disabler mutex for thread safety.
mutex sync.RWMutex
}
func (d *disabler) isDisabled() bool {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.disabled
}
func (d *disabler) Disable() {
d.mutex.Lock()
defer d.mutex.Unlock()
d.disabled = true
}
// NewMock creates a new HTTP mock based on the given request and response instances.
// It's mostly used internally.
func NewMock(req *Request, res *Response) *Mocker {
mock := &Mocker{
disabler: new(disabler),
request: req,
response: res,
matcher: DefaultMatcher.Clone(),
}
res.Mock = mock
req.Mock = mock
req.Response = res
return mock
}
// Disable disables the current mock manually.
func (m *Mocker) Disable() {
m.disabler.Disable()
}
// Done returns true in case that the current mock
// instance is disabled and therefore must be removed.
func (m *Mocker) Done() bool {
// prevent deadlock with m.mutex
if m.disabler.isDisabled() {
return true
}
m.mutex.Lock()
defer m.mutex.Unlock()
return !m.request.Persisted && m.request.Counter == 0
}
// Request returns the Request instance
// configured for the current HTTP mock.
func (m *Mocker) Request() *Request {
return m.request
}
// Response returns the Response instance
// configured for the current HTTP mock.
func (m *Mocker) Response() *Response {
return m.response
}
// Match matches the given http.Request with the current Request
// mock expectation, returning true if matches.
func (m *Mocker) Match(req *http.Request) (bool, error) {
if m.disabler.isDisabled() {
return false, nil
}
// Filter
for _, filter := range m.request.Filters {
if !filter(req) {
return false, nil
}
}
// Map
for _, mapper := range m.request.Mappers {
if treq := mapper(req); treq != nil {
req = treq
}
}
// Match
matches, err := m.matcher.Match(req, m.request)
if matches {
m.decrement()
}
return matches, err
}
// SetMatcher sets a new matcher implementation
// for the current mock expectation.
func (m *Mocker) SetMatcher(matcher Matcher) {
m.matcher = matcher
}
// AddMatcher adds a new matcher function
// for the current mock expectation.
func (m *Mocker) AddMatcher(fn MatchFunc) {
m.matcher.Add(fn)
}
// decrement decrements the current mock Request counter.
func (m *Mocker) decrement() {
if m.request.Persisted {
return
}
m.mutex.Lock()
defer m.mutex.Unlock()
m.request.Counter--
if m.request.Counter == 0 {
m.disabler.Disable()
}
}
|