Skip to content

Commit a9890bf

Browse files
walkerusrrisamukhametov
and
rrisamukhametov
authored
Adding multipart support (#8)
Co-authored-by: rrisamukhametov <rrisamukhametov@avito.ru>
1 parent ce63802 commit a9890bf

File tree

7 files changed

+205
-15
lines changed

7 files changed

+205
-15
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ jobs:
1313
- name: golangci-lint
1414
uses: golangci/golangci-lint-action@v2
1515
with:
16-
version: v1.31
16+
version: v1.47.3
1717
- name: Tests
1818
run: go test

client_test.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package wiremock
33
import (
44
"encoding/json"
55
"fmt"
6+
"io/ioutil"
7+
"reflect"
68
"testing"
79
"time"
810
)
@@ -24,6 +26,12 @@ func TestStubRule_ToJson(t *testing.T) {
2426
WithQueryParam("lastName", NotMatching("Black")).
2527
WithBodyPattern(EqualToJson(`{"meta": "information"}`)).
2628
WithBodyPattern(Contains("information")).
29+
WithMultipartPattern(
30+
NewMultipartPattern().
31+
WithName("info").
32+
WithHeader("Content-Type", Contains("charset")).
33+
WithBodyPattern(EqualToJson("{}")),
34+
).
2735
WithBasicAuth("username", "password").
2836
WithHeader("x-session", Matching("^\\S+@\\S+$")).
2937
WithCookie("session", EqualToXml("<xml>")).
@@ -38,18 +46,26 @@ func TestStubRule_ToJson(t *testing.T) {
3846
WhenScenarioStateIs("Started").
3947
WillSetStateTo("Stopped")
4048

41-
expectedRequestBody = `{"uuid":"%s","id":"%s","priority":1,"scenarioName":"Scenario","requiredScenarioState":"Started","newScenarioState":"Stopped",` +
42-
`"request":{"basicAuthCredentials":{"password":"password","username":"username"},"bodyPatterns":[{"equalToJson":"{\"meta\": \"information\"}"},{"contains":"information"}],` +
43-
`"cookies":{"session":{"equalToXml":"\u003cxml\u003e"}},` +
44-
`"headers":{"x-session":{"matches":"^\\S+@\\S+$"}},` +
45-
`"method":"POST","queryParameters":{"firstName":{"equalTo":"Jhon"},"lastName":{"doesNotMatch":"Black"}},"urlPath":"/example"},` +
46-
`"response":{"body":"{\"code\": 400, \"detail\": \"detail\"}","headers":{"Content-Type":"application/json"},"status":400,"fixedDelayMilliseconds":5000}}`
47-
result, err = json.Marshal(postStubRule)
48-
49+
rawExpectedRequestBody, err := ioutil.ReadFile("expected-template.json")
50+
if err != nil {
51+
t.Fatalf("failed to read expected-template.json %v", err)
52+
}
53+
rawResult, err := json.Marshal(postStubRule)
4954
if err != nil {
5055
t.Fatalf("StubRole json.Marshal error: %v", err)
5156
}
52-
if string(result) != fmt.Sprintf(expectedRequestBody, postStubRule.uuid, postStubRule.uuid) {
53-
t.Errorf("expected requestBody %q; got %q", expectedRequestBody, string(result))
57+
var expected map[string]interface{}
58+
err = json.Unmarshal([]byte(fmt.Sprintf(string(rawExpectedRequestBody), postStubRule.uuid, postStubRule.uuid)), &expected)
59+
if err != nil {
60+
t.Fatalf("StubRole json.Unmarshal error: %v", err)
61+
}
62+
var parsedResult map[string]interface{}
63+
err = json.Unmarshal(rawResult, &parsedResult)
64+
if err != nil {
65+
t.Fatalf("StubRole json.Unmarshal error: %v", err)
66+
}
67+
68+
if !reflect.DeepEqual(parsedResult, expected) {
69+
t.Errorf("expected requestBody\n%v\n%v", parsedResult, expected)
5470
}
5571
}

expected-template.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"uuid": "%s",
3+
"id": "%s",
4+
"priority": 1,
5+
"scenarioName": "Scenario",
6+
"requiredScenarioState": "Started",
7+
"newScenarioState": "Stopped",
8+
"request": {
9+
"method": "POST",
10+
"basicAuthCredentials": {
11+
"password": "password",
12+
"username": "username"
13+
},
14+
"bodyPatterns": [
15+
{
16+
"equalToJson": "{\"meta\": \"information\"}"
17+
},
18+
{
19+
"contains": "information"
20+
}
21+
],
22+
"multipartPatterns": [
23+
{
24+
"matchingType": "ANY",
25+
"headers": {
26+
"Content-Disposition": {
27+
"contains": "name=\"info\""
28+
},
29+
"Content-Type": {
30+
"contains": "charset"
31+
}
32+
},
33+
"bodyPatterns": [
34+
{
35+
"equalToJson": "{}"
36+
}
37+
]
38+
}
39+
],
40+
"cookies": {
41+
"session": {
42+
"equalToXml": "\u003cxml\u003e"
43+
}
44+
},
45+
"headers": {
46+
"x-session": {
47+
"matches": "^\\S+@\\S+$"
48+
}
49+
},
50+
"queryParameters": {
51+
"firstName": {
52+
"equalTo": "Jhon"
53+
},
54+
"lastName": {
55+
"doesNotMatch": "Black"
56+
}
57+
},
58+
"urlPath": "/example"
59+
},
60+
"response": {
61+
"body": "{\"code\": 400, \"detail\": \"detail\"}",
62+
"headers": {
63+
"Content-Type": "application/json"
64+
},
65+
"status": 400,
66+
"fixedDelayMilliseconds": 5000
67+
}
68+
}

go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,4 @@ module github.com/walkerus/go-wiremock
22

33
go 1.14
44

5-
require (
6-
github.com/google/uuid v1.2.0
7-
)
5+
require github.com/google/uuid v1.2.0

multipart_pattern.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package wiremock
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
)
7+
8+
const (
9+
MultipartMatchingTypeAny = "ANY"
10+
MultipartMatchingTypeAll = "ALL"
11+
)
12+
13+
type MultipartMatchingType string
14+
15+
type MultipartPattern struct {
16+
matchingType MultipartMatchingType
17+
headers map[string]ParamMatcherInterface
18+
bodyPatterns []ParamMatcher
19+
}
20+
21+
func NewMultipartPattern() *MultipartPattern {
22+
return &MultipartPattern{
23+
matchingType: MultipartMatchingTypeAny,
24+
}
25+
}
26+
27+
func (m *MultipartPattern) WithName(name string) *MultipartPattern {
28+
if m.headers == nil {
29+
m.headers = map[string]ParamMatcherInterface{}
30+
}
31+
32+
m.headers["Content-Disposition"] = Contains(fmt.Sprintf(`name="%s"`, name))
33+
return m
34+
}
35+
36+
func (m *MultipartPattern) WithMatchingType(matchingType MultipartMatchingType) *MultipartPattern {
37+
m.matchingType = matchingType
38+
return m
39+
}
40+
41+
func (m *MultipartPattern) WithAllMatchingType() *MultipartPattern {
42+
m.matchingType = MultipartMatchingTypeAll
43+
return m
44+
}
45+
46+
func (m *MultipartPattern) WithAnyMatchingType() *MultipartPattern {
47+
m.matchingType = MultipartMatchingTypeAny
48+
return m
49+
}
50+
51+
func (m *MultipartPattern) WithBodyPattern(matcher ParamMatcher) *MultipartPattern {
52+
m.bodyPatterns = append(m.bodyPatterns, matcher)
53+
return m
54+
}
55+
56+
func (m *MultipartPattern) WithHeader(header string, matcher ParamMatcherInterface) *MultipartPattern {
57+
if m.headers == nil {
58+
m.headers = map[string]ParamMatcherInterface{}
59+
}
60+
61+
m.headers[header] = matcher
62+
return m
63+
}
64+
65+
// MarshalJSON gives valid JSON or error.
66+
func (m *MultipartPattern) MarshalJSON() ([]byte, error) {
67+
multipart := map[string]interface{}{
68+
"matchingType": m.matchingType,
69+
}
70+
71+
if len(m.bodyPatterns) > 0 {
72+
bodyPatterns := make([]map[ParamMatchingStrategy]string, len(m.bodyPatterns))
73+
for i, bodyPattern := range m.bodyPatterns {
74+
bodyPatterns[i] = map[ParamMatchingStrategy]string{
75+
bodyPattern.Strategy(): bodyPattern.Value(),
76+
}
77+
}
78+
multipart["bodyPatterns"] = bodyPatterns
79+
}
80+
81+
if len(m.headers) > 0 {
82+
headers := make(map[string]map[ParamMatchingStrategy]string, len(m.headers))
83+
for key, header := range m.headers {
84+
headers[key] = map[ParamMatchingStrategy]string{
85+
header.Strategy(): header.Value(),
86+
}
87+
}
88+
multipart["headers"] = headers
89+
}
90+
91+
return json.Marshal(multipart)
92+
}

request.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type Request struct {
1212
queryParams map[string]ParamMatcherInterface
1313
cookies map[string]ParamMatcherInterface
1414
bodyPatterns []ParamMatcher
15+
multipartPatterns []*MultipartPattern
1516
basicAuthCredentials *struct {
1617
username string
1718
password string
@@ -44,6 +45,12 @@ func (r *Request) WithBodyPattern(matcher ParamMatcher) *Request {
4445
return r
4546
}
4647

48+
// WithMultipartPattern adds multipart pattern to list
49+
func (r *Request) WithMultipartPattern(pattern *MultipartPattern) *Request {
50+
r.multipartPatterns = append(r.multipartPatterns, pattern)
51+
return r
52+
}
53+
4754
// WithBasicAuth adds basic auth credentials to Request
4855
func (r *Request) WithBasicAuth(username, password string) *Request {
4956
r.basicAuthCredentials = &struct {
@@ -101,8 +108,11 @@ func (r *Request) MarshalJSON() ([]byte, error) {
101108
}
102109
request["bodyPatterns"] = bodyPatterns
103110
}
111+
if len(r.multipartPatterns) > 0 {
112+
request["multipartPatterns"] = r.multipartPatterns
113+
}
104114
if len(r.headers) > 0 {
105-
headers := make(map[string]map[ParamMatchingStrategy]string, len(r.bodyPatterns))
115+
headers := make(map[string]map[ParamMatchingStrategy]string, len(r.headers))
106116
for key, header := range r.headers {
107117
headers[key] = map[ParamMatchingStrategy]string{
108118
header.Strategy(): header.Value(),

stub_rule.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ func (s *StubRule) WithBodyPattern(matcher ParamMatcher) *StubRule {
8181
return s
8282
}
8383

84+
// WithMultipartPattern adds multipart body pattern and returns *StubRule
85+
func (s *StubRule) WithMultipartPattern(pattern *MultipartPattern) *StubRule {
86+
s.request.WithMultipartPattern(pattern)
87+
return s
88+
}
89+
8490
// WillReturn sets response and returns *StubRule
8591
func (s *StubRule) WillReturn(body string, headers map[string]string, status int64) *StubRule {
8692
s.response.body = body

0 commit comments

Comments
 (0)