Skip to content

Commit

Permalink
Support for wiremock 3.0.0 features (#32)
Browse files Browse the repository at this point in the history
* Add logical not

* Add MatchesJsonSchema

* Add Path templates
  • Loading branch information
walkerus authored Oct 22, 2024
1 parent 21343b1 commit af07491
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 28 deletions.
60 changes: 43 additions & 17 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ func TestStubRule_ToJson(t *testing.T) {
WithScheme("http").
WithPort(8080).
WithBearerToken(StartsWith("token")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
WillReturnResponse(OK()),
ExpectedFileName: "expected-template-bearer-auth-startsWith.json",
},
{
Expand All @@ -96,10 +93,7 @@ func TestStubRule_ToJson(t *testing.T) {
WithScheme("http").
WithPort(8080).
WithBearerToken(EqualTo("token")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
WillReturnResponse(OK()),
ExpectedFileName: "expected-template-bearer-auth-equalTo.json",
},
{
Expand All @@ -109,10 +103,7 @@ func TestStubRule_ToJson(t *testing.T) {
WithScheme("http").
WithPort(8080).
WithBearerToken(Contains("token")).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
WillReturnResponse(OK()),
ExpectedFileName: "expected-template-bearer-auth-contains.json",
},
{
Expand All @@ -122,12 +113,47 @@ func TestStubRule_ToJson(t *testing.T) {
WithScheme("http").
WithPort(8080).
WithBearerToken(EqualTo("token123").And(StartsWith("token"))).
WillReturnResponse(
NewResponse().
WithStatus(http.StatusOK),
),
WillReturnResponse(OK()),
ExpectedFileName: "expected-template-bearer-auth-logicalMatcher.json",
},
{
Name: "NotLogicalMatcher",
StubRule: Post(URLPathEqualTo("/example")).
WithQueryParam("firstName", Not(EqualTo("John").Or(EqualTo("Jack")))).
WillReturnResponse(OK()),
ExpectedFileName: "not-logical-expression.json",
},
{
Name: "JsonSchemaMatcher",
StubRule: Post(URLPathEqualTo("/example")).
WithQueryParam("firstName", MatchesJsonSchema(
`{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
}`,
"V202012",
)).
WillReturnResponse(OK()),
ExpectedFileName: "matches-Json-schema.json",
},
{
Name: "URLPathTemplateMatcher",
StubRule: Get(URLPathTemplate("/contacts/{contactId}/addresses/{addressId}")).
WithPathParam("contactId", EqualTo("12345")).
WithPathParam("addressId", EqualTo("99876")).
WillReturnResponse(OK()),
ExpectedFileName: "url-path-templating.json",
},
}

for _, tc := range testCases {
Expand All @@ -142,7 +168,7 @@ func TestStubRule_ToJson(t *testing.T) {
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)
t.Fatalf("StubRule json.Unmarshal error: %v", err)
}

rawResult, err := json.Marshal(stubRule)
Expand Down
14 changes: 14 additions & 0 deletions logical_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ func (m LogicalMatcher) MarshalJSON() ([]byte, error) {

// ParseMatcher returns the map representation of the structure.
func (m LogicalMatcher) ParseMatcher() map[string]interface{} {
if m.operator == "not" {
return map[string]interface{}{
m.operator: m.operands[0],
}
}

return map[string]interface{}{
m.operator: m.operands,
}
Expand Down Expand Up @@ -56,3 +62,11 @@ func And(matchers ...BasicParamMatcher) LogicalMatcher {
operands: matchers,
}
}

// Not returns a logical NOT of the given matcher. Required wiremock version >= 3.0.0
func Not(matcher BasicParamMatcher) LogicalMatcher {
return LogicalMatcher{
operator: "not",
operands: []BasicParamMatcher{matcher},
}
}
22 changes: 12 additions & 10 deletions matching.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ package wiremock

// Types of params matching.
const (
ParamEqualTo ParamMatchingStrategy = "equalTo"
ParamMatches ParamMatchingStrategy = "matches"
ParamContains ParamMatchingStrategy = "contains"
ParamEqualToXml ParamMatchingStrategy = "equalToXml"
ParamEqualToJson ParamMatchingStrategy = "equalToJson"
ParamMatchesXPath ParamMatchingStrategy = "matchesXPath"
ParamMatchesJsonPath ParamMatchingStrategy = "matchesJsonPath"
ParamAbsent ParamMatchingStrategy = "absent"
ParamDoesNotMatch ParamMatchingStrategy = "doesNotMatch"
ParamDoesNotContains ParamMatchingStrategy = "doesNotContain"
ParamEqualTo ParamMatchingStrategy = "equalTo"
ParamMatches ParamMatchingStrategy = "matches"
ParamContains ParamMatchingStrategy = "contains"
ParamEqualToXml ParamMatchingStrategy = "equalToXml"
ParamEqualToJson ParamMatchingStrategy = "equalToJson"
ParamMatchesXPath ParamMatchingStrategy = "matchesXPath"
ParamMatchesJsonPath ParamMatchingStrategy = "matchesJsonPath"
ParamAbsent ParamMatchingStrategy = "absent"
ParamDoesNotMatch ParamMatchingStrategy = "doesNotMatch"
ParamDoesNotContains ParamMatchingStrategy = "doesNotContain"
ParamMatchesJsonSchema ParamMatchingStrategy = "matchesJsonSchema"
)

// Types of url matching.
Expand All @@ -20,6 +21,7 @@ const (
URLPathEqualToRule URLMatchingStrategy = "urlPath"
URLPathMatchingRule URLMatchingStrategy = "urlPathPattern"
URLMatchingRule URLMatchingStrategy = "urlPattern"
URLPathTemplateRule URLMatchingStrategy = "urlPathTemplate"
)

// Type of less strict matching flags.
Expand Down
16 changes: 15 additions & 1 deletion request.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ type Request struct {
scheme *string
headers map[string]MatcherInterface
queryParams map[string]MatcherInterface
pathParams map[string]MatcherInterface
cookies map[string]BasicParamMatcher
bodyPatterns []BasicParamMatcher
formParameters map[string]BasicParamMatcher
bodyPatterns []BasicParamMatcher
multipartPatterns []MultipartPatternInterface
basicAuthCredentials *struct {
username string
Expand Down Expand Up @@ -104,6 +105,16 @@ func (r *Request) WithQueryParam(param string, matcher MatcherInterface) *Reques
return r
}

// WithPathParam add param to path param list
func (r *Request) WithPathParam(param string, matcher MatcherInterface) *Request {
if r.pathParams == nil {
r.pathParams = map[string]MatcherInterface{}
}

r.pathParams[param] = matcher
return r
}

// WithHeader add header to header list
func (r *Request) WithHeader(header string, matcher MatcherInterface) *Request {
if r.headers == nil {
Expand Down Expand Up @@ -161,6 +172,9 @@ func (r *Request) MarshalJSON() ([]byte, error) {
if len(r.queryParams) > 0 {
request["queryParameters"] = r.queryParams
}
if len(r.pathParams) > 0 {
request["pathParameters"] = r.pathParams
}

if r.basicAuthCredentials != nil {
request["basicAuthCredentials"] = map[string]string{
Expand Down
4 changes: 4 additions & 0 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func NewResponse() Response {
}
}

func OK() Response {
return NewResponse().WithStatus(http.StatusOK)
}

// WithLogNormalRandomDelay sets log normal random delay for response
func (r Response) WithLogNormalRandomDelay(median time.Duration, sigma float64) Response {
r.delayDistribution = NewLogNormalRandomDelay(median, sigma)
Expand Down
22 changes: 22 additions & 0 deletions string_value_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,28 @@ func StartsWith(prefix string) BasicParamMatcher {
return NewStringValueMatcher(ParamMatches, regex)
}

type JSONSchemaMatcher struct {
StringValueMatcher
schemaVersion string
}

// MatchesJsonSchema returns a matcher that matches when the parameter matches the specified JSON schema.
// Required wiremock version >= 3.0.0
func MatchesJsonSchema(schema string, schemaVersion string) BasicParamMatcher {
return JSONSchemaMatcher{
StringValueMatcher: NewStringValueMatcher(ParamMatchesJsonSchema, schema),
schemaVersion: schemaVersion,
}
}

// MarshalJSON returns the JSON encoding of the matcher.
func (m JSONSchemaMatcher) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]string{
string(m.strategy): m.value,
"schemaVersion": m.schemaVersion,
})
}

func regexContainsStartAnchor(regex string) bool {
return len(regex) > 0 && regex[0] == '^'
}
6 changes: 6 additions & 0 deletions stub_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ func (s *StubRule) WithQueryParam(param string, matcher MatcherInterface) *StubR
return s
}

// WithPathParam adds path param and returns *StubRule
func (s *StubRule) WithPathParam(param string, matcher MatcherInterface) *StubRule {
s.request.WithPathParam(param, matcher)
return s
}

// WithPort adds port and returns *StubRule
func (s *StubRule) WithPort(port int64) *StubRule {
s.request.WithPort(port)
Expand Down
17 changes: 17 additions & 0 deletions testdata/matches-Json-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"uuid": "%s",
"id": "%s",
"request": {
"method": "POST",
"urlPath": "/example",
"queryParameters": {
"firstName": {
"matchesJsonSchema" : "{\n \"type\": \"object\",\n \"required\": [\n \"name\"\n ],\n \"properties\": {\n \"name\": {\n \"type\": \"string\"\n },\n \"tag\": {\n \"type\": \"string\"\n }\n }\n}",
"schemaVersion" : "V202012"
}
}
},
"response": {
"status": 200
}
}
25 changes: 25 additions & 0 deletions testdata/not-logical-expression.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"uuid": "%s",
"id": "%s",
"request": {
"method": "POST",
"urlPath": "/example",
"queryParameters": {
"firstName": {
"not": {
"or": [
{
"equalTo": "John"
},
{
"equalTo": "Jack"
}
]
}
}
}
},
"response": {
"status": 200
}
}
19 changes: 19 additions & 0 deletions testdata/url-path-templating.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"uuid": "%s",
"id": "%s",
"request" : {
"urlPathTemplate" : "/contacts/{contactId}/addresses/{addressId}",
"method" : "GET",
"pathParameters" : {
"contactId" : {
"equalTo" : "12345"
},
"addressId" : {
"equalTo" : "99876"
}
}
},
"response" : {
"status" : 200
}
}
11 changes: 11 additions & 0 deletions url_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,14 @@ func URLMatching(url string) URLMatcher {
value: url,
}
}

// URLPathTemplate URL paths can be matched using URI templates, conforming to the same subset of the URI template standard as used in OpenAPI.
// Path variable matchers can also be used in the same manner as query and form parameters.
// Required wiremock >= 3.0.0
// Example: /contacts/{contactId}/addresses/{addressId}
func URLPathTemplate(url string) URLMatcher {
return URLMatcher{
strategy: URLPathTemplateRule,
value: url,
}
}

0 comments on commit af07491

Please sign in to comment.