Skip to content

Commit

Permalink
Ensure assertions using Len() do not panic on nil in order to keep se…
Browse files Browse the repository at this point in the history
…quence diagrams reports (#50)
  • Loading branch information
t-ricci-digit authored Apr 13, 2023
1 parent 7b62d2a commit b5ff1c9
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.idea/
.vscode/
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(The MIT License)

Copyright (c) 2022 Stein Fletcher
Copyright (c) 2023 Stein Fletcher

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
1 change: 0 additions & 1 deletion http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,3 @@ func CopyRequest(request *http.Request) *http.Request {

return resCopy
}

14 changes: 7 additions & 7 deletions jsonpath.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package jsonpath

import (
"errors"
"fmt"
httputil "github.com/steinfletcher/apitest-jsonpath/http"
"github.com/steinfletcher/apitest-jsonpath/jsonpath"
"net/http"
"reflect"
regex "regexp"

httputil "github.com/steinfletcher/apitest-jsonpath/http"
"github.com/steinfletcher/apitest-jsonpath/jsonpath"
)

// Contains is a convenience function to assert that a jsonpath expression extracts a value in an array
Expand Down Expand Up @@ -71,11 +71,11 @@ func Matches(expression string, regexp string) func(*http.Response, *http.Reques
return func(res *http.Response, req *http.Request) error {
pattern, err := regex.Compile(regexp)
if err != nil {
return errors.New(fmt.Sprintf("invalid pattern: '%s'", regexp))
return fmt.Errorf("invalid pattern: '%s'", regexp)
}
value, _ := jsonpath.JsonPath(res.Body, expression)
if value == nil {
return errors.New(fmt.Sprintf("no match for pattern: '%s'", expression))
return fmt.Errorf("no match for pattern: '%s'", expression)
}
kind := reflect.ValueOf(value).Kind()
switch kind {
Expand All @@ -95,11 +95,11 @@ func Matches(expression string, regexp string) func(*http.Response, *http.Reques
reflect.Float64,
reflect.String:
if !pattern.Match([]byte(fmt.Sprintf("%v", value))) {
return errors.New(fmt.Sprintf("value '%v' does not match pattern '%v'", value, regexp))
return fmt.Errorf("value '%v' does not match pattern '%v'", value, regexp)
}
return nil
default:
return errors.New(fmt.Sprintf("unable to match using type: %s", kind.String()))
return fmt.Errorf("unable to match using type: %s", kind.String())
}
}
}
Expand Down
35 changes: 24 additions & 11 deletions jsonpath/jsonpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/PaesslerAG/jsonpath"
"io"
"io/ioutil"
"reflect"
"strings"

"github.com/PaesslerAG/jsonpath"
)

func Contains(expression string, expected interface{}, data io.Reader) error {
Expand All @@ -19,10 +20,10 @@ func Contains(expression string, expected interface{}, data io.Reader) error {
}
ok, found := IncludesElement(value, expected)
if !ok {
return errors.New(fmt.Sprintf("\"%s\" could not be applied builtin len()", expected))
return fmt.Errorf("\"%s\" could not be applied builtin len()", expected)
}
if !found {
return errors.New(fmt.Sprintf("\"%s\" does not contain \"%s\"", value, expected))
return fmt.Errorf("\"%s\" does not contain \"%s\"", value, expected)
}
return nil
}
Expand All @@ -33,7 +34,7 @@ func Equal(expression string, expected interface{}, data io.Reader) error {
return err
}
if !ObjectsAreEqual(value, expected) {
return errors.New(fmt.Sprintf("\"%s\" not equal to \"%s\"", value, expected))
return fmt.Errorf("\"%s\" not equal to \"%s\"", value, expected)
}
return nil
}
Expand All @@ -45,7 +46,7 @@ func NotEqual(expression string, expected interface{}, data io.Reader) error {
}

if ObjectsAreEqual(value, expected) {
return errors.New(fmt.Sprintf("\"%s\" value is equal to \"%s\"", expression, expected))
return fmt.Errorf("\"%s\" value is equal to \"%s\"", expression, expected)
}
return nil
}
Expand All @@ -56,9 +57,13 @@ func Length(expression string, expectedLength int, data io.Reader) error {
return err
}

