diff --git a/go.mod b/go.mod index 7a531bd..fedc8fb 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,14 @@ module github.com/ikawaha/httpcheck go 1.19 -require github.com/stretchr/testify v1.8.4 +require ( + github.com/itchyny/gojq v0.12.14 + github.com/stretchr/testify v1.8.4 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/itchyny/timefmt-go v0.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fa4b6e6..996b5e2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/itchyny/gojq v0.12.14 h1:6k8vVtsrhQSYgSGg827AD+PVVaB1NLXEdX+dda2oZCc= +github.com/itchyny/gojq v0.12.14/go.mod h1:y1G7oO7XkcR1LPZO59KyoCRy08T3j9vDYRV0GgYSS+s= +github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= +github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/tester_body.go b/tester_body.go index 98fb0d8..af3190f 100644 --- a/tester_body.go +++ b/tester_body.go @@ -2,10 +2,12 @@ package httpcheck import ( "bytes" + "encoding/json" "fmt" "io" "strings" + "github.com/itchyny/gojq" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -191,3 +193,61 @@ func (tt *Tester) MustNotContainsString(substr string) *Tester { require.NotContains(tt.t, string(body), substr) return tt } + +func (tt *Tester) MatchesJSONQuery(q string) *Tester { + body, err := io.ReadAll(tt.response.Body) + require.NoError(tt.t, err) + tt.response.Body.Close() + defer func(body []byte) { + tt.response.Body = io.NopCloser(bytes.NewReader(body)) + }(body) + var in any + require.NoError(tt.t, json.Unmarshal(body, &in), "failed to unmarshal json: %s", string(body)) + jq, err := gojq.Parse(q) + require.NoError(tt.t, err, "failed to parse query %q: %s", q) + it := jq.Run(in) + var detect bool + for { + v, ok := it.Next() + if !ok { + break + } + if err, ok := v.(error); ok { + require.NoError(tt.t, err, "query %q does not match: %s", q, string(body)) + } + if v != nil { + detect = true + } + } + assert.True(tt.t, detect, "query %q does not match: %s", q, string(body)) + return tt +} + +func (tt *Tester) NotMatchesJSONQuery(q string) *Tester { + body, err := io.ReadAll(tt.response.Body) + require.NoError(tt.t, err) + tt.response.Body.Close() + defer func(body []byte) { + tt.response.Body = io.NopCloser(bytes.NewReader(body)) + }(body) + var in any + require.NoError(tt.t, json.Unmarshal(body, &in)) + jq, err := gojq.Parse(q) + require.NoError(tt.t, err, "failed to parse query %q: %s", q) + it := jq.Run(in) + var detect bool + for { + v, ok := it.Next() + if !ok { + break + } + if err, ok := v.(error); ok { + require.NoError(tt.t, err, "query %q does not match: %s", q, string(body)) + } + if v != nil { + detect = true + } + } + assert.False(tt.t, detect, "query %q does not match: %s", q, string(body)) + return tt +} diff --git a/tester_body_test.go b/tester_body_test.go index d9e0e9c..681d7bd 100644 --- a/tester_body_test.go +++ b/tester_body_test.go @@ -2,6 +2,7 @@ package httpcheck import ( "net/http" + "net/http/httptest" "testing" "github.com/stretchr/testify/assert" @@ -152,3 +153,33 @@ func TestTester_MustNotContainsString(t *testing.T) { t.Fatal("it is expected that this assertion will not be executed.") }) } + +func TestTester_MatchesJSONQuery(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"Name": "Some", "Age": 30}`)) + }) + ts := httptest.NewServer(mux) + defer ts.Close() + + checker := newTestChecker() + checker.Test(t, "GET", "/json"). + Check(). + MatchesJSONQuery(`.Name`) +} + +func TestTester_NotMatchesJSONQuery(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"Name": "Some", "Age": 30}`)) + }) + ts := httptest.NewServer(mux) + defer ts.Close() + + checker := newTestChecker() + checker.Test(t, "GET", "/json"). + Check(). + NotMatchesJSONQuery(`.XXX`) +}