Skip to content

Commit

Permalink
Add multiple values matchers and logical matchers (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
walkerus authored Aug 1, 2023
1 parent 22d9a6e commit 089e8bb
Show file tree
Hide file tree
Showing 12 changed files with 381 additions and 262 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestSome(t *testing.T) {

// stubbing POST http://0.0.0.0:8080/example
wiremockClient.StubFor(wiremock.Post(wiremock.URLPathEqualTo("/example")).
WithQueryParam("firstName", wiremock.EqualTo("Jhon")).
WithQueryParam("firstName", wiremock.EqualTo("John")).
WithQueryParam("lastName", wiremock.NotMatching("Black")).
WithBodyPattern(wiremock.EqualToJson(`{"meta": "information"}`)).
WithHeader("x-session", wiremock.Matching("^\\S+fingerprint\\S+$")).
Expand Down
12 changes: 10 additions & 2 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ func TestStubRule_ToJson(t *testing.T) {
}

postStubRule := Post(URLPathEqualTo("/example")).
WithQueryParam("firstName", EqualTo("Jhon")).
WithHost(EqualTo("localhost")).
WithScheme("http").
WithPort(8080).
WithQueryParam("firstName", EqualTo("John").Or(EqualTo("Jack"))).
WithQueryParam("lastName", NotMatching("Black")).
WithQueryParam("nickname", EqualToIgnoreCase("jhonBlack")).
WithQueryParam("nickname", EqualToIgnoreCase("johnBlack")).
WithQueryParam("address", Includes(EqualTo("1"), Contains("2"), NotContains("3"))).
WithQueryParam("id", Contains("1").And(NotContains("2"))).
WithBodyPattern(EqualToJson(`{"meta": "information"}`, IgnoreArrayOrder, IgnoreExtraElements)).
WithBodyPattern(Contains("information")).
WithMultipartPattern(
Expand Down Expand Up @@ -53,15 +58,18 @@ func TestStubRule_ToJson(t *testing.T) {
if err != nil {
t.Fatalf("failed to read expected-template.json %v", err)
}

rawResult, err := json.Marshal(postStubRule)
if err != nil {
t.Fatalf("StubRole json.Marshal error: %v", err)
}

var expected map[string]interface{}
err = json.Unmarshal([]byte(fmt.Sprintf(string(rawExpectedRequestBody), postStubRule.uuid, postStubRule.uuid)), &expected)
if err != nil {
t.Fatalf("StubRole json.Unmarshal error: %v", err)
}

var parsedResult map[string]interface{}
err = json.Unmarshal(rawResult, &parsedResult)
if err != nil {
Expand Down
5 changes: 2 additions & 3 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Some might consider it a service virtualization tool or a mock server.
HTTP request:
POST /example?firstName=Jhon&lastName=Any string other than "Gray" HTTP/1.1
POST /example?firstName=John&lastName=Any string other than "Gray" HTTP/1.1
Host: 0.0.0.0:8080
x-session: somefingerprintsome
Content-Type: application/json
Expand All @@ -26,7 +26,7 @@ Stub:
client := wiremock.NewClient("http://0.0.0.0:8080")
client.StubFor(wiremock.Post(wiremock.URLPathEqualTo("/example")).
WithQueryParam("firstName", wiremock.EqualTo("Jhon")).
WithQueryParam("firstName", wiremock.EqualTo("John")).
WithQueryParam("lastName", wiremock.NotMatching("Gray")).
WithBodyPattern(wiremock.EqualToJson(`{"meta": "information"}`)).
WithHeader("x-session", wiremock.Matching("^\\S+fingerprint\\S+$")).
Expand Down Expand Up @@ -61,6 +61,5 @@ You can verify if a request has been made that matches the mapping.
client.StubFor(exampleStubRule)
// ...
client.Verify(exampleStubRule.Request(), 1)
*/
package wiremock
35 changes: 33 additions & 2 deletions expected-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
"newScenarioState": "Stopped",
"request": {
"method": "POST",
"scheme": "http",
"host": {
"equalTo": "localhost"
},
"port": 8080,
"basicAuthCredentials": {
"password": "password",
"username": "username"
Expand Down Expand Up @@ -58,14 +63,40 @@
},
"queryParameters": {
"firstName": {
"equalTo": "Jhon"
"or": [
{
"equalTo": "John"
},
{
"equalTo": "Jack"
}
]
},
"lastName": {
"doesNotMatch": "Black"
},
"nickname": {
"equalTo": "jhonBlack",
"equalTo": "johnBlack",
"caseInsensitive": true
},
"address": {
"includes" : [
{
"equalTo": "1"
},
{
"contains": "2"
},
{
"doesNotContain": "3"
}
]
},
"id": {
"and": [
{"contains": "1"},
{"doesNotContain": "2"}
]
}
},
"urlPath": "/example"
Expand Down
54 changes: 54 additions & 0 deletions logical_matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package wiremock

import (
"encoding/json"
)

type LogicalMatcher struct {
operator string
operands []BasicParamMatcher
}

func (m LogicalMatcher) MarshalJSON() ([]byte, error) {
jsonMap := map[string]interface{}{
m.operator: m.operands,
}

return json.Marshal(jsonMap)
}

// Or returns a logical OR of the current matcher and the given matcher.
func (m LogicalMatcher) Or(matcher BasicParamMatcher) BasicParamMatcher {
if m.operator == "or" {
m.operands = append(m.operands, matcher)
return m
}

return Or(m, matcher)
}

// And returns a logical AND of the current matcher and the given matcher.
func (m LogicalMatcher) And(matcher BasicParamMatcher) BasicParamMatcher {
if m.operator == "and" {
m.operands = append(m.operands, matcher)
return m
}

return And(m, matcher)
}

// Or returns a logical OR of the list of matchers.
func Or(matchers ...BasicParamMatcher) LogicalMatcher {
return LogicalMatcher{
operator: "or",
operands: matchers,
}
}

// And returns a logical AND of the list of matchers.
func And(matchers ...BasicParamMatcher) LogicalMatcher {
return LogicalMatcher{
operator: "and",
operands: matchers,
}
}
168 changes: 8 additions & 160 deletions matching.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const (
ParamMatchesJsonPath ParamMatchingStrategy = "matchesJsonPath"
ParamAbsent ParamMatchingStrategy = "absent"
ParamDoesNotMatch ParamMatchingStrategy = "doesNotMatch"
ParamDoesNotContains ParamMatchingStrategy = "doesNotContain"
)

// Types of url matching.
Expand All @@ -27,6 +28,11 @@ const (
IgnoreExtraElements EqualFlag = "ignoreExtraElements"
)

const (
ParamHasExactly MultiValueMatchingStrategy = "hasExactly"
ParamIncludes MultiValueMatchingStrategy = "includes"
)

// EqualFlag is enum of less strict matching flag.
type EqualFlag string

Expand All @@ -36,163 +42,5 @@ type URLMatchingStrategy string
// ParamMatchingStrategy is enum params matching type.
type ParamMatchingStrategy string

// URLMatcher is structure for defining the type of url matching.
type URLMatcher struct {
strategy URLMatchingStrategy
value string
}

// Strategy returns URLMatchingStrategy of URLMatcher.
func (m URLMatcher) Strategy() URLMatchingStrategy {
return m.strategy
}

// Value returns value of URLMatcher.
func (m URLMatcher) Value() string {
return m.value
}

// URLEqualTo returns URLMatcher with URLEqualToRule matching strategy.
func URLEqualTo(url string) URLMatcher {
return URLMatcher{
strategy: URLEqualToRule,
value: url,
}
}

// URLPathEqualTo returns URLMatcher with URLPathEqualToRule matching strategy.
func URLPathEqualTo(url string) URLMatcher {
return URLMatcher{
strategy: URLPathEqualToRule,
value: url,
}
}

// URLPathMatching returns URLMatcher with URLPathMatchingRule matching strategy.
func URLPathMatching(url string) URLMatcher {
return URLMatcher{
strategy: URLPathMatchingRule,
value: url,
}
}

// URLMatching returns URLMatcher with URLMatchingRule matching strategy.
func URLMatching(url string) URLMatcher {
return URLMatcher{
strategy: URLMatchingRule,
value: url,
}
}

// ParamMatcher is structure for defining the type of params.
type ParamMatcher struct {
strategy ParamMatchingStrategy
value string
flags map[string]bool
}

// Strategy returns ParamMatchingStrategy of ParamMatcher.
func (m ParamMatcher) Strategy() ParamMatchingStrategy {
return m.strategy
}

// Value returns value of ParamMatcher.
func (m ParamMatcher) Value() string {
return m.value
}

// Flags return value of ParamMatcher.
func (m ParamMatcher) Flags() map[string]bool {
return m.flags
}

// EqualTo returns ParamMatcher with ParamEqualTo matching strategy.
func EqualTo(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamEqualTo,
value: param,
}
}

// EqualToIgnoreCase returns ParamMatcher with ParamEqualToIgnoreCase matching strategy
func EqualToIgnoreCase(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamEqualTo,
value: param,
flags: map[string]bool{
"caseInsensitive": true,
},
}
}

// Matching returns ParamMatcher with ParamMatches matching strategy.
func Matching(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamMatches,
value: param,
}
}

// Contains returns ParamMatcher with ParamContains matching strategy.
func Contains(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamContains,
value: param,
}
}

// EqualToXml returns ParamMatcher with ParamEqualToXml matching strategy.
func EqualToXml(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamEqualToXml,
value: param,
}
}

// EqualToJson returns ParamMatcher with ParamEqualToJson matching strategy.
func EqualToJson(param string, flags ...EqualFlag) ParamMatcher {
mflags := make(map[string]bool, len(flags))
for _, flag := range flags {
mflags[string(flag)] = true
}

return ParamMatcher{
strategy: ParamEqualToJson,
value: param,
flags: mflags,
}
}

// MatchingXPath returns ParamMatcher with ParamMatchesXPath matching strategy.
func MatchingXPath(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamMatchesXPath,
value: param,
}
}

// MatchingJsonPath returns ParamMatcher with ParamMatchesJsonPath matching strategy.
func MatchingJsonPath(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamMatchesJsonPath,
value: param,
}
}

// NotMatching returns ParamMatcher with ParamDoesNotMatch matching strategy.
func NotMatching(param string) ParamMatcher {
return ParamMatcher{
strategy: ParamDoesNotMatch,
value: param,
}
}

func Absent() ParamMatcher {
return ParamMatcher{
strategy: ParamAbsent,
value: "",
flags: map[string]bool{
string(ParamAbsent): true,
},
}
}
// MultiValueMatchingStrategy is enum multi value matching type.
type MultiValueMatchingStrategy string
33 changes: 33 additions & 0 deletions multi_value_matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package wiremock

import "encoding/json"

type MultiValueMatcher struct {
strategy MultiValueMatchingStrategy
matchers []BasicParamMatcher
}

// MarshalJSON returns the JSON encoding of the matcher.
func (m MultiValueMatcher) MarshalJSON() ([]byte, error) {
jsonMap := map[string]interface{}{
string(m.strategy): m.matchers,
}

return json.Marshal(jsonMap)
}

// HasExactly returns a matcher that matches when the parameter has exactly the specified values.
func HasExactly(matchers ...BasicParamMatcher) MultiValueMatcher {
return MultiValueMatcher{
strategy: ParamHasExactly,
matchers: matchers,
}
}

// Includes returns a matcher that matches when the parameter includes the specified values.
func Includes(matchers ...BasicParamMatcher) MultiValueMatcher {
return MultiValueMatcher{
strategy: ParamIncludes,
matchers: matchers,
}
}
Loading

0 comments on commit 089e8bb

Please sign in to comment.