Skip to content

Commit

Permalink
Merge branch 'master' into fix-trailing-newline
Browse files Browse the repository at this point in the history
  • Loading branch information
jekiapp authored Aug 13, 2024
2 parents 84e9e4e + 134bf82 commit 959dff6
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 13 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
4 changes: 2 additions & 2 deletions gripmock.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import (
func main() {
outputPointer := flag.String("o", "", "directory to output server.go. Default is $GOPATH/src/grpc/")
grpcPort := flag.String("grpc-port", "4770", "Port of gRPC tcp server")
grpcBindAddr := flag.String("grpc-listen", "", "Adress the gRPC server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine")
grpcBindAddr := flag.String("grpc-listen", "", "Address the gRPC server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine")
adminport := flag.String("admin-port", "4771", "Port of stub admin server")
adminBindAddr := flag.String("admin-listen", "", "Adress the admin server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine")
adminBindAddr := flag.String("admin-listen", "", "Address the admin server will bind to. Default to localhost, set to 0.0.0.0 to use from another machine")
stubPath := flag.String("stub", "", "Path where the stub files are (Optional)")
imports := flag.String("imports", "/protobuf", "comma separated imports path. default path /protobuf is where gripmock Dockerfile install WKT protos")
// for backwards compatibility
Expand Down
47 changes: 41 additions & 6 deletions stub/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"reflect"
"regexp"
"sort"
"sync"

"github.com/lithammer/fuzzysearch/fuzzy"
Expand Down Expand Up @@ -82,6 +83,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 @@ -187,18 +195,41 @@ 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 find(expect, actual interface{}, acc, exactMatch bool, f matchFunc) bool {
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, ignoreOrder bool) bool {

// circuit brake
if !acc {
return false
Expand All @@ -225,9 +256,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 +291,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 @@ -59,9 +59,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 @@ -126,6 +127,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 959dff6

Please sign in to comment.