github.com/openshift-online/ocm-sdk-go@v0.1.473/testing/jq.go (about) 1 /* 2 Copyright (c) 2021 Red Hat, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package testing 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "fmt" 23 "io" 24 "net/http" 25 "reflect" 26 "strings" 27 28 "github.com/itchyny/gojq" 29 "github.com/onsi/gomega/types" 30 31 . "github.com/onsi/gomega" //nolint 32 ) 33 34 // JQ runs the given `jq` filter on the given object and returns the list of results. The returned 35 // slice will never be nil; if there are no results it will be empty. 36 func JQ(filter string, input interface{}) (results []interface{}, err error) { 37 query, err := gojq.Parse(filter) 38 if err != nil { 39 return 40 } 41 iterator := query.Run(input) 42 for { 43 result, ok := iterator.Next() 44 if !ok { 45 break 46 } 47 results = append(results, result) 48 } 49 return 50 } 51 52 // MatchJQ creates a matcher that checks that the all the results of applying a `jq` filter to the 53 // actual value is the given expected value. 54 func MatchJQ(filter string, expected interface{}) types.GomegaMatcher { 55 return &jqMatcher{ 56 filter: filter, 57 expected: expected, 58 } 59 } 60 61 type jqMatcher struct { 62 filter string 63 expected interface{} 64 results []interface{} 65 } 66 67 func (m *jqMatcher) Match(actual interface{}) (success bool, err error) { 68 // Run the query: 69 m.results, err = JQ(m.filter, actual) 70 if err != nil { 71 return 72 } 73 74 // Check that there is at least one result: 75 if len(m.results) == 0 { 76 return 77 } 78 79 // We consider the match sucessful if all the results returned by the JQ filter are exactly 80 // equal to the expected value. 81 success = true 82 for _, result := range m.results { 83 if !reflect.DeepEqual(result, m.expected) { 84 success = false 85 break 86 } 87 } 88 return 89 } 90 91 func (m *jqMatcher) FailureMessage(actual interface{}) string { 92 return fmt.Sprintf( 93 "Expected all results of running JQ filter\n\t%s\n"+ 94 "on input\n\t%s\n"+ 95 "to be\n\t%s\n"+ 96 "but at list one of the following results isn't\n\t%s\n", 97 m.filter, m.pretty(actual), m.pretty(m.expected), m.pretty(m.results), 98 ) 99 } 100 101 func (m *jqMatcher) NegatedFailureMessage(actual interface{}) (message string) { 102 return fmt.Sprintf( 103 "Expected results of running JQ filter\n\t%s\n"+ 104 "on\n\t%s\n"+ 105 "to not be\n\t%s\n", 106 m.filter, m.pretty(actual), m.pretty(m.expected), 107 ) 108 } 109 110 func (m *jqMatcher) pretty(object interface{}) string { 111 var buffer bytes.Buffer 112 encoder := json.NewEncoder(&buffer) 113 encoder.SetIndent("\t", " ") 114 err := encoder.Encode(object) 115 if err != nil { 116 return fmt.Sprintf("\t%v", object) 117 } 118 return strings.TrimRight(buffer.String(), "\n") 119 } 120 121 // VerifyJQ verifies that the result of applying the given `jq` filter to the request body matches 122 // the given expected value. 123 func VerifyJQ(filter string, expected interface{}) http.HandlerFunc { 124 return func(w http.ResponseWriter, r *http.Request) { 125 // Read the body completely: 126 body, err := io.ReadAll(r.Body) 127 Expect(err).ToNot(HaveOccurred()) 128 err = r.Body.Close() 129 Expect(err).ToNot(HaveOccurred()) 130 131 // Replace the body with a buffer so that other calls to this same method, or to 132 // other verification methods will also be able to work with it. 133 r.Body = io.NopCloser(bytes.NewBuffer(body)) 134 135 // Parse the body as JSON and verify that it matches the filter: 136 var data interface{} 137 err = json.Unmarshal(body, &data) 138 Expect(err).ToNot(HaveOccurred()) 139 Expect(data).To(MatchJQ(filter, expected)) 140 } 141 }