From 8b295afb3e272daad672a3268ad1092c8f70e955 Mon Sep 17 00:00:00 2001 From: mohanprasaths Date: Sun, 4 Nov 2018 20:53:33 +0530 Subject: [PATCH] Bugfix-773 show better error message on JSON decoding errors instead of dumping entire JSON, offset, line number and column numbers are displayed --- js/modules/k6/http/response_test.go | 3 +- lib/netext/httpext/response.go | 43 +++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/js/modules/k6/http/response_test.go b/js/modules/k6/http/response_test.go index 0024310682a2..90d5cc35b564 100644 --- a/js/modules/k6/http/response_test.go +++ b/js/modules/k6/http/response_test.go @@ -178,7 +178,8 @@ func TestResponse(t *testing.T) { t.Run("Invalid", func(t *testing.T) { _, err := common.RunString(rt, sr(`http.request("GET", "HTTPBIN_URL/html").json();`)) - assert.EqualError(t, err, "GoError: invalid character '<' looking for beginning of value") + //nolint:lll + assert.EqualError(t, err, "GoError: cannot parse json due to an error at line 1, character 2 , error: invalid character '<' looking for beginning of value") }) }) t.Run("JsonSelector", func(t *testing.T) { diff --git a/lib/netext/httpext/response.go b/lib/netext/httpext/response.go index 0cb9a83a517b..25040b202522 100644 --- a/lib/netext/httpext/response.go +++ b/lib/netext/httpext/response.go @@ -24,9 +24,11 @@ import ( "context" "crypto/tls" "encoding/json" + "fmt" "net/http" "net/http/httputil" + "github.com/loadimpact/k6/js/common" "github.com/loadimpact/k6/lib" "github.com/loadimpact/k6/lib/netext" "github.com/pkg/errors" @@ -59,6 +61,17 @@ const ( ResponseTypeNone ) +type jsonError struct { + line int + character int + err error +} + +func (j jsonError) Error() string { + errMessage := "cannot parse json due to an error at line" + return fmt.Sprintf("%s %d, character %d , error: %v", errMessage, j.line, j.character, j.err) +} + // ResponseTimings is a struct to put all timings for a given HTTP response/request type ResponseTimings struct { Duration float64 `json:"duration"` @@ -141,7 +154,6 @@ func (res *Response) JSON(selector ...string) (interface{}, error) { } if hasSelector { - if !res.validatedJSON { if !gjson.ValidBytes(body) { return nil, nil @@ -158,11 +170,38 @@ func (res *Response) JSON(selector ...string) (interface{}, error) { } if err := json.Unmarshal(body, &v); err != nil { - return nil, err + switch t := err.(type) { + case *json.SyntaxError: + err = checkErrorInJSON(body, int(t.Offset), err) + default: + break + } + common.Throw(common.GetRuntime(res.GetCtx()), err) } res.validatedJSON = true res.cachedJSON = v } return res.cachedJSON, nil +} + +func checkErrorInJSON(input []byte, offset int, err error) error { + lf := '\n' + str := string(input) + + // Humans tend to count from 1. + line := 1 + character := 0 + + for i, b := range str { + if b == lf { + line++ + character = 0 + } + character++ + if i == offset { + break + } + } + return jsonError{line: line, character: character, err: err} }