Skip to content

Commit e3a9ccf

Browse files
authored
Add methods for common Auth schemes (#27)
1 parent ab5e24f commit e3a9ccf

12 files changed

+457
-70
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func TestSome(t *testing.T) {
6666
WithQueryParam("lastName", wiremock.NotMatching("Black")).
6767
WithBodyPattern(wiremock.EqualToJson(`{"meta": "information"}`)).
6868
WithHeader("x-session", wiremock.Matching("^\\S+fingerprint\\S+$")).
69+
WithBearerToken(wiremock.StartsWith("token")).
6970
WillReturnResponse(
7071
wiremock.NewResponse().
7172
WithJSONBody(map[string]interface{}{

client_test.go

Lines changed: 134 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,87 +5,152 @@ import (
55
"fmt"
66
"net/http"
77
"os"
8+
"path/filepath"
89
"reflect"
910
"testing"
1011
"time"
1112
)
1213

13-
func TestStubRule_ToJson(t *testing.T) {
14-
newStubRule := NewStubRule("PATCH", URLMatching("/example"))
15-
expectedRequestBody := fmt.Sprintf(`{"uuid":"%s","id":"%s","request":{"method":"PATCH","urlPattern":"/example"},"response":{"status":200}}`, newStubRule.uuid, newStubRule.uuid)
16-
result, err := json.Marshal(newStubRule)
14+
const testDataDir = "testdata"
1715

18-
if err != nil {
19-
t.Fatalf("StubRole json.Marshal error: %v", err)
20-
}
21-
if string(result) != expectedRequestBody {
22-
t.Errorf("expected requestBody %q; got %q", expectedRequestBody, string(result))
16+
func TestStubRule_ToJson(t *testing.T) {
17+
testCases := []struct {
18+
Name string
19+
StubRule *StubRule
20+
ExpectedFileName string
21+
}{
22+
{
23+
Name: "BasicStubRule",
24+
StubRule: NewStubRule("PATCH", URLMatching("/example")),
25+
ExpectedFileName: "expected-template-basic.json",
26+
},
27+
{
28+
Name: "StubRuleWithScenario",
29+
StubRule: Post(URLPathEqualTo("/example")).
30+
WithHost(EqualTo("localhost")).
31+
WithScheme("http").
32+
WithPort(8080).
33+
WithQueryParam("firstName", EqualTo("John").Or(EqualTo("Jack"))).
34+
WithQueryParam("lastName", NotMatching("Black")).
35+
WithQueryParam("nickname", EqualToIgnoreCase("johnBlack")).
36+
WithQueryParam("address", Includes(EqualTo("1"), Contains("2"), NotContains("3"))).
37+
WithQueryParam("id", Contains("1").And(NotContains("2"))).
38+
WithBodyPattern(EqualToJson(`{"meta": "information"}`, IgnoreArrayOrder, IgnoreExtraElements)).
39+
WithBodyPattern(Contains("information")).
40+
WithMultipartPattern(
41+
NewMultipartPattern().
42+
WithName("info").
43+
WithHeader("Content-Type", Contains("charset")).
44+
WithBodyPattern(EqualToJson("{}", IgnoreExtraElements)),
45+
).
46+
WithBasicAuth("username", "password").
47+
WithHeader("x-absent", Absent()).
48+
WithCookie("absentcookie", Absent()).
49+
WithHeader("x-session", Matching("^\\S+@\\S+$")).
50+
WithCookie("session", EqualToXml("<xml>")).
51+
WillReturnResponse(
52+
NewResponse().
53+
WithStatus(http.StatusBadRequest).
54+
WithHeader("Content-Type", "application/json").
55+
WithBody(`{"code": 400, "detail": "detail"}`).
56+
WithFault(FaultConnectionResetByPeer).
57+
WithFixedDelay(time.Second*5),
58+
).
59+
WithPostServeAction("webhook", NewWebhook().
60+
WithMethod("POST").
61+
WithURL("http://my-target-host/callback").
62+
WithHeader("Content-Type", "application/json").
63+
WithBody(`{ "result": "SUCCESS" }`).
64+
WithFixedDelay(time.Second)).
65+
AtPriority(1).
66+
InScenario("Scenario").
67+
WhenScenarioStateIs("Started").
68+
WillSetStateTo("Stopped"),
69+
ExpectedFileName: "expected-template-scenario.json",
70+
},
71+
{
72+
Name: "StubRuleWithBearerToken_StartsWithMatcher",
73+
StubRule: Post(URLPathEqualTo("/example")).
74+
WithHost(EqualTo("localhost")).
75+
WithScheme("http").
76+
WithPort(8080).
77+
WithBearerToken(StartsWith("token")).
78+
WillReturnResponse(
79+
NewResponse().
80+
WithStatus(http.StatusOK),
81+
),
82+
ExpectedFileName: "expected-template-bearer-auth-startsWith.json",
83+
},
84+
{
85+
Name: "StubRuleWithBearerToken_EqualToMatcher",
86+
StubRule: Post(URLPathEqualTo("/example")).
87+
WithHost(EqualTo("localhost")).
88+
WithScheme("http").
89+
WithPort(8080).
90+
WithBearerToken(EqualTo("token")).
91+
WillReturnResponse(
92+
NewResponse().
93+
WithStatus(http.StatusOK),
94+
),
95+
ExpectedFileName: "expected-template-bearer-auth-equalTo.json",
96+
},
97+
{
98+
Name: "StubRuleWithBearerToken_ContainsMatcher",
99+
StubRule: Post(URLPathEqualTo("/example")).
100+
WithHost(EqualTo("localhost")).
101+
WithScheme("http").
102+
WithPort(8080).
103+
WithBearerToken(Contains("token")).
104+
WillReturnResponse(
105+
NewResponse().
106+
WithStatus(http.StatusOK),
107+
),
108+
ExpectedFileName: "expected-template-bearer-auth-contains.json",
109+
},
110+
{
111+
Name: "StubRuleWithBearerToken_LogicalMatcher",
112+
StubRule: Post(URLPathEqualTo("/example")).
113+
WithHost(EqualTo("localhost")).
114+
WithScheme("http").
115+
WithPort(8080).
116+
WithBearerToken(EqualTo("token123").And(StartsWith("token"))).
117+
WillReturnResponse(
118+
NewResponse().
119+
WithStatus(http.StatusOK),
120+
),
121+
ExpectedFileName: "expected-template-bearer-auth-logicalMatcher.json",
122+
},
23123
}
24124

25-
postStubRule := Post(URLPathEqualTo("/example")).
26-
WithHost(EqualTo("localhost")).
27-
WithScheme("http").
28-
WithPort(8080).
29-
WithQueryParam("firstName", EqualTo("John").Or(EqualTo("Jack"))).
30-
WithQueryParam("lastName", NotMatching("Black")).
31-
WithQueryParam("nickname", EqualToIgnoreCase("johnBlack")).
32-
WithQueryParam("address", Includes(EqualTo("1"), Contains("2"), NotContains("3"))).
33-
WithQueryParam("id", Contains("1").And(NotContains("2"))).
34-
WithBodyPattern(EqualToJson(`{"meta": "information"}`, IgnoreArrayOrder, IgnoreExtraElements)).
35-
WithBodyPattern(Contains("information")).
36-
WithMultipartPattern(
37-
NewMultipartPattern().
38-
WithName("info").
39-
WithHeader("Content-Type", Contains("charset")).
40-
WithBodyPattern(EqualToJson("{}", IgnoreExtraElements)),
41-
).
42-
WithBasicAuth("username", "password").
43-
WithHeader("x-absent", Absent()).
44-
WithCookie("absentcookie", Absent()).
45-
WithHeader("x-session", Matching("^\\S+@\\S+$")).
46-
WithCookie("session", EqualToXml("<xml>")).
47-
WillReturnResponse(
48-
NewResponse().
49-
WithStatus(http.StatusBadRequest).
50-
WithHeader("Content-Type", "application/json").
51-
WithBody(`{"code": 400, "detail": "detail"}`).
52-
WithFault(FaultConnectionResetByPeer).
53-
WithFixedDelay(time.Second*5),
54-
).
55-
WithPostServeAction("webhook", NewWebhook().
56-
WithMethod("POST").
57-
WithURL("http://my-target-host/callback").
58-
WithHeader("Content-Type", "application/json").
59-
WithBody(`{ "result": "SUCCESS" }`).
60-
WithFixedDelay(time.Second)).
61-
AtPriority(1).
62-
InScenario("Scenario").
63-
WhenScenarioStateIs("Started").
64-
WillSetStateTo("Stopped")
125+
for _, tc := range testCases {
126+
t.Run(tc.Name, func(t *testing.T) {
127+
stubRule := tc.StubRule
65128

66-
rawExpectedRequestBody, err := os.ReadFile("expected-template.json")
67-
if err != nil {
68-
t.Fatalf("failed to read expected-template.json %v", err)
69-
}
129+
rawExpectedRequestBody, err := os.ReadFile(filepath.Join(testDataDir, tc.ExpectedFileName))
130+
if err != nil {
131+
t.Fatalf("failed to read expected JSON file %s: %v", tc.ExpectedFileName, err)
132+
}
70133

71-
rawResult, err := json.Marshal(postStubRule)
72-
if err != nil {
73-
t.Fatalf("StubRole json.Marshal error: %v", err)
74-
}
134+
var expected map[string]interface{}
135+
err = json.Unmarshal([]byte(fmt.Sprintf(string(rawExpectedRequestBody), stubRule.uuid, stubRule.uuid)), &expected)
136+
if err != nil {
137+
t.Fatalf("StubRole json.Unmarshal error: %v", err)
138+
}
75139

76-
var expected map[string]interface{}
77-
err = json.Unmarshal([]byte(fmt.Sprintf(string(rawExpectedRequestBody), postStubRule.uuid, postStubRule.uuid)), &expected)
78-
if err != nil {
79-
t.Fatalf("StubRole json.Unmarshal error: %v", err)
80-
}
140+
rawResult, err := json.Marshal(stubRule)
141+
if err != nil {
142+
t.Fatalf("StubRule json.Marshal error: %v", err)
143+
}
81144

82-
var parsedResult map[string]interface{}
83-
err = json.Unmarshal(rawResult, &parsedResult)
84-
if err != nil {
85-
t.Fatalf("StubRole json.Unmarshal error: %v", err)
86-
}
145+
var parsedResult map[string]interface{}
146+
err = json.Unmarshal(rawResult, &parsedResult)
147+
if err != nil {
148+
t.Fatalf("StubRule json.Unmarshal error: %v", err)
149+
}
87150

88-
if !reflect.DeepEqual(parsedResult, expected) {
89-
t.Errorf("expected requestBody\n%v\n%v", parsedResult, expected)
151+
if !reflect.DeepEqual(parsedResult, expected) {
152+
t.Errorf("expected JSON:\n%v\nactual JSON:\n%v", parsedResult, expected)
153+
}
154+
})
90155
}
91156
}

string_value_matcher.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package wiremock
22

3-
import "encoding/json"
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"regexp"
7+
)
48

59
type MatcherInterface interface {
610
json.Marshaler
@@ -49,6 +53,21 @@ func (m StringValueMatcher) And(matcher BasicParamMatcher) BasicParamMatcher {
4953
return And(m, matcher)
5054
}
5155

56+
// addPrefixToMatcher adds prefix to matcher.
57+
// In case of "contains", "absent", "doesNotContain" prefix is not added as it doesn't affect the match result
58+
func (m StringValueMatcher) addPrefixToMatcher(prefix string) BasicParamMatcher {
59+
switch m.strategy {
60+
case ParamEqualTo, ParamEqualToJson, ParamEqualToXml, ParamMatchesJsonPath, ParamMatchesXPath:
61+
m.value = prefix + m.value
62+
case ParamMatches, ParamDoesNotMatch:
63+
if regexContainsStartAnchor(m.value) {
64+
m.value = m.value[1:]
65+
}
66+
m.value = fmt.Sprintf("^%s", prefix) + m.value
67+
}
68+
return m
69+
}
70+
5271
// NewStringValueMatcher creates a new StringValueMatcher.
5372
func NewStringValueMatcher(strategy ParamMatchingStrategy, value string, flags ...string) StringValueMatcher {
5473
return StringValueMatcher{
@@ -119,3 +138,14 @@ func Contains(param string) BasicParamMatcher {
119138
func NotContains(param string) BasicParamMatcher {
120139
return NewStringValueMatcher(ParamDoesNotContains, param)
121140
}
141+
142+
// StartsWith returns a matcher that matches when the parameter starts with the specified prefix.
143+
// Matches also when prefix alone is the whole expression
144+
func StartsWith(prefix string) BasicParamMatcher {
145+
regex := fmt.Sprintf(`^%s\s*\S*`, regexp.QuoteMeta(prefix))
146+
return NewStringValueMatcher(ParamMatches, regex)
147+
}
148+
149+
func regexContainsStartAnchor(regex string) bool {
150+
return len(regex) > 0 && regex[0] == '^'
151+
}

string_value_matcher_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package wiremock
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestStringValueMatcher_AddPrefixToMatcher(t *testing.T) {
8+
testCases := []struct {
9+
name string
10+
strategy ParamMatchingStrategy
11+
value string
12+
prefix string
13+
expected string
14+
}{
15+
{
16+
name: "EqualTo - Add prefix to the value",
17+
strategy: ParamEqualTo,
18+
value: "abc",
19+
prefix: "pre_",
20+
expected: "pre_abc",
21+
},
22+
{
23+
name: "Matches - Add prefix to regex value without start anchor",
24+
strategy: ParamMatches,
25+
value: "abc",
26+
prefix: "pre_",
27+
expected: "^pre_abc",
28+
},
29+
{
30+
name: "Matches - Add prefix to regex value with start anchor",
31+
strategy: ParamMatches,
32+
value: "^abc",
33+
prefix: "pre_",
34+
expected: "^pre_abc",
35+
},
36+
{
37+
name: "Matches - Add prefix to regex value with end anchor",
38+
strategy: ParamMatches,
39+
value: "t?o?ken$",
40+
prefix: "pre_",
41+
expected: "^pre_t?o?ken$",
42+
},
43+
{
44+
name: "Matches - Should add prefix to wildcard regex",
45+
strategy: ParamMatches,
46+
value: ".*",
47+
prefix: "pre_",
48+
expected: "^pre_.*",
49+
},
50+
{
51+
name: "Matches - Should add prefix to empty regex",
52+
strategy: ParamMatches,
53+
value: "",
54+
prefix: "pre_",
55+
expected: "^pre_",
56+
},
57+
{
58+
name: "DoesNotMatch - Add prefix to regex value without start anchor",
59+
strategy: ParamDoesNotMatch,
60+
value: "abc",
61+
prefix: "pre_",
62+
expected: "^pre_abc",
63+
},
64+
{
65+
name: "DoesNotMatch - Add prefix to regex value with start anchor",
66+
strategy: ParamDoesNotMatch,
67+
value: "^abc",
68+
prefix: "pre_",
69+
expected: "^pre_abc",
70+
},
71+
{
72+
name: "DoesNotMatch - wildcard regex",
73+
strategy: ParamDoesNotMatch,
74+
value: ".*",
75+
prefix: "pre_",
76+
expected: "^pre_.*",
77+
},
78+
}
79+
80+
for _, tc := range testCases {
81+
t.Run(tc.name, func(t *testing.T) {
82+
matcher := StringValueMatcher{
83+
strategy: tc.strategy,
84+
value: tc.value,
85+
}
86+
87+
modifiedMatcher := matcher.addPrefixToMatcher(tc.prefix)
88+
89+
if modifiedMatcher.(StringValueMatcher).value != tc.expected {
90+
t.Errorf("Expected: %s, Got: %s", tc.expected, modifiedMatcher.(StringValueMatcher).value)
91+
}
92+
})
93+
}
94+
}

0 commit comments

Comments
 (0)