Skip to content

Commit

Permalink
Recursive body matching (#160)
Browse files Browse the repository at this point in the history
* adding some additional error info

* go fmt

* adding recursive body matching for nested objects and arrays

* Revert "go fmt"

This reverts commit 7e51167.

* revert internal/console/dispatcher.go

---------

Co-authored-by: Jonathan C. Dietrich <[email protected]>
Co-authored-by: Jordi Martin <[email protected]>
  • Loading branch information
3 people authored Apr 8, 2024
1 parent 6cd3978 commit 826f51f
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 6 deletions.
72 changes: 66 additions & 6 deletions pkg/match/payload/json_comparator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ func isArray(s string) bool {
return len(st) > 0 && st[0] == '['
}

func (jc *JSONComparator) doCompareJSONRegexUnmarshaled(patterns, values map[string]interface{}) bool {
var matches bool
matches = jc.match(patterns, values)
if !matches{
log.Printf("values: %v don't match: %v", values, patterns)
}
return matches
}

func (jc *JSONComparator) doCompareJSONRegex(jsonWithPatterns, jsonWithValues string) bool {
var patterns map[string]interface{}
var values map[string]interface{}
Expand All @@ -32,7 +41,7 @@ func (jc *JSONComparator) doCompareJSONRegex(jsonWithPatterns, jsonWithValues st
log.Errorf("error in json values: %v", err)
return false
}
return match(patterns, values)
return jc.doCompareJSONRegexUnmarshaled(patterns, values)
}

func (jc *JSONComparator) doCompareArrayRegex(jsonWithPatterns, jsonWithValues string) bool {
Expand All @@ -48,16 +57,24 @@ func (jc *JSONComparator) doCompareArrayRegex(jsonWithPatterns, jsonWithValues s
log.Errorf("error in json patterns: %v", err)
return false
}
return jc.doCompareArrayRegexUnmarshaled(patterns, values)
}

func (jc *JSONComparator) doCompareArrayRegexUnmarshaled(patterns, values []map[string]interface{}) bool {

for i := 0; i < len(patterns); i++ {
if !match(patterns[i], values[i]) {
if !jc.match(patterns[i], values[i]) {
log.Printf("value %v doesn't match %v",
values[i], patterns[i])
return false
}
}
return true
}

func match(p, v map[string]interface{}) bool {
func (jc *JSONComparator) match(p, v map[string]interface{}) bool {
for field, pattern := range p {

value, exists := v[field]
log.Debugf("comparing field %v with pattern %v against value %v",
field, pattern, value)
Expand All @@ -69,12 +86,56 @@ func match(p, v map[string]interface{}) bool {
}
str, ok := pattern.(string)
if !ok {
return reflect.DeepEqual(pattern, value)
var valueType reflect.Kind
var patternType reflect.Kind
var result bool

valueType = reflect.ValueOf(value).Kind()
patternType = reflect.ValueOf(pattern).Kind()

if valueType == reflect.Map && patternType == reflect.Map {
log.Printf("recursing into map %v", field)

result = jc.doCompareJSONRegexUnmarshaled(
pattern.(map[string]interface{}),
value.(map[string]interface{}))

if !result {
return false
}
} else if (valueType == reflect.Array || valueType == reflect.Slice) &&
(patternType == reflect.Array || patternType == reflect.Slice) {

log.Printf("recursing into array %v", field)
valueJsonBytes, err1 := json.Marshal(value)
patternJsonBytes, err2 := json.Marshal(pattern)

if err1 != nil || err2 != nil {
log.Printf("value %v raised %v and pattern %v raised %v",
value, err1, pattern, err2)
return false
}

result = jc.doCompareArrayRegex(
string(patternJsonBytes), string(valueJsonBytes))

if !result {
return false
}
} else {
var eql bool
eql = reflect.DeepEqual(pattern, value)

if !eql {
log.Printf("value %v doesn't DeepEqual %v", value, pattern)
}

return eql
}
}
matched, err := regexp.MatchString(str, fmt.Sprint(value))
if err != nil || !matched {
log.Debugf("value %v doesn't match %v : %v", fmt.Sprint(value), str, err)

return false
}
}
Expand All @@ -85,7 +146,6 @@ func (jc *JSONComparator) Compare(s1, s2 string) bool {

if isArray(s1) != isArray(s2) {
log.Debugf("only one of these is an array %v %v", s1, s2)

return false
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/match/payload/json_comparator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ func TestJSONComparator_Compare(t *testing.T) {
{"Test different order", args{"{\"name\":\"bob\",\"age\":30}", "{\"age\":30,\"name\":\"bob\"}"}, true},
{"Test equal arrays", args{"[{\"name\":\"bob\",\"age\":30}]", "[{\"age\":30,\"name\":\"bob\"}]"}, true},
{"Test object and array difference", args{"{\"name\":\"bob\",\"age\":30}", "[{\"age\":30,\"name\":\"bob\"}]"}, false},
{"Test array with object with regex", args{"[{\"name\":\".*\",\"age\":\"\\\\d*\"}]", "[{\"age\":30,\"name\":\"bob\"}]"}, true},
{"Test array of objects with regex", args{"[{\"name\":\".*\",\"age\":\"\\\\d*\"}]", "[{\"age\":3,\"name\":\"gary\"},{\"age\":30,\"name\":\"bob\"}]"}, true},
{"Test nested objects with regex", args{"{\"name\": {\"firstName\":\".*\"},\"age\":\"\\\\d*\"}", "{\"age\":30,\"name\": {\"firstName\": \"bob\"}}"}, true},
{"Test array of nested objects with regex", args{"[{\"name\": {\"firstName\":\".*\"},\"age\":\"\\\\d*\"}]", "[{\"age\":3,\"name\": {\"firstName\": \"gary\"}},{\"age\":30,\"name\": {\"firstName\": \"bob\"}}]"}, true},
{"Test different arrays", args{"[{\"name\":\"john\",\"age\":30}]", "[{\"age\":30,\"name\":\"bob\"}]"}, false},
{"Test different format", args{"{\"name\":\"bob\",\"age\":30}", "{\"name\" : \"bob\"\n,\"age\" : 30}"}, true},
{"Test different values", args{"{\"name\":\"bobs\",\"age\":30}", "{\"name\":\"bob\",\"age\":30}"}, false},
Expand Down

0 comments on commit 826f51f

Please sign in to comment.