Skip to content

Commit

Permalink
feat: introduce new matchers feature
Browse files Browse the repository at this point in the history
It is now possible to create a Responder based on Request
content (header or body for example), so not only on method and URL as
before.

Signed-off-by: Maxime Soulé <btik-git@scoubidou.com>
  • Loading branch information
maxatome committed Nov 8, 2022
1 parent a55e961 commit f69cd5e
Show file tree
Hide file tree
Showing 9 changed files with 2,000 additions and 210 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ func TestFetchArticles(t *testing.T) {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
})

// return an article related to the request with the help of regexp submatch (\d+)
httpmock.RegisterResponder("GET", `=~^https://api\.mybiz\.com/articles/id/(\d+)\z`,
Expand All @@ -77,8 +76,7 @@ func TestFetchArticles(t *testing.T) {
"id": id,
"name": "My Great Article",
})
},
)
})

// mock to add a new article
httpmock.RegisterResponder("POST", "https://api.mybiz.com/articles",
Expand All @@ -95,8 +93,13 @@ func TestFetchArticles(t *testing.T) {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
})

// mock to add a specific article, send a Bad Request response
// when the request body contains `"type":"toy"`
httpmock.RegisterMatcherResponder("POST", "https://api.mybiz.com/articles",
httpmock.BodyContainsString(`"type":"toy"`),
httpmock.NewStringResponder(400, `{"reason":"Invalid article type"}`))

// do stuff that adds and checks articles
}
Expand Down
87 changes: 87 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package httpmock

import (
"io"
"net/http"
"reflect"
"sync/atomic"

"github.com/jarcoal/httpmock/internal"
)

var (
GetPackage = getPackage
ExtractPackage = extractPackage
CalledFrom = calledFrom
)

type (
MatchResponder = matchResponder
MatchResponders = matchResponders
)

func init() {
atomic.AddInt64(&matcherID, 0xabcdef)
}

func GetIgnorePackages() map[string]bool {
return ignorePackages
}

// bodyCopyOnRead

func NewBodyCopyOnRead(body io.ReadCloser) *bodyCopyOnRead { //nolint: revive
return &bodyCopyOnRead{body: body}
}

func (b *bodyCopyOnRead) Body() io.ReadCloser {
return b.body
}

func (b *bodyCopyOnRead) Buf() []byte {
return b.buf
}

func (b *bodyCopyOnRead) Rearm() {
b.rearm()
}

// matchRouteKey

func NewMatchRouteKey(rk internal.RouteKey, name string) matchRouteKey { //nolint: revive
return matchRouteKey{RouteKey: rk, name: name}
}

// matchResponder

func NewMatchResponder(matcher Matcher, resp Responder) matchResponder { //nolint: revive
return matchResponder{matcher: matcher, responder: resp}
}

func (mr matchResponder) ResponderPointer() uintptr {
return reflect.ValueOf(mr.responder).Pointer()
}

func (mr matchResponder) Matcher() Matcher {
return mr.matcher
}

// matchResponders

func (mrs matchResponders) Add(mr matchResponder) matchResponders {
return mrs.add(mr)
}

func (mrs matchResponders) Remove(name string) matchResponders {
return mrs.remove(name)
}

func (mrs matchResponders) FindMatchResponder(req *http.Request) *matchResponder {
return mrs.findMatchResponder(req)
}

// Matcher

func (m Matcher) FnPointer() uintptr {
return reflect.ValueOf(m.fn).Pointer()
}
8 changes: 7 additions & 1 deletion internal/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var NoResponderFound = errors.New("no responder found") // nolint: revive
// ErrorNoResponderFoundMistake encapsulates a NoResponderFound
// error probably due to a user error on the method or URL path.
type ErrorNoResponderFoundMistake struct {
Kind string // "method" or "URL"
Kind string // "method", "URL" or "matcher"
Orig string // original wrong method/URL, without any matching responder
Suggested string // suggested method/URL with a matching responder
}
Expand All @@ -26,6 +26,12 @@ func (e *ErrorNoResponderFoundMistake) Unwrap() error {

// Error implements error interface.
func (e *ErrorNoResponderFoundMistake) Error() string {
if e.Kind == "matcher" {
return fmt.Sprintf("%s despite %s",
NoResponderFound,
e.Suggested,
)
}
return fmt.Sprintf("%[1]s for %[2]s %[3]q, but one matches %[2]s %[4]q",
NoResponderFound,
e.Kind,
Expand Down
8 changes: 7 additions & 1 deletion internal/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ func TestErrorNoResponderFoundMistake(t *testing.T) {
Orig: "pipo",
Suggested: "BINGO",
}

td.Cmp(t, e.Error(), `no responder found for method "pipo", but one matches method "BINGO"`)
td.Cmp(t, e.Unwrap(), internal.NoResponderFound)

e = &internal.ErrorNoResponderFoundMistake{
Kind: "matcher",
Orig: "--not--used--",
Suggested: "BINGO",
}
td.Cmp(t, e.Error(), `no responder found despite BINGO`)
td.Cmp(t, e.Unwrap(), internal.NoResponderFound)
}
Loading

0 comments on commit f69cd5e

Please sign in to comment.