Skip to content

Commit 089e8bb

Browse files
authored
Add multiple values matchers and logical matchers (#14)
1 parent 22d9a6e commit 089e8bb

12 files changed

+381
-262
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestSome(t *testing.T) {
2626

2727
// stubbing POST http://0.0.0.0:8080/example
2828
wiremockClient.StubFor(wiremock.Post(wiremock.URLPathEqualTo("/example")).
29-
WithQueryParam("firstName", wiremock.EqualTo("Jhon")).
29+
WithQueryParam("firstName", wiremock.EqualTo("John")).
3030
WithQueryParam("lastName", wiremock.NotMatching("Black")).
3131
WithBodyPattern(wiremock.EqualToJson(`{"meta": "information"}`)).
3232
WithHeader("x-session", wiremock.Matching("^\\S+fingerprint\\S+$")).

client_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ func TestStubRule_ToJson(t *testing.T) {
2222
}
2323

2424
postStubRule := Post(URLPathEqualTo("/example")).
25-
WithQueryParam("firstName", EqualTo("Jhon")).
25+
WithHost(EqualTo("localhost")).
26+
WithScheme("http").
27+
WithPort(8080).
28+
WithQueryParam("firstName", EqualTo("John").Or(EqualTo("Jack"))).
2629
WithQueryParam("lastName", NotMatching("Black")).
27-
WithQueryParam("nickname", EqualToIgnoreCase("jhonBlack")).
30+
WithQueryParam("nickname", EqualToIgnoreCase("johnBlack")).
31+
WithQueryParam("address", Includes(EqualTo("1"), Contains("2"), NotContains("3"))).
32+
WithQueryParam("id", Contains("1").And(NotContains("2"))).
2833
WithBodyPattern(EqualToJson(`{"meta": "information"}`, IgnoreArrayOrder, IgnoreExtraElements)).
2934
WithBodyPattern(Contains("information")).
3035
WithMultipartPattern(
@@ -53,15 +58,18 @@ func TestStubRule_ToJson(t *testing.T) {
5358
if err != nil {
5459
t.Fatalf("failed to read expected-template.json %v", err)
5560
}
61+
5662
rawResult, err := json.Marshal(postStubRule)
5763
if err != nil {
5864
t.Fatalf("StubRole json.Marshal error: %v", err)
5965
}
66+
6067
var expected map[string]interface{}
6168
err = json.Unmarshal([]byte(fmt.Sprintf(string(rawExpectedRequestBody), postStubRule.uuid, postStubRule.uuid)), &expected)
6269
if err != nil {
6370
t.Fatalf("StubRole json.Unmarshal error: %v", err)
6471
}
72+
6573
var parsedResult map[string]interface{}
6674
err = json.Unmarshal(rawResult, &parsedResult)
6775
if err != nil {

doc.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Some might consider it a service virtualization tool or a mock server.
88
99
HTTP request:
1010
11-
POST /example?firstName=Jhon&lastName=Any string other than "Gray" HTTP/1.1
11+
POST /example?firstName=John&lastName=Any string other than "Gray" HTTP/1.1
1212
Host: 0.0.0.0:8080
1313
x-session: somefingerprintsome
1414
Content-Type: application/json
@@ -26,7 +26,7 @@ Stub:
2626
2727
client := wiremock.NewClient("http://0.0.0.0:8080")
2828
client.StubFor(wiremock.Post(wiremock.URLPathEqualTo("/example")).
29-
WithQueryParam("firstName", wiremock.EqualTo("Jhon")).
29+
WithQueryParam("firstName", wiremock.EqualTo("John")).
3030
WithQueryParam("lastName", wiremock.NotMatching("Gray")).
3131
WithBodyPattern(wiremock.EqualToJson(`{"meta": "information"}`)).
3232
WithHeader("x-session", wiremock.Matching("^\\S+fingerprint\\S+$")).
@@ -61,6 +61,5 @@ You can verify if a request has been made that matches the mapping.
6161
client.StubFor(exampleStubRule)
6262
// ...
6363
client.Verify(exampleStubRule.Request(), 1)
64-
6564
*/
6665
package wiremock

expected-template.json

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
"newScenarioState": "Stopped",
88
"request": {
99
"method": "POST",
10+
"scheme": "http",
11+
"host": {
12+
"equalTo": "localhost"
13+
},
14+
"port": 8080,
1015
"basicAuthCredentials": {
1116
"password": "password",
1217
"username": "username"
@@ -58,14 +63,40 @@
5863
},
5964
"queryParameters": {
6065
"firstName": {
61-
"equalTo": "Jhon"
66+
"or": [
67+
{
68+
"equalTo": "John"
69+
},
70+
{
71+
"equalTo": "Jack"
72+
}
73+
]
6274
},
6375
"lastName": {
6476
"doesNotMatch": "Black"
6577
},
6678
"nickname": {
67-
"equalTo": "jhonBlack",
79+
"equalTo": "johnBlack",
6880
"caseInsensitive": true
81+
},
82+
"address": {
83+
"includes" : [
84+
{
85+
"equalTo": "1"
86+
},
87+
{
88+
"contains": "2"
89+
},
90+
{
91+
"doesNotContain": "3"
92+
}
93+
]
94+
},
95+
"id": {
96+
"and": [
97+
{"contains": "1"},
98+
{"doesNotContain": "2"}
99+
]
69100
}
70101
},
71102
"urlPath": "/example"

logical_matcher.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package wiremock
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
type LogicalMatcher struct {
8+
operator string
9+
operands []BasicParamMatcher
10+
}
11+
12+
func (m LogicalMatcher) MarshalJSON() ([]byte, error) {
13+
jsonMap := map[string]interface{}{
14+
m.operator: m.operands,
15+
}
16+
17+
return json.Marshal(jsonMap)
18+
}
19+
20+
// Or returns a logical OR of the current matcher and the given matcher.
21+
func (m LogicalMatcher) Or(matcher BasicParamMatcher) BasicParamMatcher {
22+
if m.operator == "or" {
23+
m.operands = append(m.operands, matcher)
24+
return m
25+
}
26+
27+
return Or(m, matcher)
28+
}
29+
30+
// And returns a logical AND of the current matcher and the given matcher.
31+
func (m LogicalMatcher) And(matcher BasicParamMatcher) BasicParamMatcher {
32+
if m.operator == "and" {
33+
m.operands = append(m.operands, matcher)
34+
return m
35+
}
36+
37+
return And(m, matcher)
38+
}
39+
40+
// Or returns a logical OR of the list of matchers.
41+
func Or(matchers ...BasicParamMatcher) LogicalMatcher {
42+
return LogicalMatcher{
43+
operator: "or",
44+
operands: matchers,
45+
}
46+
}
47+
48+
// And returns a logical AND of the list of matchers.
49+
func And(matchers ...BasicParamMatcher) LogicalMatcher {
50+
return LogicalMatcher{
51+
operator: "and",
52+
operands: matchers,
53+
}
54+
}

matching.go

Lines changed: 8 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const (
1111
ParamMatchesJsonPath ParamMatchingStrategy = "matchesJsonPath"
1212
ParamAbsent ParamMatchingStrategy = "absent"
1313
ParamDoesNotMatch ParamMatchingStrategy = "doesNotMatch"
14+
ParamDoesNotContains ParamMatchingStrategy = "doesNotContain"
1415
)
1516

1617
// Types of url matching.
@@ -27,6 +28,11 @@ const (
2728
IgnoreExtraElements EqualFlag = "ignoreExtraElements"
2829
)
2930

31+
const (
32+
ParamHasExactly MultiValueMatchingStrategy = "hasExactly"
33+
ParamIncludes MultiValueMatchingStrategy = "includes"
34+
)
35+
3036
// EqualFlag is enum of less strict matching flag.
3137
type EqualFlag string
3238

@@ -36,163 +42,5 @@ type URLMatchingStrategy string
3642
// ParamMatchingStrategy is enum params matching type.
3743
type ParamMatchingStrategy string
3844

39-
// URLMatcher is structure for defining the type of url matching.
40-
type URLMatcher struct {
41-
strategy URLMatchingStrategy
42-
value string
43-
}
44-
45-
// Strategy returns URLMatchingStrategy of URLMatcher.
46-
func (m URLMatcher) Strategy() URLMatchingStrategy {
47-
return m.strategy
48-
}
49-
50-
// Value returns value of URLMatcher.
51-
func (m URLMatcher) Value() string {
52-
return m.value
53-
}
54-
55-
// URLEqualTo returns URLMatcher with URLEqualToRule matching strategy.
56-
func URLEqualTo(url string) URLMatcher {
57-
return URLMatcher{
58-
strategy: URLEqualToRule,
59-
value: url,
60-
}
61-
}
62-
63-
// URLPathEqualTo returns URLMatcher with URLPathEqualToRule matching strategy.
64-
func URLPathEqualTo(url string) URLMatcher {
65-
return URLMatcher{
66-
strategy: URLPathEqualToRule,
67-
value: url,
68-
}
69-
}
70-
71-
// URLPathMatching returns URLMatcher with URLPathMatchingRule matching strategy.
72-
func URLPathMatching(url string) URLMatcher {
73-
return URLMatcher{
74-
strategy: URLPathMatchingRule,
75-
value: url,
76-
}
77-
}
78-
79-
// URLMatching returns URLMatcher with URLMatchingRule matching strategy.
80-
func URLMatching(url string) URLMatcher {
81-
return URLMatcher{
82-
strategy: URLMatchingRule,
83-
value: url,
84-
}
85-
}
86-
87-
// ParamMatcher is structure for defining the type of params.
88-
type ParamMatcher struct {
89-
strategy ParamMatchingStrategy
90-
value string
91-
flags map[string]bool
92-
}
93-
94-
// Strategy returns ParamMatchingStrategy of ParamMatcher.
95-
func (m ParamMatcher) Strategy() ParamMatchingStrategy {
96-
return m.strategy
97-
}
98-
99-
// Value returns value of ParamMatcher.
100-
func (m ParamMatcher) Value() string {
101-
return m.value
102-
}
103-
104-
// Flags return value of ParamMatcher.
105-
func (m ParamMatcher) Flags() map[string]bool {
106-
return m.flags
107-
}
108-
109-
// EqualTo returns ParamMatcher with ParamEqualTo matching strategy.
110-
func EqualTo(param string) ParamMatcher {
111-
return ParamMatcher{
112-
strategy: ParamEqualTo,
113-
value: param,
114-
}
115-
}
116-
117-
// EqualToIgnoreCase returns ParamMatcher with ParamEqualToIgnoreCase matching strategy
118-
func EqualToIgnoreCase(param string) ParamMatcher {
119-
return ParamMatcher{
120-
strategy: ParamEqualTo,
121-
value: param,
122-
flags: map[string]bool{
123-
"caseInsensitive": true,
124-
},
125-
}
126-
}
127-
128-
// Matching returns ParamMatcher with ParamMatches matching strategy.
129-
func Matching(param string) ParamMatcher {
130-
return ParamMatcher{
131-
strategy: ParamMatches,
132-
value: param,
133-
}
134-
}
135-
136-
// Contains returns ParamMatcher with ParamContains matching strategy.
137-
func Contains(param string) ParamMatcher {
138-
return ParamMatcher{
139-
strategy: ParamContains,
140-
value: param,
141-
}
142-
}
143-
144-
// EqualToXml returns ParamMatcher with ParamEqualToXml matching strategy.
145-
func EqualToXml(param string) ParamMatcher {
146-
return ParamMatcher{
147-
strategy: ParamEqualToXml,
148-
value: param,
149-
}
150-
}
151-
152-
// EqualToJson returns ParamMatcher with ParamEqualToJson matching strategy.
153-
func EqualToJson(param string, flags ...EqualFlag) ParamMatcher {
154-
mflags := make(map[string]bool, len(flags))
155-
for _, flag := range flags {
156-
mflags[string(flag)] = true
157-
}
158-
159-
return ParamMatcher{
160-
strategy: ParamEqualToJson,
161-
value: param,
162-
flags: mflags,
163-
}
164-
}
165-
166-
// MatchingXPath returns ParamMatcher with ParamMatchesXPath matching strategy.
167-
func MatchingXPath(param string) ParamMatcher {
168-
return ParamMatcher{
169-
strategy: ParamMatchesXPath,
170-
value: param,
171-
}
172-
}
173-
174-
// MatchingJsonPath returns ParamMatcher with ParamMatchesJsonPath matching strategy.
175-
func MatchingJsonPath(param string) ParamMatcher {
176-
return ParamMatcher{
177-
strategy: ParamMatchesJsonPath,
178-
value: param,
179-
}
180-
}
181-
182-
// NotMatching returns ParamMatcher with ParamDoesNotMatch matching strategy.
183-
func NotMatching(param string) ParamMatcher {
184-
return ParamMatcher{
185-
strategy: ParamDoesNotMatch,
186-
value: param,
187-
}
188-
}
189-
190-
func Absent() ParamMatcher {
191-
return ParamMatcher{
192-
strategy: ParamAbsent,
193-
value: "",
194-
flags: map[string]bool{
195-
string(ParamAbsent): true,
196-
},
197-
}
198-
}
45+
// MultiValueMatchingStrategy is enum multi value matching type.
46+
type MultiValueMatchingStrategy string

multi_value_matcher.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package wiremock
2+
3+
import "encoding/json"
4+
5+
type MultiValueMatcher struct {
6+
strategy MultiValueMatchingStrategy
7+
matchers []BasicParamMatcher
8+
}
9+
10+
// MarshalJSON returns the JSON encoding of the matcher.
11+
func (m MultiValueMatcher) MarshalJSON() ([]byte, error) {
12+
jsonMap := map[string]interface{}{
13+
string(m.strategy): m.matchers,
14+
}
15+
16+
return json.Marshal(jsonMap)
17+
}
18+
19+
// HasExactly returns a matcher that matches when the parameter has exactly the specified values.
20+
func HasExactly(matchers ...BasicParamMatcher) MultiValueMatcher {
21+
return MultiValueMatcher{
22+
strategy: ParamHasExactly,
23+
matchers: matchers,
24+
}
25+
}
26+
27+
// Includes returns a matcher that matches when the parameter includes the specified values.
28+
func Includes(matchers ...BasicParamMatcher) MultiValueMatcher {
29+
return MultiValueMatcher{
30+
strategy: ParamIncludes,
31+
matchers: matchers,
32+
}
33+
}

0 commit comments

Comments
 (0)