if value == nil {
return errors.New("value is null")
}

v := reflect.ValueOf(value)
if v.Len() != expectedLength {
return errors.New(fmt.Sprintf("\"%d\" not equal to \"%d\"", v.Len(), expectedLength))
return fmt.Errorf("\"%d\" not equal to \"%d\"", v.Len(), expectedLength)
}
return nil
}
Expand All @@ -69,9 +74,13 @@ func GreaterThan(expression string, minimumLength int, data io.Reader) error {
return err
}

if value == nil {
return fmt.Errorf("value is null")
}

v := reflect.ValueOf(value)
if v.Len() < minimumLength {
return errors.New(fmt.Sprintf("\"%d\" is greater than \"%d\"", v.Len(), minimumLength))
return fmt.Errorf("\"%d\" is greater than \"%d\"", v.Len(), minimumLength)
}
return nil
}
Expand All @@ -82,25 +91,29 @@ func LessThan(expression string, maximumLength int, data io.Reader) error {
return err
}

if value == nil {
return fmt.Errorf("value is null")
}

v := reflect.ValueOf(value)
if v.Len() > maximumLength {
return errors.New(fmt.Sprintf("\"%d\" is less than \"%d\"", v.Len(), maximumLength))
return fmt.Errorf("\"%d\" is less than \"%d\"", v.Len(), maximumLength)
}
return nil
}

func Present(expression string, data io.Reader) error {
value, _ := JsonPath(data, expression)
if isEmpty(value) {
return errors.New(fmt.Sprintf("value not present for expression: '%s'", expression))
return fmt.Errorf("value not present for expression: '%s'", expression)
}
return nil
}

func NotPresent(expression string, data io.Reader) error {
value, _ := JsonPath(data, expression)
if !isEmpty(value) {
return errors.New(fmt.Sprintf("value present for expression: '%s'", expression))
return fmt.Errorf("value present for expression: '%s'", expression)
}
return nil
}
Expand Down Expand Up @@ -197,4 +210,4 @@ func isEmpty(object interface{}) bool {
zero := reflect.Zero(objValue.Type())
return reflect.DeepEqual(object, zero.Interface())
}
}
}
50 changes: 42 additions & 8 deletions jsonpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package jsonpath_test

import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
Expand All @@ -18,7 +19,7 @@ func TestApiTest_Contains(t *testing.T) {
handler.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(`{"a": 12345, "b": [{"key": "c", "value": "result"}]}`))
_, err := w.Write([]byte(`{"a": 12345, "b": [{"key": "c", "value": "result"}], "d": null}`))
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -151,7 +152,7 @@ func TestApiTest_Len(t *testing.T) {
handler.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(`{"a": [1, 2, 3], "b": "c"}`))
_, err := w.Write([]byte(`{"a": [1, 2, 3], "b": "c", "d": null}`))
if err != nil {
panic(err)
}
Expand All @@ -163,6 +164,17 @@ func TestApiTest_Len(t *testing.T) {
Expect(t).
Assert(jsonpath.Len(`$.a`, 3)).
Assert(jsonpath.Len(`$.b`, 1)).
Assert(func(r1 *http.Response, r2 *http.Request) error {
err := jsonpath.Len(`$.d`, 0)(r1, r2)

if err == nil {
return errors.New("jsonpath.Len was expected to fail on null value but it didn't")
}

assert.EqualError(t, err, "value is null")

return nil
}).
End()
}

Expand All @@ -171,7 +183,7 @@ func TestApiTest_GreaterThan(t *testing.T) {
handler.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(`{"a": [1, 2, 3], "b": "c"}`))
_, err := w.Write([]byte(`{"a": [1, 2, 3], "b": "c", "d": null}`))
if err != nil {
panic(err)
}
Expand All @@ -183,6 +195,17 @@ func TestApiTest_GreaterThan(t *testing.T) {
Expect(t).
Assert(jsonpath.GreaterThan(`$.a`, 2)).
Assert(jsonpath.GreaterThan(`$.b`, 0)).
Assert(func(r1 *http.Response, r2 *http.Request) error {
err := jsonpath.GreaterThan(`$.d`, 5)(r1, r2)

if err == nil {
return errors.New("jsonpath.GreaterThan was expected to fail on null value but it didn't")
}

assert.EqualError(t, err, "value is null")

return nil
}).
End()
}

