diff --git a/pkg/match/request.go b/pkg/match/request.go index cb9529c..17c761c 100644 --- a/pkg/match/request.go +++ b/pkg/match/request.go @@ -20,6 +20,7 @@ var ( ErrCookiesNotMatch = errors.New("Cookies not match") ErrScenarioNotMatch = errors.New("Scenario state not match") ErrPathNotMatch = errors.New("Path not match") + ErrPathVariablesNotMatch = errors.New("Path variables not match") ) func NewTester(comparator *payload.Comparator, scenario ScenearioStorer) *Request { @@ -190,9 +191,15 @@ func (mm Request) Match(req *mock.Request, mock *mock.Definition, scenarioAware return false, fmt.Errorf("Fragment not match. Actual: %s, Expected: %s", req.Fragment, mock.Request.Fragment) } - if !glob.Glob(mock.Request.Path, req.Path) && route.Match(req.Path) == nil { + var pathMatch = route.Match(req.Path) + + if !glob.Glob(mock.Request.Path, req.Path) && pathMatch == nil { return false, fmt.Errorf("%w Actual: %s, Expected: %s", ErrPathNotMatch, req.Path, mock.Request.Path) } + + if pathMatch != nil && mock.Request.PathVariables != nil && !route.MatchPathVariables(pathMatch, mock.Request.PathVariables) { + return false, fmt.Errorf("%w Actual: %s Expected: %v", ErrPathVariablesNotMatch, mock.Request.PathVariables, pathMatch.Params) + } if !mm.mockIncludesMethod(req.Method, &mock.Request) { return false, fmt.Errorf("Method not match. Actual: %s, Expected: %s", req.Method, mock.Request.Method) diff --git a/pkg/match/request_test.go b/pkg/match/request_test.go index 59a2620..ac533c4 100644 --- a/pkg/match/request_test.go +++ b/pkg/match/request_test.go @@ -94,6 +94,39 @@ func TestPathVars(t *testing.T) { } } +func TestPathVariables(t *testing.T) { + req := mock.Request{} + req.Path = "/a/b/c" + + m := mock.Definition{} + m.Request.Path = "/a/:b/:c" + m.Request.PathVariables = map[string]string{ + "b": "[a-z]", + "c": "[a-z]", + } + + mm := Request{} + + // match + if b, err := mm.Match(&req, &m, true); !b { + t.Error(err) + } + + // not match + req.Path = "a/b/3" + + if b, err := mm.Match(&req, &m, true); b { + t.Error(err) + } + + // not present + req.Path = "a/b" + + if b, err := mm.Match(&req, &m, true); b { + t.Error(err) + } +} + func TestPathGlob(t *testing.T) { req := mock.Request{} req.Path = "/a/b/c" diff --git a/pkg/mock/definition.go b/pkg/mock/definition.go index c5c909c..8c3d281 100644 --- a/pkg/mock/definition.go +++ b/pkg/mock/definition.go @@ -11,6 +11,8 @@ type Values map[string][]string type Cookies map[string]string +type PathValues map[string]string + type HttpHeaders struct { Headers Values `json:"headers"` Cookies Cookies `json:"cookies"` @@ -22,6 +24,7 @@ type Request struct { Port string `json:"port"` Method string `json:"method"` Path string `json:"path"` + PathVariables PathValues `json:"pathVariables"` QueryStringParameters Values `json:"queryStringParameters"` Fragment string `json:"fragment"` HttpHeaders diff --git a/pkg/route/route.go b/pkg/route/route.go index dffab94..61bfb26 100644 --- a/pkg/route/route.go +++ b/pkg/route/route.go @@ -4,8 +4,11 @@ import ( "fmt" "regexp" "strings" + "github.com/jmartin82/mmock/v3/internal/config/logger" ) +var log = logger.Log + type Route struct { Keys []string Regex *regexp.Regexp @@ -40,6 +43,28 @@ func (route *Route) Match(url string) *Match { return &Match{route.Params(url), route.Pattern} } +func (route *Route) MatchPathVariables(match *Match, patterns map[string]string) bool{ + for key, value := range match.Params { + + pattern, exists := patterns[key] + + if !exists { + log.Debugf("pathVariable %v isn't in request", key) + return false + } + + log.Debugf("comparing pathVariable %v that has a value of %v against pattern %v", key, value, pattern) + matched, err := regexp.MatchString(pattern, value) + + if !matched || err != nil { + log.Debugf("PathVariable %v with a value of %v either doesn't match %v or %v", key, value, pattern, err) + return false + } + } + + return true +} + func NewRoute(pattern string) *Route { regex, keys := pathToRegex(pattern) return &Route{keys, regex, pattern}