1/* 2Copyright 2015 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package jsonpath 18 19import ( 20 "testing" 21) 22 23type parserTest struct { 24 name string 25 text string 26 nodes []Node 27 shouldError bool 28} 29 30var 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 88func 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 106func 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 131type failParserTest struct { 132 name string 133 text string 134 err string 135} 136 137func 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 } 145 for _, test := range failParserTests { 146 _, err := Parse(test.name, test.text) 147 var out string 148 if err == nil { 149 out = "nil" 150 } else { 151 out = err.Error() 152 } 153 if out != test.err { 154 t.Errorf("in %s, expect to get error %v, got %v", test.name, test.err, out) 155 } 156 } 157} 158