Codebase list golang-gomega / 4ac488a
Merge pull request #205 from onsi/revert-201-json_formatting Revert "Report first instance of mismatch for JSON matcher" Onsi Fakhouri authored 7 years ago GitHub committed 7 years ago
2 changed file(s) with 5 addition(s) and 90 deletion(s). Raw diff Collapse all Expand all
44 "encoding/json"
55 "fmt"
66 "reflect"
7 "strings"
87
98 "github.com/onsi/gomega/format"
109 )
1110
1211 type MatchJSONMatcher struct {
13 JSONToMatch interface{}
14 firstFailurePath []interface{}
12 JSONToMatch interface{}
1513 }
1614
1715 func (matcher *MatchJSONMatcher) Match(actual interface{}) (success bool, err error) {
2624 // this is guarded by prettyPrint
2725 json.Unmarshal([]byte(actualString), &aval)
2826 json.Unmarshal([]byte(expectedString), &eval)
29 var equal bool
30 equal, matcher.firstFailurePath = deepEqual(aval, eval)
31 return equal, nil
27
28 return reflect.DeepEqual(aval, eval), nil
3229 }
3330
3431 func (matcher *MatchJSONMatcher) FailureMessage(actual interface{}) (message string) {
3532 actualString, expectedString, _ := matcher.prettyPrint(actual)
36 return formattedMessage(format.Message(actualString, "to match JSON of", expectedString), matcher.firstFailurePath)
33 return format.Message(actualString, "to match JSON of", expectedString)
3734 }
3835
3936 func (matcher *MatchJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) {
4037 actualString, expectedString, _ := matcher.prettyPrint(actual)
41 return formattedMessage(format.Message(actualString, "not to match JSON of", expectedString), matcher.firstFailurePath)
42 }
43
44 func formattedMessage(comparisonMessage string, failurePath []interface{}) string {
45 var diffMessage string
46 if len(failurePath) == 0 {
47 diffMessage = ""
48 } else {
49 diffMessage = fmt.Sprintf("\n\nfirst mismatched key: %s", formattedFailurePath(failurePath))
50 }
51 return fmt.Sprintf("%s%s", comparisonMessage, diffMessage)
52 }
53
54 func formattedFailurePath(failurePath []interface{}) string {
55 formattedPaths := []string{}
56 for i := len(failurePath) - 1; i >= 0; i-- {
57 switch p := failurePath[i].(type) {
58 case int:
59 formattedPaths = append(formattedPaths, fmt.Sprintf(`[%d]`, p))
60 default:
61 if i != len(failurePath)-1 {
62 formattedPaths = append(formattedPaths, ".")
63 }
64 formattedPaths = append(formattedPaths, fmt.Sprintf(`"%s"`, p))
65 }
66 }
67 return strings.Join(formattedPaths, "")
38 return format.Message(actualString, "not to match JSON of", expectedString)
6839 }
6940
7041 func (matcher *MatchJSONMatcher) prettyPrint(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
9061
9162 return abuf.String(), ebuf.String(), nil
9263 }
93
94 func deepEqual(a interface{}, b interface{}) (bool, []interface{}) {
95 var errorPath []interface{}
96 if reflect.TypeOf(a) != reflect.TypeOf(b) {
97 return false, errorPath
98 }
99
100 switch a.(type) {
101 case []interface{}:
102 if len(a.([]interface{})) != len(b.([]interface{})) {
103 return false, errorPath
104 }
105
106 for i, v := range a.([]interface{}) {
107 elementEqual, keyPath := deepEqual(v, b.([]interface{})[i])
108 if !elementEqual {
109 return false, append(keyPath, i)
110 }
111 }
112 return true, errorPath
113
114 case map[string]interface{}:
115 if len(a.(map[string]interface{})) != len(b.(map[string]interface{})) {
116 return false, errorPath
117 }
118
119 for k, v := range a.(map[string]interface{}) {
120 elementEqual, keyPath := deepEqual(v, b.(map[string]interface{})[k])
121 if !elementEqual {
122 return false, append(keyPath, k)
123 }
124 }
125 return true, errorPath
126
127 default:
128 return a == b, errorPath
129 }
130 }
2121 Ω([]byte("{}")).Should(MatchJSON([]byte("{}")))
2222 Ω("{}").Should(MatchJSON([]byte("{}")))
2323 Ω([]byte("{}")).Should(MatchJSON("{}"))
24 })
25 })
26
27 Context("when a key mismatch is found", func() {
28 It("reports the first found mismatch", func() {
29 subject := MatchJSONMatcher{JSONToMatch: `5`}
30 actual := `7`
31 subject.Match(actual)
32
33 failureMessage := subject.FailureMessage(`7`)
34 Ω(failureMessage).ToNot(ContainSubstring("first mismatched key"))
35
36 subject = MatchJSONMatcher{JSONToMatch: `{"a": 1, "b.g": {"c": 2, "1": ["hello", "see ya"]}}`}
37 actual = `{"a": 1, "b.g": {"c": 2, "1": ["hello", "goodbye"]}}`
38 subject.Match(actual)
39
40 failureMessage = subject.FailureMessage(actual)
41 Ω(failureMessage).To(ContainSubstring(`first mismatched key: "b.g"."1"[1]`))
4224 })
4325 })
4426