k8s.io/client-go@v0.31.1/util/jsonpath/parser_test.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 }