b74544d345
As it turns out, closures are very hard to validate without running them. Since table-driven tests tend to rely on value types that can be compared directly, using structs that adhere to a generic callback interface is more work, but is more easily tested. * Changed jsonpath match functions to structs with Call() methods * Added tests to verify the generation of jsonpath QueryPath data * Added tests to verify jsonpath lexer * Fixed jsonpath whitespace handling bug * Fixed numerous flaws in jsonpath parser
187 lines
3.5 KiB
Go
187 lines
3.5 KiB
Go
package jpath
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
func pathString(path QueryPath) string {
|
|
result := "["
|
|
for _, v := range path {
|
|
result += fmt.Sprintf("%T:%v, ", v, v)
|
|
}
|
|
return result + "]"
|
|
}
|
|
|
|
func assertPathMatch(t *testing.T, path, ref QueryPath) bool {
|
|
if len(path) != len(ref) {
|
|
t.Errorf("lengths do not match: %v vs %v",
|
|
pathString(path), pathString(ref))
|
|
return false
|
|
} else {
|
|
for i, v := range ref {
|
|
pass := false
|
|
node := path[i]
|
|
// compare by value
|
|
switch refNode := v.(type) {
|
|
case *matchKeyFn:
|
|
castNode, ok := node.(*matchKeyFn)
|
|
pass = ok && (*refNode == *castNode)
|
|
case *matchIndexFn:
|
|
castNode, ok := node.(*matchIndexFn)
|
|
pass = ok && (*refNode == *castNode)
|
|
case *matchSliceFn:
|
|
castNode, ok := node.(*matchSliceFn)
|
|
pass = ok && (*refNode == *castNode)
|
|
case *matchAnyFn:
|
|
castNode, ok := node.(*matchAnyFn)
|
|
pass = ok && (*refNode == *castNode)
|
|
case *matchUnionFn:
|
|
castNode, ok := node.(*matchUnionFn)
|
|
// special case - comapre all contents
|
|
pass = ok && assertPathMatch(t, castNode.Union, refNode.Union)
|
|
case *matchRecursiveFn:
|
|
castNode, ok := node.(*matchRecursiveFn)
|
|
pass = ok && (*refNode == *castNode)
|
|
}
|
|
if !pass {
|
|
t.Errorf("paths do not match at index %d: %v vs %v",
|
|
i, pathString(path), pathString(ref))
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func assertPath(t *testing.T, query string, ref QueryPath) {
|
|
_, flow := lex(query)
|
|
path := parse(flow)
|
|
assertPathMatch(t, path, ref)
|
|
}
|
|
|
|
func TestPathRoot(t *testing.T) {
|
|
assertPath(t,
|
|
"$",
|
|
QueryPath{
|
|
// empty
|
|
})
|
|
}
|
|
|
|
func TestPathKey(t *testing.T) {
|
|
assertPath(t,
|
|
"$.foo",
|
|
QueryPath{
|
|
&matchKeyFn{ "foo" },
|
|
})
|
|
}
|
|
|
|
func TestPathBracketKey(t *testing.T) {
|
|
assertPath(t,
|
|
"$[foo]",
|
|
QueryPath{
|
|
&matchKeyFn{ "foo" },
|
|
})
|
|
}
|
|
|
|
func TestPathBracketStringKey(t *testing.T) {
|
|
assertPath(t,
|
|
"$['foo']",
|
|
QueryPath{
|
|
&matchKeyFn{ "foo" },
|
|
})
|
|
}
|
|
|
|
func TestPathIndex(t *testing.T) {
|
|
assertPath(t,
|
|
"$[123]",
|
|
QueryPath{
|
|
&matchIndexFn{ 123 },
|
|
})
|
|
}
|
|
|
|
func TestPathSliceStart(t *testing.T) {
|
|
assertPath(t,
|
|
"$[123:]",
|
|
QueryPath{
|
|
&matchSliceFn{ 123, math.MaxInt64, 1 },
|
|
})
|
|
}
|
|
|
|
func TestPathSliceStartEnd(t *testing.T) {
|
|
assertPath(t,
|
|
"$[123:456]",
|
|
QueryPath{
|
|
&matchSliceFn{ 123, 456, 1 },
|
|
})
|
|
}
|
|
|
|
func TestPathSliceStartEndColon(t *testing.T) {
|
|
assertPath(t,
|
|
"$[123:456:]",
|
|
QueryPath{
|
|
&matchSliceFn{ 123, 456, 1 },
|
|
})
|
|
}
|
|
|
|
func TestPathSliceStartStep(t *testing.T) {
|
|
assertPath(t,
|
|
"$[123::7]",
|
|
QueryPath{
|
|
&matchSliceFn{ 123, math.MaxInt64, 7 },
|
|
})
|
|
}
|
|
|
|
func TestPathSliceEndStep(t *testing.T) {
|
|
assertPath(t,
|
|
"$[:456:7]",
|
|
QueryPath{
|
|
&matchSliceFn{ 0, 456, 7 },
|
|
})
|
|
}
|
|
|
|
func TestPathSliceStep(t *testing.T) {
|
|
assertPath(t,
|
|
"$[::7]",
|
|
QueryPath{
|
|
&matchSliceFn{ 0, math.MaxInt64, 7 },
|
|
})
|
|
}
|
|
|
|
func TestPathSliceAll(t *testing.T) {
|
|
assertPath(t,
|
|
"$[123:456:7]",
|
|
QueryPath{
|
|
&matchSliceFn{ 123, 456, 7 },
|
|
})
|
|
}
|
|
|
|
func TestPathAny(t *testing.T) {
|
|
assertPath(t,
|
|
"$.*",
|
|
QueryPath{
|
|
&matchAnyFn{},
|
|
})
|
|
}
|
|
|
|
func TestPathUnion(t *testing.T) {
|
|
assertPath(t,
|
|
"$[foo, bar, baz]",
|
|
QueryPath{
|
|
&matchUnionFn{ []PathFn {
|
|
&matchKeyFn{ "foo" },
|
|
&matchKeyFn{ "bar" },
|
|
&matchKeyFn{ "baz" },
|
|
}},
|
|
})
|
|
}
|
|
|
|
func TestPathRecurse(t *testing.T) {
|
|
assertPath(t,
|
|
"$..*",
|
|
QueryPath{
|
|
&matchRecursiveFn{},
|
|
})
|
|
}
|