From f9d132c3692d856ef948676ca5f4ced348f7f474 Mon Sep 17 00:00:00 2001 From: TwinProduction Date: Sat, 5 Jun 2021 18:41:42 -0400 Subject: [PATCH] Fix #122: Partially invalid JSONPath ending with string does not return an error --- jsonpath/jsonpath.go | 3 + jsonpath/jsonpath_test.go | 312 +++++++++++++++++--------------------- 2 files changed, 143 insertions(+), 172 deletions(-) diff --git a/jsonpath/jsonpath.go b/jsonpath/jsonpath.go index dedf306f..7c1cf3a0 100644 --- a/jsonpath/jsonpath.go +++ b/jsonpath/jsonpath.go @@ -25,6 +25,9 @@ func walk(path string, object interface{}) (string, int, error) { case map[string]interface{}: return walk(strings.Replace(path, fmt.Sprintf("%s.", currentKey), "", 1), value) case string: + if len(keys) > 1 { + return "", 0, fmt.Errorf("couldn't walk through '%s', because '%s' was a string instead of an object", keys[1], currentKey) + } return value, len(value), nil case []interface{}: return fmt.Sprintf("%v", value), len(value), nil diff --git a/jsonpath/jsonpath_test.go b/jsonpath/jsonpath_test.go index ccadf78f..40634cc3 100644 --- a/jsonpath/jsonpath_test.go +++ b/jsonpath/jsonpath_test.go @@ -1,180 +1,148 @@ package jsonpath -import "testing" +import ( + "testing" +) func TestEval(t *testing.T) { - path := "simple" - data := `{"simple": "value"}` - - expectedOutput := "value" - - output, outputLength, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) + type Scenario struct { + Name string + Path string + Data string + ExpectedOutput string + ExpectedOutputLength int + ExpectedError bool } - if outputLength != len(expectedOutput) { - t.Errorf("Expected output length to be %v, but was %v", len(expectedOutput), outputLength) + scenarios := []Scenario{ + { + Name: "simple", + Path: "key", + Data: `{"key": "value"}`, + ExpectedOutput: "value", + ExpectedOutputLength: 5, + ExpectedError: false, + }, + { + Name: "simple-with-invalid-data", + Path: "key", + Data: "invalid data", + ExpectedOutput: "", + ExpectedOutputLength: 0, + ExpectedError: true, + }, + { + Name: "invalid-path", + Path: "key", + Data: `{}`, + ExpectedOutput: "", + ExpectedOutputLength: 0, + ExpectedError: true, + }, + { + Name: "long-simple-walk", + Path: "long.simple.walk", + Data: `{"long": {"simple": {"walk": "value"}}}`, + ExpectedOutput: "value", + ExpectedOutputLength: 5, + ExpectedError: false, + }, + { + Name: "array-of-maps", + Path: "ids[1].id", + Data: `{"ids": [{"id": 1}, {"id": 2}]}`, + ExpectedOutput: "2", + ExpectedOutputLength: 1, + ExpectedError: false, + }, + { + Name: "array-of-values", + Path: "ids[0]", + Data: `{"ids": [1, 2]}`, + ExpectedOutput: "1", + ExpectedOutputLength: 1, + ExpectedError: false, + }, + { + Name: "array-of-values-and-invalid-index", + Path: "ids[wat]", + Data: `{"ids": [1, 2]}`, + ExpectedOutput: "", + ExpectedOutputLength: 0, + ExpectedError: true, + }, + { + Name: "array-of-values-at-root", + Path: "[1]", + Data: `[1, 2]`, + ExpectedOutput: "2", + ExpectedOutputLength: 1, + ExpectedError: false, + }, + { + Name: "array-of-maps-at-root", + Path: "[0].id", + Data: `[{"id": 1}, {"id": 2}]`, + ExpectedOutput: "1", + ExpectedOutputLength: 1, + ExpectedError: false, + }, + { + Name: "array-of-maps-at-root-and-invalid-index", + Path: "[5].id", + Data: `[{"id": 1}, {"id": 2}]`, + ExpectedOutput: "", + ExpectedOutputLength: 0, + ExpectedError: true, + }, + { + Name: "long-walk-and-array", + Path: "data.ids[0].id", + Data: `{"data": {"ids": [{"id": 1}, {"id": 2}, {"id": 3}]}}`, + ExpectedOutput: "1", + ExpectedOutputLength: 1, + ExpectedError: false, + }, + { + Name: "nested-array", + Path: "[3][2]", + Data: `[[1, 2], [3, 4], [], [5, 6, 7]]`, + ExpectedOutput: "7", + ExpectedOutputLength: 1, + ExpectedError: false, + }, + { + Name: "map-of-nested-arrays", + Path: "data[1][1]", + Data: `{"data": [["a", "b", "c"], ["d", "eeeee", "f"]]}`, + ExpectedOutput: "eeeee", + ExpectedOutputLength: 5, + ExpectedError: false, + }, + { + Name: "partially-invalid-path", + Path: "data.name.invalid", + Data: `{"data": {"name": "john"}}`, + ExpectedOutput: "", + ExpectedOutputLength: 0, + ExpectedError: true, + }, } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithInvalidData(t *testing.T) { - path := "simple" - data := `invalid data` - _, _, err := Eval(path, []byte(data)) - if err == nil { - t.Error("expected an error") - } -} - -func TestEvalWithInvalidPath(t *testing.T) { - path := "errors" - data := `{}` - _, _, err := Eval(path, []byte(data)) - if err == nil { - t.Error("Expected error, but got", err) - } -} - -func TestEvalWithLongSimpleWalk(t *testing.T) { - path := "long.simple.walk" - data := `{"long": {"simple": {"walk": "value"}}}` - - expectedOutput := "value" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithArrayOfMaps(t *testing.T) { - path := "ids[1].id" - data := `{"ids": [{"id": 1}, {"id": 2}]}` - - expectedOutput := "2" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithArrayOfValues(t *testing.T) { - path := "ids[0]" - data := `{"ids": [1, 2]}` - - expectedOutput := "1" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithArrayOfValuesAndInvalidIndex(t *testing.T) { - path := "ids[wat]" - data := `{"ids": [1, 2]}` - - _, _, err := Eval(path, []byte(data)) - if err == nil { - t.Error("Expected an error") - } -} - -func TestEvalWithRootArrayOfValues(t *testing.T) { - path := "[1]" - data := `[1, 2]` - - expectedOutput := "2" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithRootArrayOfMaps(t *testing.T) { - path := "[0].id" - data := `[{"id": 1}, {"id": 2}]` - - expectedOutput := "1" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithRootArrayOfMapsUsingInvalidArrayIndex(t *testing.T) { - path := "[5].id" - data := `[{"id": 1}, {"id": 2}]` - - _, _, err := Eval(path, []byte(data)) - if err == nil { - t.Error("Should've returned an error, but didn't") - } -} - -func TestEvalWithLongWalkAndArray(t *testing.T) { - path := "data.ids[0].id" - data := `{"data": {"ids": [{"id": 1}, {"id": 2}, {"id": 3}]}}` - - expectedOutput := "1" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithNestedArray(t *testing.T) { - path := "[3][2]" - data := `[[1, 2], [3, 4], [], [5, 6, 7]]` - - expectedOutput := "7" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) - } -} - -func TestEvalWithMapOfNestedArray(t *testing.T) { - path := "data[1][1]" - data := `{"data": [["a", "b", "c"], ["d", "e", "f"]]}` - - expectedOutput := "e" - - output, _, err := Eval(path, []byte(data)) - if err != nil { - t.Error("Didn't expect any error, but got", err) - } - if output != expectedOutput { - t.Errorf("Expected output to be %v, but was %v", expectedOutput, output) + for _, scenario := range scenarios { + t.Run(scenario.Name, func(t *testing.T) { + output, outputLength, err := Eval(scenario.Path, []byte(scenario.Data)) + if (err != nil) != scenario.ExpectedError { + if scenario.ExpectedError { + t.Errorf("Expected error, got '%v'", err) + } else { + t.Errorf("Expected no error, got '%v'", err) + } + } + if outputLength != scenario.ExpectedOutputLength { + t.Errorf("Expected output length to be %v, but was %v", scenario.ExpectedOutputLength, outputLength) + } + if output != scenario.ExpectedOutput { + t.Errorf("Expected output to be %v, but was %v", scenario.ExpectedOutput, output) + } + }) } }