Skip to content

Commit

Permalink
Adding multipart support (#8)
Browse files Browse the repository at this point in the history
Co-authored-by: rrisamukhametov <[email protected]>
  • Loading branch information
walkerus and rrisamukhametov authored Aug 2, 2022
1 parent ce63802 commit a9890bf
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.31
version: v1.47.3
- name: Tests
run: go test
36 changes: 26 additions & 10 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package wiremock
import (
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"testing"
"time"
)
Expand All @@ -24,6 +26,12 @@ func TestStubRule_ToJson(t *testing.T) {
WithQueryParam("lastName", NotMatching("Black")).
WithBodyPattern(EqualToJson(`{"meta": "information"}`)).
WithBodyPattern(Contains("information")).
WithMultipartPattern(
NewMultipartPattern().
WithName("info").
WithHeader("Content-Type", Contains("charset")).
WithBodyPattern(EqualToJson("{}")),
).
WithBasicAuth("username", "password").
WithHeader("x-session", Matching("^\\S+@\\S+$")).
WithCookie("session", EqualToXml("<xml>")).
Expand All @@ -38,18 +46,26 @@ func TestStubRule_ToJson(t *testing.T) {
WhenScenarioStateIs("Started").
WillSetStateTo("Stopped")

expectedRequestBody = `{"uuid":"%s","id":"%s","priority":1,"scenarioName":"Scenario","requiredScenarioState":"Started","newScenarioState":"Stopped",` +
`"request":{"basicAuthCredentials":{"password":"password","username":"username"},"bodyPatterns":[{"equalToJson":"{\"meta\": \"information\"}"},{"contains":"information"}],` +
`"cookies":{"session":{"equalToXml":"\u003cxml\u003e"}},` +
`"headers":{"x-session":{"matches":"^\\S+@\\S+$"}},` +
`"method":"POST","queryParameters":{"firstName":{"equalTo":"Jhon"},"lastName":{"doesNotMatch":"Black"}},"urlPath":"/example"},` +
`"response":{"body":"{\"code\": 400, \"detail\": \"detail\"}","headers":{"Content-Type":"application/json"},"status":400,"fixedDelayMilliseconds":5000}}`
result, err = json.Marshal(postStubRule)

rawExpectedRequestBody, err := ioutil.ReadFile("expected-template.json")
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)
}
if string(result) != fmt.Sprintf(expectedRequestBody, postStubRule.uuid, postStubRule.uuid) {
t.Errorf("expected requestBody %q; got %q", expectedRequestBody, string(result))
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 {
t.Fatalf("StubRole json.Unmarshal error: %v", err)
}

if !reflect.DeepEqual(parsedResult, expected) {
t.Errorf("expected requestBody\n%v\n%v", parsedResult, expected)
}
}
68 changes: 68 additions & 0 deletions expected-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"uuid": "%s",
"id": "%s",
"priority": 1,
"scenarioName": "Scenario",
"requiredScenarioState": "Started",
"newScenarioState": "Stopped",
"request": {
"method": "POST",
"basicAuthCredentials": {
"password": "password",
"username": "username"
},
"bodyPatterns": [
{
"equalToJson": "{\"meta\": \"information\"}"
},
{
"contains": "information"
}
],
"multipartPatterns": [
{
"matchingType": "ANY",
"headers": {
"Content-Disposition": {
"contains": "name=\"info\""
},
"Content-Type": {
"contains": "charset"
}
},
"bodyPatterns": [
{
"equalToJson": "{}"
}
]
}
],
"cookies": {
"session": {
"equalToXml": "\u003cxml\u003e"
}
},
"headers": {
"x-session": {
"matches": "^\\S+@\\S+$"
}
},
"queryParameters": {
"firstName": {
"equalTo": "Jhon"
},
"lastName": {
"doesNotMatch": "Black"
}
},
"urlPath": "/example"
},
"response": {
"body": "{\"code\": 400, \"detail\": \"detail\"}",
"headers": {
"Content-Type": "application/json"
},
"status": 400,
"fixedDelayMilliseconds": 5000
}
}
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@ module github.com/walkerus/go-wiremock

go 1.14

require (
github.com/google/uuid v1.2.0
)
require github.com/google/uuid v1.2.0
92 changes: 92 additions & 0 deletions multipart_pattern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package wiremock

import (
"encoding/json"
"fmt"
)

const (
MultipartMatchingTypeAny = "ANY"
MultipartMatchingTypeAll = "ALL"
)

type MultipartMatchingType string

