jsonpath_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package jsonpath
  14. import (
  15. "bytes"
  16. "encoding/json"
  17. "fmt"
  18. "reflect"
  19. "sort"
  20. "strings"
  21. "testing"
  22. )
  23. type jsonpathTest struct {
  24. name string
  25. template string
  26. input interface{}
  27. expect string
  28. }
  29. func testJSONPath(tests []jsonpathTest, t *testing.T) {
  30. for _, test := range tests {
  31. j := New(test.name)
  32. err := j.Parse(test.template)
  33. if err != nil {
  34. t.Errorf("in %s, parse %s error %v", test.name, test.template, err)
  35. }
  36. buf := new(bytes.Buffer)
  37. err = j.Execute(buf, test.input)
  38. if err != nil {
  39. t.Errorf("in %s, execute error %v", test.name, err)
  40. }
  41. out := buf.String()
  42. if out != test.expect {
  43. t.Errorf(`in %s, expect to get "%s", got "%s"`, test.name, test.expect, out)
  44. }
  45. }
  46. }
  47. // testJSONPathSortOutput test cases related to map, the results may print in random order
  48. func testJSONPathSortOutput(tests []jsonpathTest, t *testing.T) {
  49. for _, test := range tests {
  50. j := New(test.name)
  51. err := j.Parse(test.template)
  52. if err != nil {
  53. t.Errorf("in %s, parse %s error %v", test.name, test.template, err)
  54. }
  55. buf := new(bytes.Buffer)
  56. err = j.Execute(buf, test.input)
  57. if err != nil {
  58. t.Errorf("in %s, execute error %v", test.name, err)
  59. }
  60. out := buf.String()
  61. //since map is visited in random order, we need to sort the results.
  62. sortedOut := strings.Fields(out)
  63. sort.Strings(sortedOut)
  64. sortedExpect := strings.Fields(test.expect)
  65. sort.Strings(sortedExpect)
  66. if !reflect.DeepEqual(sortedOut, sortedExpect) {
  67. t.Errorf(`in %s, expect to get "%s", got "%s"`, test.name, test.expect, out)
  68. }
  69. }
  70. }
  71. func testFailJSONPath(tests []jsonpathTest, t *testing.T) {
  72. for _, test := range tests {
  73. j := New(test.name)
  74. err := j.Parse(test.template)
  75. if err != nil {
  76. t.Errorf("in %s, parse %s error %v", test.name, test.template, err)
  77. }
  78. buf := new(bytes.Buffer)
  79. err = j.Execute(buf, test.input)
  80. var out string
  81. if err == nil {
  82. out = "nil"
  83. } else {
  84. out = err.Error()
  85. }
  86. if out != test.expect {
  87. t.Errorf("in %s, expect to get error %q, got %q", test.name, test.expect, out)
  88. }
  89. }
  90. }
  91. type book struct {
  92. Category string
  93. Author string
  94. Title string
  95. Price float32
  96. }
  97. func (b book) String() string {
  98. return fmt.Sprintf("{Category: %s, Author: %s, Title: %s, Price: %v}", b.Category, b.Author, b.Title, b.Price)
  99. }
  100. type bicycle struct {
  101. Color string
  102. Price float32
  103. }
  104. type empName string
  105. type job string
  106. type store struct {
  107. Book []book
  108. Bicycle bicycle
  109. Name string
  110. Labels map[string]int
  111. Employees map[empName]job
  112. }
  113. func TestStructInput(t *testing.T) {
  114. storeData := store{
  115. Name: "jsonpath",
  116. Book: []book{
  117. {"reference", "Nigel Rees", "Sayings of the Centurey", 8.95},
  118. {"fiction", "Evelyn Waugh", "Sword of Honour", 12.99},
  119. {"fiction", "Herman Melville", "Moby Dick", 8.99},
  120. },
  121. Bicycle: bicycle{"red", 19.95},
  122. Labels: map[string]int{
  123. "engieer": 10,
  124. "web/html": 15,
  125. "k8s-app": 20,
  126. },
  127. Employees: map[empName]job{
  128. "jason": "manager",
  129. "dan": "clerk",
  130. },
  131. }
  132. storeTests := []jsonpathTest{
  133. {"plain", "hello jsonpath", nil, "hello jsonpath"},
  134. {"recursive", "{..}", []int{1, 2, 3}, "[1 2 3]"},
  135. {"filter", "{[?(@<5)]}", []int{2, 6, 3, 7}, "2 3"},
  136. {"quote", `{"{"}`, nil, "{"},
  137. {"union", "{[1,3,4]}", []int{0, 1, 2, 3, 4}, "1 3 4"},
  138. {"array", "{[0:2]}", []string{"Monday", "Tudesday"}, "Monday Tudesday"},
  139. {"variable", "hello {.Name}", storeData, "hello jsonpath"},
  140. {"dict/", "{$.Labels.web/html}", storeData, "15"},
  141. {"dict/", "{$.Employees.jason}", storeData, "manager"},
  142. {"dict/", "{$.Employees.dan}", storeData, "clerk"},
  143. {"dict-", "{.Labels.k8s-app}", storeData, "20"},
  144. {"nest", "{.Bicycle.Color}", storeData, "red"},
  145. {"allarray", "{.Book[*].Author}", storeData, "Nigel Rees Evelyn Waugh Herman Melville"},
  146. {"allfileds", "{.Bicycle.*}", storeData, "red 19.95"},
  147. {"recurfileds", "{..Price}", storeData, "8.95 12.99 8.99 19.95"},
  148. {"lastarray", "{.Book[-1:]}", storeData,
  149. "{Category: fiction, Author: Herman Melville, Title: Moby Dick, Price: 8.99}"},
  150. {"recurarray", "{..Book[2]}", storeData,
  151. "{Category: fiction, Author: Herman Melville, Title: Moby Dick, Price: 8.99}"},
  152. }
  153. testJSONPath(storeTests, t)
  154. failStoreTests := []jsonpathTest{
  155. {"invalid identfier", "{hello}", storeData, "unrecognized identifier hello"},
  156. {"nonexistent field", "{.hello}", storeData, "hello is not found"},
  157. {"invalid array", "{.Labels[0]}", storeData, "map[string]int is not array or slice"},
  158. {"invalid filter operator", "{.Book[?(@.Price<>10)]}", storeData, "unrecognized filter operator <>"},
  159. {"redundent end", "{range .Labels.*}{@}{end}{end}", storeData, "not in range, nothing to end"},
  160. }
  161. testFailJSONPath(failStoreTests, t)
  162. }
  163. func TestJSONInput(t *testing.T) {
  164. var pointsJSON = []byte(`[
  165. {"id": "i1", "x":4, "y":-5},
  166. {"id": "i2", "x":-2, "y":-5, "z":1},
  167. {"id": "i3", "x": 8, "y": 3 },
  168. {"id": "i4", "x": -6, "y": -1 },
  169. {"id": "i5", "x": 0, "y": 2, "z": 1 },
  170. {"id": "i6", "x": 1, "y": 4 }
  171. ]`)
  172. var pointsData interface{}
  173. err := json.Unmarshal(pointsJSON, &pointsData)
  174. if err != nil {
  175. t.Error(err)
  176. }
  177. pointsTests := []jsonpathTest{
  178. {"exists filter", "{[?(@.z)].id}", pointsData, "i2 i5"},
  179. {"bracket key", "{[0]['id']}", pointsData, "i1"},
  180. }
  181. testJSONPath(pointsTests, t)
  182. }
  183. // TestKubernetes tests some use cases from kubernetes
  184. func TestKubernetes(t *testing.T) {
  185. var input = []byte(`{
  186. "kind": "List",
  187. "items":[
  188. {
  189. "kind":"None",
  190. "metadata":{"name":"127.0.0.1"},
  191. "status":{
  192. "capacity":{"cpu":"4"},
  193. "addresses":[{"type": "LegacyHostIP", "address":"127.0.0.1"}]
  194. }
  195. },
  196. {
  197. "kind":"None",
  198. "metadata":{"name":"127.0.0.2"},
  199. "status":{
  200. "capacity":{"cpu":"8"},
  201. "addresses":[
  202. {"type": "LegacyHostIP", "address":"127.0.0.2"},
  203. {"type": "another", "address":"127.0.0.3"}
  204. ]
  205. }
  206. }
  207. ],
  208. "users":[
  209. {
  210. "name": "myself",
  211. "user": {}
  212. },
  213. {
  214. "name": "e2e",
  215. "user": {"username": "admin", "password": "secret"}
  216. }
  217. ]
  218. }`)
  219. var nodesData interface{}
  220. err := json.Unmarshal(input, &nodesData)
  221. if err != nil {
  222. t.Error(err)
  223. }
  224. nodesTests := []jsonpathTest{
  225. {"range item", `{range .items[*]}{.metadata.name}, {end}{.kind}`, nodesData, "127.0.0.1, 127.0.0.2, List"},
  226. {"range item with quote", `{range .items[*]}{.metadata.name}{"\t"}{end}`, nodesData, "127.0.0.1\t127.0.0.2\t"},
  227. {"range addresss", `{.items[*].status.addresses[*].address}`, nodesData,
  228. "127.0.0.1 127.0.0.2 127.0.0.3"},
  229. {"double range", `{range .items[*]}{range .status.addresses[*]}{.address}, {end}{end}`, nodesData,
  230. "127.0.0.1, 127.0.0.2, 127.0.0.3, "},
  231. {"item name", `{.items[*].metadata.name}`, nodesData, "127.0.0.1 127.0.0.2"},
  232. {"union nodes capacity", `{.items[*]['metadata.name', 'status.capacity']}`, nodesData,
  233. "127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]"},
  234. {"range nodes capacity", `{range .items[*]}[{.metadata.name}, {.status.capacity}] {end}`, nodesData,
  235. "[127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]] "},
  236. {"user password", `{.users[?(@.name=="e2e")].user.password}`, &nodesData, "secret"},
  237. }
  238. testJSONPath(nodesTests, t)
  239. randomPrintOrderTests := []jsonpathTest{
  240. {"recursive name", "{..name}", nodesData, `127.0.0.1 127.0.0.2 myself e2e`},
  241. }
  242. testJSONPathSortOutput(randomPrintOrderTests, t)
  243. }