Expand All @@ -191,7 +214,7 @@ func TestApiTest_LessThan(t *testing.T) {
handler.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(`{"a": [1, 2, 3], "b": "c"}`))
_, err := w.Write([]byte(`{"a": [1, 2, 3], "b": "c", "d": null}`))
if err != nil {
panic(err)
}
Expand All @@ -203,6 +226,17 @@ func TestApiTest_LessThan(t *testing.T) {
Expect(t).
Assert(jsonpath.LessThan(`$.a`, 4)).
Assert(jsonpath.LessThan(`$.b`, 2)).
Assert(func(r1 *http.Response, r2 *http.Request) error {
err := jsonpath.LessThan(`$.d`, 5)(r1, r2)

if err == nil {
return errors.New("jsonpath.LessThan was expected to fail on null value but it didn't")
}

assert.EqualError(t, err, "value is null")

return nil
}).
End()
}

Expand Down Expand Up @@ -263,15 +297,15 @@ func TestApiTest_Chain(t *testing.T) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(`{
"a": {
"a": {
"b": {
"c": {
"c": {
"d": 1,
"e": "2",
"f": [3, 4, 5]
}
}
}
}
}
}`))
if err != nil {
panic(err)
Expand Down
9 changes: 5 additions & 4 deletions jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/steinfletcher/apitest-jsonpath/jsonpath"
"net/http"
"strings"

"github.com/steinfletcher/apitest-jsonpath/jsonpath"
)

const (
Expand All @@ -32,13 +33,13 @@ func jwtEqual(tokenSelector func(*http.Response) (string, error), expression str

parts := strings.Split(token, ".")
if len(parts) != 3 {
splitErr := errors.New("Invalid token: token should contain header, payload and secret")
splitErr := errors.New("invalid token: token should contain header, payload and secret")
return splitErr
}

decodedPayload, PayloadErr := base64Decode(parts[index])
if PayloadErr != nil {
return fmt.Errorf("Invalid jwt: %s", PayloadErr.Error())
return fmt.Errorf("invalid jwt: %s", PayloadErr.Error())
}

value, err := jsonpath.JsonPath(bytes.NewReader(decodedPayload), expression)
Expand All @@ -61,7 +62,7 @@ func base64Decode(src string) ([]byte, error) {

decoded, err := base64.URLEncoding.DecodeString(src)
if err != nil {
errMsg := fmt.Errorf("Decoding Error %s", err)
errMsg := fmt.Errorf("decoding Error %s", err)
return nil, errMsg
}
return decoded, nil
Expand Down
5 changes: 3 additions & 2 deletions mocks/mocks.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package mocks

import (
"net/http"

"github.com/steinfletcher/apitest"
httputil "github.com/steinfletcher/apitest-jsonpath/http"
"github.com/steinfletcher/apitest-jsonpath/jsonpath"
"net/http"
)

// Contains is a convenience function to assert that a jsonpath expression extracts a value in an array
Expand Down Expand Up @@ -40,4 +41,4 @@ func GreaterThan(expression string, minimumLength int) apitest.Matcher {
return func(req *http.Request, mockReq *apitest.MockRequest) error {
return jsonpath.GreaterThan(expression, minimumLength, httputil.CopyRequest(req).Body)
}
}
}
3 changes: 2 additions & 1 deletion mocks/mocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package mocks_test
import (
"encoding/json"
"fmt"
"github.com/steinfletcher/apitest-jsonpath/mocks"
"io/ioutil"
"net/http"
"strings"
"testing"

"github.com/steinfletcher/apitest-jsonpath/mocks"

"github.com/steinfletcher/apitest"
)

Expand Down

0 comments on commit b5ff1c9

Please sign in to comment.