Skip to content

Commit

Permalink
Add methods for common Auth schemes (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoniKania authored Sep 1, 2023
1 parent ab5e24f commit e3a9ccf
Show file tree
Hide file tree
Showing 12 changed files with 457 additions and 70 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func TestSome(t *testing.T) {
WithQueryParam("lastName", wiremock.NotMatching("Black")).
WithBodyPattern(wiremock.EqualToJson(`{"meta": "information"}`)).
WithHeader("x-session", wiremock.Matching("^\\S+fingerprint\\S+$")).
WithBearerToken(wiremock.StartsWith("token")).
WillReturnResponse(
wiremock.NewResponse().
WithJSONBody(map[string]interface{}{
Expand Down
203 changes: 134 additions & 69 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,87 +5,152 @@ import (
"fmt"
"net/http"
"os"
"path/filepath"
"reflect"
"testing"
"time"
)

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

if err != nil {
t.Fatalf("StubRole json.Marshal error: %v", err)
}
if string(result) != expectedRequestBody {
t.Errorf("expected requestBody %q; got %q", expectedRequestBody, string(result))
func TestStubRule_ToJson(t *testing.T) {
testCases := []struct {
Name string
StubRule *StubRule
ExpectedFileName string
}{
{
Name: "BasicStubRule",
StubRule: NewStubRule("PATCH", URLMatching("/example")),
ExpectedFileName: "expected-template-basic.json",
},
{
Name: "StubRuleWithScenario",
StubRule: Post(URLPathEqualTo("/example")).
WithHost(EqualTo("localhost")).
WithScheme("http").
WithPort(8080).
WithQueryParam("firstName", EqualTo("John").Or(EqualTo("Jack"))).
WithQueryParam("lastName", NotMatching("Black")).
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(
NewMultipartPattern().
WithName("info").
WithHeader("Content-Type", Contains("charset")).
WithBodyPattern(EqualToJson("{}", IgnoreExtraElements)),
).
WithBasicAuth("username", "password").
WithHeader("x-absent", Absent()).
WithCookie("absentcookie", Absent()).
WithHeader("x-session", Matching("^\\S+@\\S+$")).
WithCookie("session", EqualToXml("<xml>")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusBadRequest).
WithHeader("Content-Type", "application/json").
WithBody(`{"code": 400, "detail": "detail"}`).
WithFault(FaultConnectionResetByPeer).
WithFixedDelay(time.Second*5),
).
WithPostServeAction("webhook", NewWebhook().
WithMethod("POST").
WithURL("http://my-target-host/callback").
WithHeader("Content-Type", "application/json").
WithBody(`{ "result": "SUCCESS" }`).
WithFixedDelay(time.Second)).
AtPriority(1).
InScenario("Scenario").
WhenScenarioStateIs("Started").
WillSetStateTo("Stopped"),
ExpectedFileName: "expected-template-scenario.json",
},
{
Name: "StubRuleWithBearerToken_StartsWithMatcher",
StubRule: Post(URLPathEqualTo("/example")).
WithHost(EqualTo("localhost")).
WithScheme("http").
WithPort(8080).
WithBearerToken(StartsWith("token")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
ExpectedFileName: "expected-template-bearer-auth-startsWith.json",
},
{
Name: "StubRuleWithBearerToken_EqualToMatcher",
StubRule: Post(URLPathEqualTo("/example")).
WithHost(EqualTo("localhost")).
WithScheme("http").
WithPort(8080).
WithBearerToken(EqualTo("token")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
ExpectedFileName: "expected-template-bearer-auth-equalTo.json",
},
{
Name: "StubRuleWithBearerToken_ContainsMatcher",
StubRule: Post(URLPathEqualTo("/example")).
WithHost(EqualTo("localhost")).
WithScheme("http").
WithPort(8080).
WithBearerToken(Contains("token")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
ExpectedFileName: "expected-template-bearer-auth-contains.json",
},
{
Name: "StubRuleWithBearerToken_LogicalMatcher",
StubRule: Post(URLPathEqualTo("/example")).
WithHost(EqualTo("localhost")).
WithScheme("http").
WithPort(8080).
WithBearerToken(EqualTo("token123").And(StartsWith("token"))).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
ExpectedFileName: "expected-template-bearer-auth-logicalMatcher.json",
},
}

postStubRule := Post(URLPathEqualTo("/example")).
WithHost(EqualTo("localhost")).
WithScheme("http").
WithPort(8080).
WithQueryParam("firstName", EqualTo("John").Or(EqualTo("Jack"))).
WithQueryParam("lastName", NotMatching("Black")).
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(
NewMultipartPattern().
WithName("info").
WithHeader("Content-Type", Contains("charset")).
WithBodyPattern(EqualToJson("{}", IgnoreExtraElements)),
).
WithBasicAuth("username", "password").
WithHeader("x-absent", Absent()).
WithCookie("absentcookie", Absent()).
WithHeader("x-session", Matching("^\\S+@\\S+$")).
WithCookie("session", EqualToXml("<xml>")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusBadRequest).
WithHeader("Content-Type", "application/json").
WithBody(`{"code": 400, "detail": "detail"}`).
WithFault(FaultConnectionResetByPeer).
WithFixedDelay(time.Second*5),
).
WithPostServeAction("webhook", NewWebhook().
WithMethod("POST").
WithURL("http://my-target-host/callback").
WithHeader("Content-Type", "application/json").
WithBody(`{ "result": "SUCCESS" }`).
WithFixedDelay(time.Second)).
AtPriority(1).
InScenario("Scenario").
WhenScenarioStateIs("Started").
WillSetStateTo("Stopped")
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
stubRule := tc.StubRule

rawExpectedRequestBody, err := os.ReadFile("expected-template.json")
if err != nil {
t.Fatalf("failed to read expected-template.json %v", err)
}
rawExpectedRequestBody, err := os.ReadFile(filepath.Join(testDataDir, tc.ExpectedFileName))
if err != nil {
t.Fatalf("failed to read expected JSON file %s: %v", tc.ExpectedFileName, 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), stubRule.uuid, stubRule.uuid)), &expected)
if err != nil {
t.Fatalf("StubRole json.Unmarshal 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)
}
rawResult, err := json.Marshal(stubRule)
if err != nil {
t.Fatalf("StubRule json.Marshal error: %v", err)
}

var parsedResult map[string]interface{}
err = json.Unmarshal(rawResult, &parsedResult)
if err != nil {
t.Fatalf("StubRole json.Unmarshal error: %v", err)
}
var parsedResult map[string]interface{}
err = json.Unmarshal(rawResult, &parsedResult)
if err != nil {
t.Fatalf("StubRule json.Unmarshal error: %v", err)
}

if !reflect.DeepEqual(parsedResult, expected) {
t.Errorf("expected requestBody\n%v\n%v", parsedResult, expected)
if !reflect.DeepEqual(parsedResult, expected) {
t.Errorf("expected JSON:\n%v\nactual JSON:\n%v", parsedResult, expected)
}
})
}
}
32 changes: 31 additions & 1 deletion string_value_matcher.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package wiremock

import "encoding/json"
import (
"encoding/json"
"fmt"
"regexp"
)

type MatcherInterface interface {
json.Marshaler
Expand Down Expand Up @@ -49,6 +53,21 @@ func (m StringValueMatcher) And(matcher BasicParamMatcher) BasicParamMatcher {
return And(m, matcher)
}

// addPrefixToMatcher adds prefix to matcher.
// In case of "contains", "absent", "doesNotContain" prefix is not added as it doesn't affect the match result
func (m StringValueMatcher) addPrefixToMatcher(prefix string) BasicParamMatcher {
switch m.strategy {
case ParamEqualTo, ParamEqualToJson, ParamEqualToXml, ParamMatchesJsonPath, ParamMatchesXPath:
m.value = prefix + m.value
case ParamMatches, ParamDoesNotMatch:
if regexContainsStartAnchor(m.value) {
m.value = m.value[1:]
}
m.value = fmt.Sprintf("^%s", prefix) + m.value
}
return m
}

// NewStringValueMatcher creates a new StringValueMatcher.
func NewStringValueMatcher(strategy ParamMatchingStrategy, value string, flags ...string) StringValueMatcher {
return StringValueMatcher{
Expand Down Expand Up @@ -119,3 +138,14 @@ func Contains(param string) BasicParamMatcher {
func NotContains(param string) BasicParamMatcher {
return NewStringValueMatcher(ParamDoesNotContains, param)
}

// StartsWith returns a matcher that matches when the parameter starts with the specified prefix.
// Matches also when prefix alone is the whole expression
func StartsWith(prefix string) BasicParamMatcher {
regex := fmt.Sprintf(`^%s\s*\S*`, regexp.QuoteMeta(prefix))
return NewStringValueMatcher(ParamMatches, regex)
}

func regexContainsStartAnchor(regex string) bool {
return len(regex) > 0 && regex[0] == '^'
}
94 changes: 94 additions & 0 deletions string_value_matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package wiremock

import (
"testing"
)

func TestStringValueMatcher_AddPrefixToMatcher(t *testing.T) {
testCases := []struct {
name string
strategy ParamMatchingStrategy
value string
prefix string
expected string
}{
{
name: "EqualTo - Add prefix to the value",
strategy: ParamEqualTo,
value: "abc",
prefix: "pre_",
expected: "pre_abc",
},
{
name: "Matches - Add prefix to regex value without start anchor",
strategy: ParamMatches,
value: "abc",
prefix: "pre_",
expected: "^pre_abc",
},
{
name: "Matches - Add prefix to regex value with start anchor",
strategy: ParamMatches,
value: "^abc",
prefix: "pre_",
expected: "^pre_abc",
},
{
name: "Matches - Add prefix to regex value with end anchor",
strategy: ParamMatches,
value: "t?o?ken$",
prefix: "pre_",
expected: "^pre_t?o?ken$",
},
{
name: "Matches - Should add prefix to wildcard regex",
strategy: ParamMatches,
value: ".*",
prefix: "pre_",
expected: "^pre_.*",
},
{
name: "Matches - Should add prefix to empty regex",
strategy: ParamMatches,
value: "",
prefix: "pre_",
expected: "^pre_",
},
{
name: "DoesNotMatch - Add prefix to regex value without start anchor",
strategy: ParamDoesNotMatch,
value: "abc",
prefix: "pre_",
expected: "^pre_abc",
},
{
name: "DoesNotMatch - Add prefix to regex value with start anchor",
strategy: ParamDoesNotMatch,
value: "^abc",
prefix: "pre_",
expected: "^pre_abc",
},
{
name: "DoesNotMatch - wildcard regex",
strategy: ParamDoesNotMatch,
value: ".*",
prefix: "pre_",
expected: "^pre_.*",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
matcher := StringValueMatcher{
strategy: tc.strategy,
value: tc.value,
}

modifiedMatcher := matcher.addPrefixToMatcher(tc.prefix)

if modifiedMatcher.(StringValueMatcher).value != tc.expected {
t.Errorf("Expected: %s, Got: %s", tc.expected, modifiedMatcher.(StringValueMatcher).value)
}
})
}
}
Loading

0 comments on commit e3a9ccf

Please sign in to comment.