1
16
17 package jsonpath
18
19 import (
20 "testing"
21 )
22
23 type parserTest struct {
24 name string
25 text string
26 nodes []Node
27 shouldError bool
28 }
29
30 var parserTests = []parserTest{
31 {"plain", `hello jsonpath`, []Node{newText("hello jsonpath")}, false},
32 {"variable", `hello {.jsonpath}`,
33 []Node{newText("hello "), newList(), newField("jsonpath")}, false},
34 {"arrayfiled", `hello {['jsonpath']}`,
35 []Node{newText("hello "), newList(), newField("jsonpath")}, false},
36 {"quote", `{"{"}`, []Node{newList(), newText("{")}, false},
37 {"array", `{[1:3]}`, []Node{newList(),
38 newArray([3]ParamsEntry{{1, true, false}, {3, true, false}, {0, false, false}})}, false},
39 {"allarray", `{.book[*].author}`,
40 []Node{newList(), newField("book"),
41 newArray([3]ParamsEntry{{0, false, false}, {0, false, false}, {0, false, false}}), newField("author")}, false},
42 {"wildcard", `{.bicycle.*}`,
43 []Node{newList(), newField("bicycle"), newWildcard()}, false},
44 {"filter", `{[?(@.price<3)]}`,
45 []Node{newList(), newFilter(newList(), newList(), "<"),
46 newList(), newField("price"), newList(), newInt(3)}, false},
47 {"recursive", `{..}`, []Node{newList(), newRecursive()}, false},
48 {"recurField", `{..price}`,
49 []Node{newList(), newRecursive(), newField("price")}, false},
50 {"arraydict", `{['book.price']}`, []Node{newList(),
51 newField("book"), newField("price"),
52 }, false},
53 {"union", `{['bicycle.price', 3, 'book.price']}`, []Node{newList(), newUnion([]*ListNode{}),
54 newList(), newField("bicycle"), newField("price"),
55 newList(), newArray([3]ParamsEntry{{3, true, false}, {4, true, true}, {0, false, false}}),
56 newList(), newField("book"), newField("price"),
57 }, false},
58 {"range", `{range .items}{.name},{end}`, []Node{
59 newList(), newIdentifier("range"), newField("items"),
60 newList(), newField("name"), newText(","),
61 newList(), newIdentifier("end"),
62 }, false},
63 {"malformat input", `{\\\}`, []Node{}, true},
64 {"paired parentheses in quotes", `{[?(@.status.nodeInfo.osImage == "()")]}`,
65 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("()")}, false},
66 {"paired parentheses in double quotes and with double quotes escape", `{[?(@.status.nodeInfo.osImage == "(\"\")")]}`,
67 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("(\"\")")}, false},
68 {"unregular parentheses in double quotes", `{[?(@.test == "())(")]}`,
69 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("test"), newList(), newText("())(")}, false},
70 {"plain text in single quotes", `{[?(@.status.nodeInfo.osImage == 'Linux')]}`,
71 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("Linux")}, false},
72 {"test filter suffix", `{[?(@.status.nodeInfo.osImage == "{[()]}")]}`,
73 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("{[()]}")}, false},
74 {"double inside single", `{[?(@.status.nodeInfo.osImage == "''")]}`,
75 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("''")}, false},
76 {"single inside double", `{[?(@.status.nodeInfo.osImage == '""')]}`,
77 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("\"\"")}, false},
78 {"single containing escaped single", `{[?(@.status.nodeInfo.osImage == '\\\'')]}`,
79 []Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("\\'")}, false},
80 {"negative index slice, equals a[len-5] to a[len-1]", `{[-5:]}`, []Node{newList(),
81 newArray([3]ParamsEntry{{-5, true, false}, {0, false, false}, {0, false, false}})}, false},
82 {"negative index slice, equals a[len-1]", `{[-1]}`, []Node{newList(),
83 newArray([3]ParamsEntry{{-1, true, false}, {0, true, true}, {0, false, false}})}, false},
84 {"negative index slice, equals a[1] to a[len-1]", `{[1:-1]}`, []Node{newList(),
85 newArray([3]ParamsEntry{{1, true, false}, {-1, true, false}, {0, false, false}})}, false},
86 }
87
88 func collectNode(nodes []Node, cur Node) []Node {
89 nodes = append(nodes, cur)
90 switch cur.Type() {
91 case NodeList:
92 for _, node := range cur.(*ListNode).Nodes {
93 nodes = collectNode(nodes, node)
94 }
95 case NodeFilter:
96 nodes = collectNode(nodes, cur.(*FilterNode).Left)
97 nodes = collectNode(nodes, cur.(*FilterNode).Right)
98 case NodeUnion:
99 for _, node := range cur.(*UnionNode).Nodes {
100 nodes = collectNode(nodes, node)
101 }
102 }
103 return nodes
104 }
105
106 func TestParser(t *testing.T) {
107 for _, test := range parserTests {
108 parser, err := Parse(test.name, test.text)
109 if test.shouldError {
110 if err == nil {
111 t.Errorf("unexpected non-error when parsing %s", test.name)
112 }
113 continue
114 }
115 if err != nil {
116 t.Errorf("parse %s error %v", test.name, err)
117 }
118 result := collectNode([]Node{}, parser.Root)[1:]
119 if len(result) != len(test.nodes) {
120 t.Errorf("in %s, expect to get %d nodes, got %d nodes", test.name, len(test.nodes), len(result))
121 t.Error(result)
122 }
123 for i, expect := range test.nodes {
124 if result[i].String() != expect.String() {
125 t.Errorf("in %s, %dth node, expect %v, got %v", test.name, i, expect, result[i])
126 }
127 }
128 }
129 }
130
131 type failParserTest struct {
132 name string
133 text string
134 err string
135 }
136
137 func TestFailParser(t *testing.T) {
138 failParserTests := []failParserTest{
139 {"unclosed action", "{.hello", "unclosed action"},
140 {"unrecognized character", "{*}", "unrecognized character in action: U+002A '*'"},
141 {"invalid number", "{+12.3.0}", "cannot parse number +12.3.0"},
142 {"unterminated array", "{[1}", "unterminated array"},
143 {"unterminated filter", "{[?(.price]}", "unterminated filter"},
144 {"invalid multiple recursive descent", "{........}", "invalid multiple recursive descent"},
145 }
146 for _, test := range failParserTests {
147 _, err := Parse(test.name, test.text)
148 var out string
149 if err == nil {
150 out = "nil"
151 } else {
152 out = err.Error()
153 }
154 if out != test.err {
155 t.Errorf("in %s, expect to get error %v, got %v", test.name, test.err, out)
156 }
157 }
158 }
159
View as plain text