type MultipartPattern struct {
matchingType MultipartMatchingType
headers map[string]ParamMatcherInterface
bodyPatterns []ParamMatcher
}

func NewMultipartPattern() *MultipartPattern {
return &MultipartPattern{
matchingType: MultipartMatchingTypeAny,
}
}

func (m *MultipartPattern) WithName(name string) *MultipartPattern {
if m.headers == nil {
m.headers = map[string]ParamMatcherInterface{}
}

m.headers["Content-Disposition"] = Contains(fmt.Sprintf(`name="%s"`, name))
return m
}

func (m *MultipartPattern) WithMatchingType(matchingType MultipartMatchingType) *MultipartPattern {
m.matchingType = matchingType
return m
}

func (m *MultipartPattern) WithAllMatchingType() *MultipartPattern {
m.matchingType = MultipartMatchingTypeAll
return m
}

func (m *MultipartPattern) WithAnyMatchingType() *MultipartPattern {
m.matchingType = MultipartMatchingTypeAny
return m
}

func (m *MultipartPattern) WithBodyPattern(matcher ParamMatcher) *MultipartPattern {
m.bodyPatterns = append(m.bodyPatterns, matcher)
return m
}

func (m *MultipartPattern) WithHeader(header string, matcher ParamMatcherInterface) *MultipartPattern {
if m.headers == nil {
m.headers = map[string]ParamMatcherInterface{}
}

m.headers[header] = matcher
return m
}

// MarshalJSON gives valid JSON or error.
func (m *MultipartPattern) MarshalJSON() ([]byte, error) {
multipart := map[string]interface{}{
"matchingType": m.matchingType,
}

if len(m.bodyPatterns) > 0 {
bodyPatterns := make([]map[ParamMatchingStrategy]string, len(m.bodyPatterns))
for i, bodyPattern := range m.bodyPatterns {
bodyPatterns[i] = map[ParamMatchingStrategy]string{
bodyPattern.Strategy(): bodyPattern.Value(),
}
}
multipart["bodyPatterns"] = bodyPatterns
}

if len(m.headers) > 0 {
headers := make(map[string]map[ParamMatchingStrategy]string, len(m.headers))
for key, header := range m.headers {
headers[key] = map[ParamMatchingStrategy]string{
header.Strategy(): header.Value(),
}
}
multipart["headers"] = headers
}

return json.Marshal(multipart)
}
12 changes: 11 additions & 1 deletion request.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Request struct {
queryParams map[string]ParamMatcherInterface
cookies map[string]ParamMatcherInterface
bodyPatterns []ParamMatcher
multipartPatterns []*MultipartPattern
basicAuthCredentials *struct {
username string
password string
Expand Down Expand Up @@ -44,6 +45,12 @@ func (r *Request) WithBodyPattern(matcher ParamMatcher) *Request {
return r
}

// WithMultipartPattern adds multipart pattern to list
func (r *Request) WithMultipartPattern(pattern *MultipartPattern) *Request {
r.multipartPatterns = append(r.multipartPatterns, pattern)
return r
}

// WithBasicAuth adds basic auth credentials to Request
func (r *Request) WithBasicAuth(username, password string) *Request {
r.basicAuthCredentials = &struct {
Expand Down Expand Up @@ -101,8 +108,11 @@ func (r *Request) MarshalJSON() ([]byte, error) {
}
request["bodyPatterns"] = bodyPatterns
}
if len(r.multipartPatterns) > 0 {
request["multipartPatterns"] = r.multipartPatterns
}
if len(r.headers) > 0 {
headers := make(map[string]map[ParamMatchingStrategy]string, len(r.bodyPatterns))
headers := make(map[string]map[ParamMatchingStrategy]string, len(r.headers))
for key, header := range r.headers {
headers[key] = map[ParamMatchingStrategy]string{
header.Strategy(): header.Value(),
Expand Down
6 changes: 6 additions & 0 deletions stub_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ func (s *StubRule) WithBodyPattern(matcher ParamMatcher) *StubRule {
return s
}

// WithMultipartPattern adds multipart body pattern and returns *StubRule
func (s *StubRule) WithMultipartPattern(pattern *MultipartPattern) *StubRule {
s.request.WithMultipartPattern(pattern)
return s
}

// WillReturn sets response and returns *StubRule
func (s *StubRule) WillReturn(body string, headers map[string]string, status int64) *StubRule {
s.response.body = body
Expand Down

0 comments on commit a9890bf

Please sign in to comment.