Skip to content

Commit

Permalink
Merge pull request #168 from mytestopia/gripmock-sets
Browse files Browse the repository at this point in the history
New input matching rule: equals_unordered
  • Loading branch information
jekiapp authored Aug 13, 2024
2 parents 4bc0bf6 + aaa2e0e commit 134bf82
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 11 deletions.
27 changes: 26 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Stub will respond with the expected response only if the request matches any rul
So if you do a `curl -X POST -d '{"service":"Greeter","method":"SayHello","data":{"name":"gripmock"}}' localhost:4771/find` stub service will find a match from listed stubs stored there.

### Input Matching Rule
Input matching has 3 rules to match an input: **equals**,**contains** and **regex**
Input matching has 4 rules to match an input: **equals**, **equals_unordered**, **contains** and **regex**
<br>
Nested fields are allowed for input matching too for all JSON data types. (`string`, `bool`, `array`, etc.)
<br>
Expand Down Expand Up @@ -151,6 +151,31 @@ Nested fields are allowed for input matching too for all JSON data types. (`stri
}
```

**equals_unordered** will match the exact field name and value of input into expected stub, except lists (which are compared as sets). example stub JSON:


```
{
.
.
"input":{
"equals_unordered":{
"name":"gripmock",
"greetings": {
"english": "Hello World!",
"indonesian": "Halo Dunia!",
"turkish": "Merhaba Dünya!"
},
"ok": true,
"numbers": [4, 8, 15, 16, 23, 42]
"null": null
}
}
.
.
}
```

**contains** will match input that has the value declared expected fields. example stub JSON:
```
{
Expand Down
46 changes: 40 additions & 6 deletions stub/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"reflect"
"regexp"
"sort"
"sync"

"github.com/lithammer/fuzzysearch/fuzzy"
Expand Down Expand Up @@ -81,6 +82,13 @@ func findStub(stub *findStubPayload) (*Output, error) {
}
}

if expect := stubrange.Input.EqualsUnordered; expect != nil {
closestMatch = append(closestMatch, closeMatch{"equals_unordered", expect})
if equalsUnordered(stub.Data, expect) {
return &stubrange.Output, nil
}
}

if expect := stubrange.Input.Contains; expect != nil {
closestMatch = append(closestMatch, closeMatch{"contains", expect})
if contains(stubrange.Input.Contains, stub.Data) {
Expand Down Expand Up @@ -186,18 +194,40 @@ func regexMatch(expect, actual interface{}) bool {
}

func equals(expect, actual map[string]interface{}) bool {
return find(expect, actual, true, true, deepEqual)
return find(expect, actual, true, true, deepEqual, false)
}

func equalsUnordered(expect, actual map[string]interface{}) bool {
return find(expect, actual, true, true, deepEqual, true)
}

func contains(expect, actual map[string]interface{}) bool {
return find(expect, actual, true, false, deepEqual)
return find(expect, actual, true, false, deepEqual, false)
}

func matches(expect, actual map[string]interface{}) bool {
return find(expect, actual, true, false, regexMatch)
return find(expect, actual, true, false, regexMatch, false)
}

func equalsIgnoreOrder(expect, actual interface{}) bool {
expectSlice, expectOk := expect.([]interface{})
actualSlice, actualOk := actual.([]interface{})
if !expectOk || !actualOk {
return false
}
if len(expectSlice) != len(actualSlice) {
return false
}
sort.Slice(expectSlice, func(i, j int) bool {
return fmt.Sprint(expectSlice[i]) < fmt.Sprint(expectSlice[j])
})
sort.Slice(actualSlice, func(i, j int) bool {
return fmt.Sprint(actualSlice[i]) < fmt.Sprint(actualSlice[j])
})
return reflect.DeepEqual(expectSlice, actualSlice)
}

func find(expect, actual interface{}, acc, exactMatch bool, f matchFunc) bool {
func find(expect, actual interface{}, acc, exactMatch bool, f matchFunc, ignoreOrder bool) bool {

// circuit brake
if acc == false {
Expand Down Expand Up @@ -225,9 +255,13 @@ func find(expect, actual interface{}, acc, exactMatch bool, f matchFunc) bool {
}
}

if expectArrayOk && actualArrayOk && ignoreOrder {
return equalsIgnoreOrder(expectArrayValue, actualArrayValue)
}

for expectItemIndex, expectItemValue := range expectArrayValue {
actualItemValue := actualArrayValue[expectItemIndex]
acc = find(expectItemValue, actualItemValue, acc, exactMatch, f)
acc = find(expectItemValue, actualItemValue, acc, exactMatch, f, ignoreOrder)
}

return acc
Expand Down Expand Up @@ -256,7 +290,7 @@ func find(expect, actual interface{}, acc, exactMatch bool, f matchFunc) bool {

for expectItemKey, expectItemValue := range expectMapValue {
actualItemValue := actualMapValue[expectItemKey]
acc = find(expectItemValue, actualItemValue, acc, exactMatch, f)
acc = find(expectItemValue, actualItemValue, acc, exactMatch, f, ignoreOrder)
}

return acc
Expand Down
9 changes: 6 additions & 3 deletions stub/stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ type Stub struct {
}

type Input struct {
Equals map[string]interface{} `json:"equals"`
Contains map[string]interface{} `json:"contains"`
Matches map[string]interface{} `json:"matches"`
Equals map[string]interface{} `json:"equals"`
EqualsUnordered map[string]interface{} `json:"equals_unordered"`
Contains map[string]interface{} `json:"contains"`
Matches map[string]interface{} `json:"matches"`
}

type Output struct {
Expand Down Expand Up @@ -118,6 +119,8 @@ func validateStub(stub *Stub) error {
break
case stub.Input.Equals != nil:
break
case stub.Input.EqualsUnordered != nil:
break
case stub.Input.Matches != nil:
break
default:
Expand Down
54 changes: 53 additions & 1 deletion stub/stub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestStub(t *testing.T) {
return httptest.NewRequest("GET", "/", nil)
},
handler: listStub,
expect: "{\"Testing\":{\"TestMethod\":[{\"Input\":{\"equals\":{\"Hola\":\"Mundo\"},\"contains\":null,\"matches\":null},\"Output\":{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}}]}}\n",
expect: "{\"Testing\":{\"TestMethod\":[{\"Input\":{\"equals\":{\"Hola\":\"Mundo\"},\"equals_unordered\":null,\"contains\":null,\"matches\":null},\"Output\":{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}}]}}\n",
},
{
name: "find stub equals",
Expand Down Expand Up @@ -99,6 +99,58 @@ func TestStub(t *testing.T) {
handler: handleFindStub,
expect: "{\"data\":{\"Hello\":\"World\"},\"error\":\"\"}\n",
},
{
name: "add stub equals_unordered",
mock: func() *http.Request {
payload := `{
"service": "TestingUnordered",
"method":"TestMethod",
"input": {
"equals_unordered": {
"ids": [1,2]
}
},
"output":{
"data":{
"hello":"world"
}
}
}`
return httptest.NewRequest("POST", "/add", bytes.NewReader([]byte(payload)))
},
handler: addStub,
expect: `Success add stub`,
},
{
name: "find stub equals_unordered",
mock: func() *http.Request {
payload := `{
"service":"TestingUnordered",
"method":"TestMethod",
"data":{
"ids":[1,2]
}
}`
return httptest.NewRequest("GET", "/find", bytes.NewReader([]byte(payload)))
},
handler: handleFindStub,
expect: "{\"data\":{\"hello\":\"world\"},\"error\":\"\"}\n",
},
{
name: "find stub equals_unordered reversed",
mock: func() *http.Request {
payload := `{
"service":"TestingUnordered",
"method":"TestMethod",
"data":{
"ids":[2,1]
}
}`
return httptest.NewRequest("GET", "/find", bytes.NewReader([]byte(payload)))
},
handler: handleFindStub,
expect: "{\"data\":{\"hello\":\"world\"},\"error\":\"\"}\n",
},
{
name: "add stub contains",
mock: func() *http.Request {
Expand Down

0 comments on commit 134bf82

Please sign in to comment.