1 package query
2
3 import (
4 "fmt"
5 "io/ioutil"
6 "sort"
7 "strings"
8 "testing"
9 "time"
10
11 "github.com/pelletier/go-toml"
12 )
13
14 type queryTestNode struct {
15 value interface{}
16 position toml.Position
17 }
18
19 func valueString(root interface{}) string {
20 result := ""
21 switch node := root.(type) {
22 case *Result:
23 items := []string{}
24 for i, v := range node.Values() {
25 items = append(items, fmt.Sprintf("%s:%s",
26 node.Positions()[i].String(), valueString(v)))
27 }
28 sort.Strings(items)
29 result = "[" + strings.Join(items, ", ") + "]"
30 case queryTestNode:
31 result = fmt.Sprintf("%s:%s",
32 node.position.String(), valueString(node.value))
33 case []interface{}:
34 items := []string{}
35 for _, v := range node {
36 items = append(items, valueString(v))
37 }
38 sort.Strings(items)
39 result = "[" + strings.Join(items, ", ") + "]"
40 case *toml.Tree:
41
42 items := []string{}
43 for _, k := range node.Keys() {
44 v := node.GetPath([]string{k})
45 items = append(items, k+":"+valueString(v))
46 }
47 sort.Strings(items)
48 result = "{" + strings.Join(items, ", ") + "}"
49 case map[string]interface{}:
50
51 items := []string{}
52 for k, v := range node {
53 items = append(items, k+":"+valueString(v))
54 }
55 sort.Strings(items)
56 result = "{" + strings.Join(items, ", ") + "}"
57 case int64:
58 result += fmt.Sprintf("%d", node)
59 case string:
60 result += "'" + node + "'"
61 case float64:
62 result += fmt.Sprintf("%f", node)
63 case bool:
64 result += fmt.Sprintf("%t", node)
65 case time.Time:
66 result += fmt.Sprintf("'%v'", node)
67 }
68 return result
69 }
70
71 func assertValue(t *testing.T, result, ref interface{}) {
72 pathStr := valueString(result)
73 refStr := valueString(ref)
74 if pathStr != refStr {
75 t.Errorf("values do not match")
76 t.Log("test:", pathStr)
77 t.Log("ref: ", refStr)
78 }
79 }
80
81 func assertParseError(t *testing.T, query string, errString string) {
82 _, err := Compile(query)
83 if err == nil {
84 t.Error("error should be non-nil")
85 return
86 }
87 if err.Error() != errString {
88 t.Errorf("error does not match")
89 t.Log("test:", err.Error())
90 t.Log("ref: ", errString)
91 }
92 }
93
94 func assertQueryPositions(t *testing.T, tomlDoc string, query string, ref []interface{}) {
95 tree, err := toml.Load(tomlDoc)
96 if err != nil {
97 t.Errorf("Non-nil toml parse error: %v", err)
98 return
99 }
100 q, err := Compile(query)
101 if err != nil {
102 t.Error(err)
103 return
104 }
105 results := q.Execute(tree)
106 assertValue(t, results, ref)
107 }
108
109 func TestQueryRoot(t *testing.T) {
110 assertQueryPositions(t,
111 "a = 42",
112 "$",
113 []interface{}{
114 queryTestNode{
115 map[string]interface{}{
116 "a": int64(42),
117 }, toml.Position{1, 1},
118 },
119 })
120 }
121
122 func TestQueryKey(t *testing.T) {
123 assertQueryPositions(t,
124 "[foo]\na = 42",
125 "$.foo.a",
126 []interface{}{
127 queryTestNode{
128 int64(42), toml.Position{2, 1},
129 },
130 })
131 }
132
133 func TestQueryKeyString(t *testing.T) {
134 assertQueryPositions(t,
135 "[foo]\na = 42",
136 "$.foo['a']",
137 []interface{}{
138 queryTestNode{
139 int64(42), toml.Position{2, 1},
140 },
141 })
142 }
143
144 func TestQueryKeyUnicodeString(t *testing.T) {
145 assertQueryPositions(t,
146 "['f𝟘.o']\na = 42",
147 "$['f𝟘.o']['a']",
148 []interface{}{
149 queryTestNode{
150 int64(42), toml.Position{2, 1},
151 },
152 })
153 }
154
155 func TestQueryIndexError1(t *testing.T) {
156 assertParseError(t, "$.foo.a[5", "(1, 10): expected ',' or ']', not ''")
157 }
158
159 func TestQueryIndexError2(t *testing.T) {
160 assertParseError(t, "$.foo.a[]", "(1, 9): expected union sub expression, not ']', 0")
161 }
162
163 func TestQueryIndex(t *testing.T) {
164 assertQueryPositions(t,
165 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
166 "$.foo.a[5]",
167 []interface{}{
168 queryTestNode{int64(5), toml.Position{2, 1}},
169 })
170 }
171
172 func TestQueryIndexNegative(t *testing.T) {
173 assertQueryPositions(t,
174 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
175 "$.foo.a[-2]",
176 []interface{}{
177 queryTestNode{int64(8), toml.Position{2, 1}},
178 })
179 }
180
181 func TestQueryIndexWrong(t *testing.T) {
182 assertQueryPositions(t,
183 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
184 "$.foo.a[99]",
185 []interface{}{})
186 }
187
188 func TestQueryIndexEmpty(t *testing.T) {
189 assertQueryPositions(t,
190 "[foo]\na = []",
191 "$.foo.a[5]",
192 []interface{}{})
193 }
194
195 func TestQueryIndexTree(t *testing.T) {
196 assertQueryPositions(t,
197 "[[foo]]\na = [0,1,2,3,4,5,6,7,8,9]\n[[foo]]\nb = 3",
198 "$.foo[1].b",
199 []interface{}{
200 queryTestNode{int64(3), toml.Position{4, 1}},
201 })
202 }
203
204 func TestQuerySliceError1(t *testing.T) {
205 assertParseError(t, "$.foo.a[3:?]", "(1, 11): expected ']' or ':'")
206 }
207
208 func TestQuerySliceError2(t *testing.T) {
209 assertParseError(t, "$.foo.a[:::]", "(1, 11): expected ']'")
210 }
211
212 func TestQuerySliceError3(t *testing.T) {
213 assertParseError(t, "$.foo.a[::0]", "(1, 11): step cannot be zero")
214 }
215
216 func TestQuerySliceRange(t *testing.T) {
217 assertQueryPositions(t,
218 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
219 "$.foo.a[:5]",
220 []interface{}{
221 queryTestNode{int64(0), toml.Position{2, 1}},
222 queryTestNode{int64(1), toml.Position{2, 1}},
223 queryTestNode{int64(2), toml.Position{2, 1}},
224 queryTestNode{int64(3), toml.Position{2, 1}},
225 queryTestNode{int64(4), toml.Position{2, 1}},
226 })
227 }
228
229 func TestQuerySliceStep(t *testing.T) {
230 assertQueryPositions(t,
231 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
232 "$.foo.a[0:5:2]",
233 []interface{}{
234 queryTestNode{int64(0), toml.Position{2, 1}},
235 queryTestNode{int64(2), toml.Position{2, 1}},
236 queryTestNode{int64(4), toml.Position{2, 1}},
237 })
238 }
239
240 func TestQuerySliceStartNegative(t *testing.T) {
241 assertQueryPositions(t,
242 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
243 "$.foo.a[-3:]",
244 []interface{}{
245 queryTestNode{int64(7), toml.Position{2, 1}},
246 queryTestNode{int64(8), toml.Position{2, 1}},
247 queryTestNode{int64(9), toml.Position{2, 1}},
248 })
249 }
250
251 func TestQuerySliceEndNegative(t *testing.T) {
252 assertQueryPositions(t,
253 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
254 "$.foo.a[:-6]",
255 []interface{}{
256 queryTestNode{int64(0), toml.Position{2, 1}},
257 queryTestNode{int64(1), toml.Position{2, 1}},
258 queryTestNode{int64(2), toml.Position{2, 1}},
259 queryTestNode{int64(3), toml.Position{2, 1}},
260 })
261 }
262
263 func TestQuerySliceStepNegative(t *testing.T) {
264 assertQueryPositions(t,
265 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
266 "$.foo.a[::-2]",
267 []interface{}{
268 queryTestNode{int64(9), toml.Position{2, 1}},
269 queryTestNode{int64(7), toml.Position{2, 1}},
270 queryTestNode{int64(5), toml.Position{2, 1}},
271 queryTestNode{int64(3), toml.Position{2, 1}},
272 queryTestNode{int64(1), toml.Position{2, 1}},
273 })
274 }
275
276 func TestQuerySliceStartOverRange(t *testing.T) {
277 assertQueryPositions(t,
278 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
279 "$.foo.a[-99:3]",
280 []interface{}{
281 queryTestNode{int64(0), toml.Position{2, 1}},
282 queryTestNode{int64(1), toml.Position{2, 1}},
283 queryTestNode{int64(2), toml.Position{2, 1}},
284 })
285 }
286
287 func TestQuerySliceStartOverRangeNegative(t *testing.T) {
288 assertQueryPositions(t,
289 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
290 "$.foo.a[99:7:-1]",
291 []interface{}{
292 queryTestNode{int64(9), toml.Position{2, 1}},
293 queryTestNode{int64(8), toml.Position{2, 1}},
294 })
295 }
296
297 func TestQuerySliceEndOverRange(t *testing.T) {
298 assertQueryPositions(t,
299 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
300 "$.foo.a[7:99]",
301 []interface{}{
302 queryTestNode{int64(7), toml.Position{2, 1}},
303 queryTestNode{int64(8), toml.Position{2, 1}},
304 queryTestNode{int64(9), toml.Position{2, 1}},
305 })
306 }
307
308 func TestQuerySliceEndOverRangeNegative(t *testing.T) {
309 assertQueryPositions(t,
310 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
311 "$.foo.a[2:-99:-1]",
312 []interface{}{
313 queryTestNode{int64(2), toml.Position{2, 1}},
314 queryTestNode{int64(1), toml.Position{2, 1}},
315 queryTestNode{int64(0), toml.Position{2, 1}},
316 })
317 }
318
319 func TestQuerySliceWrongRange(t *testing.T) {
320 assertQueryPositions(t,
321 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
322 "$.foo.a[5:3]",
323 []interface{}{})
324 }
325
326 func TestQuerySliceWrongRangeNegative(t *testing.T) {
327 assertQueryPositions(t,
328 "[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
329 "$.foo.a[3:5:-1]",
330 []interface{}{})
331 }
332
333 func TestQuerySliceEmpty(t *testing.T) {
334 assertQueryPositions(t,
335 "[foo]\na = []",
336 "$.foo.a[5:]",
337 []interface{}{})
338 }
339
340 func TestQuerySliceTree(t *testing.T) {
341 assertQueryPositions(t,
342 "[[foo]]\na='nok'\n[[foo]]\na = [0,1,2,3,4,5,6,7,8,9]\n[[foo]]\na='ok'\nb = 3",
343 "$.foo[1:].a",
344 []interface{}{
345 queryTestNode{
346 []interface{}{
347 int64(0), int64(1), int64(2), int64(3), int64(4),
348 int64(5), int64(6), int64(7), int64(8), int64(9)},
349 toml.Position{4, 1}},
350 queryTestNode{"ok", toml.Position{6, 1}},
351 })
352 }
353
354 func TestQueryAny(t *testing.T) {
355 assertQueryPositions(t,
356 "[foo.bar]\na=1\nb=2\n[foo.baz]\na=3\nb=4",
357 "$.foo.*",
358 []interface{}{
359 queryTestNode{
360 map[string]interface{}{
361 "a": int64(1),
362 "b": int64(2),
363 }, toml.Position{1, 1},
364 },
365 queryTestNode{
366 map[string]interface{}{
367 "a": int64(3),
368 "b": int64(4),
369 }, toml.Position{4, 1},
370 },
371 })
372 }
373 func TestQueryUnionSimple(t *testing.T) {
374 assertQueryPositions(t,
375 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
376 "$.*[bar,foo]",
377 []interface{}{
378 queryTestNode{
379 map[string]interface{}{
380 "a": int64(1),
381 "b": int64(2),
382 }, toml.Position{1, 1},
383 },
384 queryTestNode{
385 map[string]interface{}{
386 "a": int64(3),
387 "b": int64(4),
388 }, toml.Position{4, 1},
389 },
390 queryTestNode{
391 map[string]interface{}{
392 "a": int64(5),
393 "b": int64(6),
394 }, toml.Position{7, 1},
395 },
396 })
397 }
398
399 func TestQueryRecursionAll(t *testing.T) {
400 assertQueryPositions(t,
401 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
402 "$..*",
403 []interface{}{
404 queryTestNode{
405 map[string]interface{}{
406 "foo": map[string]interface{}{
407 "bar": map[string]interface{}{
408 "a": int64(1),
409 "b": int64(2),
410 },
411 },
412 "baz": map[string]interface{}{
413 "foo": map[string]interface{}{
414 "a": int64(3),
415 "b": int64(4),
416 },
417 },
418 "gorf": map[string]interface{}{
419 "foo": map[string]interface{}{
420 "a": int64(5),
421 "b": int64(6),
422 },
423 },
424 }, toml.Position{1, 1},
425 },
426 queryTestNode{
427 map[string]interface{}{
428 "bar": map[string]interface{}{
429 "a": int64(1),
430 "b": int64(2),
431 },
432 }, toml.Position{1, 1},
433 },
434 queryTestNode{
435 map[string]interface{}{
436 "a": int64(1),
437 "b": int64(2),
438 }, toml.Position{1, 1},
439 },
440 queryTestNode{int64(1), toml.Position{2, 1}},
441 queryTestNode{int64(2), toml.Position{3, 1}},
442 queryTestNode{
443 map[string]interface{}{
444 "foo": map[string]interface{}{
445 "a": int64(3),
446 "b": int64(4),
447 },
448 }, toml.Position{4, 1},
449 },
450 queryTestNode{
451 map[string]interface{}{
452 "a": int64(3),
453 "b": int64(4),
454 }, toml.Position{4, 1},
455 },
456 queryTestNode{int64(3), toml.Position{5, 1}},
457 queryTestNode{int64(4), toml.Position{6, 1}},
458 queryTestNode{
459 map[string]interface{}{
460 "foo": map[string]interface{}{
461 "a": int64(5),
462 "b": int64(6),
463 },
464 }, toml.Position{7, 1},
465 },
466 queryTestNode{
467 map[string]interface{}{
468 "a": int64(5),
469 "b": int64(6),
470 }, toml.Position{7, 1},
471 },
472 queryTestNode{int64(5), toml.Position{8, 1}},
473 queryTestNode{int64(6), toml.Position{9, 1}},
474 })
475 }
476
477 func TestQueryRecursionUnionSimple(t *testing.T) {
478 assertQueryPositions(t,
479 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
480 "$..['foo','bar']",
481 []interface{}{
482 queryTestNode{
483 map[string]interface{}{
484 "bar": map[string]interface{}{
485 "a": int64(1),
486 "b": int64(2),
487 },
488 }, toml.Position{1, 1},
489 },
490 queryTestNode{
491 map[string]interface{}{
492 "a": int64(3),
493 "b": int64(4),
494 }, toml.Position{4, 1},
495 },
496 queryTestNode{
497 map[string]interface{}{
498 "a": int64(1),
499 "b": int64(2),
500 }, toml.Position{1, 1},
501 },
502 queryTestNode{
503 map[string]interface{}{
504 "a": int64(5),
505 "b": int64(6),
506 }, toml.Position{7, 1},
507 },
508 })
509 }
510
511 func TestQueryFilterFn(t *testing.T) {
512 buff, err := ioutil.ReadFile("../example.toml")
513 if err != nil {
514 t.Error(err)
515 return
516 }
517
518 assertQueryPositions(t, string(buff),
519 "$..[?(int)]",
520 []interface{}{
521 queryTestNode{int64(8001), toml.Position{13, 1}},
522 queryTestNode{int64(8001), toml.Position{13, 1}},
523 queryTestNode{int64(8002), toml.Position{13, 1}},
524 queryTestNode{int64(5000), toml.Position{14, 1}},
525 })
526
527 assertQueryPositions(t, string(buff),
528 "$..[?(string)]",
529 []interface{}{
530 queryTestNode{"TOML Example", toml.Position{3, 1}},
531 queryTestNode{"Tom Preston-Werner", toml.Position{6, 1}},
532 queryTestNode{"GitHub", toml.Position{7, 1}},
533 queryTestNode{"GitHub Cofounder & CEO\nLikes tater tots and beer.", toml.Position{8, 1}},
534 queryTestNode{"192.168.1.1", toml.Position{12, 1}},
535 queryTestNode{"10.0.0.1", toml.Position{21, 3}},
536 queryTestNode{"eqdc10", toml.Position{22, 3}},
537 queryTestNode{"10.0.0.2", toml.Position{25, 3}},
538 queryTestNode{"eqdc10", toml.Position{26, 3}},
539 })
540
541 assertQueryPositions(t, string(buff),
542 "$..[?(float)]",
543 []interface{}{
544 queryTestNode{4e-08, toml.Position{30, 1}},
545 })
546
547 tv, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
548 assertQueryPositions(t, string(buff),
549 "$..[?(tree)]",
550 []interface{}{
551 queryTestNode{
552 map[string]interface{}{
553 "name": "Tom Preston-Werner",
554 "organization": "GitHub",
555 "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.",
556 "dob": tv,
557 }, toml.Position{5, 1},
558 },
559 queryTestNode{
560 map[string]interface{}{
561 "server": "192.168.1.1",
562 "ports": []interface{}{int64(8001), int64(8001), int64(8002)},
563 "connection_max": int64(5000),
564 "enabled": true,
565 }, toml.Position{11, 1},
566 },
567 queryTestNode{
568 map[string]interface{}{
569 "alpha": map[string]interface{}{
570 "ip": "10.0.0.1",
571 "dc": "eqdc10",
572 },
573 "beta": map[string]interface{}{
574 "ip": "10.0.0.2",
575 "dc": "eqdc10",
576 },
577 }, toml.Position{17, 1},
578 },
579 queryTestNode{
580 map[string]interface{}{
581 "ip": "10.0.0.1",
582 "dc": "eqdc10",
583 }, toml.Position{20, 3},
584 },
585 queryTestNode{
586 map[string]interface{}{
587 "ip": "10.0.0.2",
588 "dc": "eqdc10",
589 }, toml.Position{24, 3},
590 },
591 queryTestNode{
592 map[string]interface{}{
593 "data": []interface{}{
594 []interface{}{"gamma", "delta"},
595 []interface{}{int64(1), int64(2)},
596 },
597 "score": 4e-08,
598 }, toml.Position{28, 1},
599 },
600 })
601
602 assertQueryPositions(t, string(buff),
603 "$..[?(time)]",
604 []interface{}{
605 queryTestNode{tv, toml.Position{9, 1}},
606 })
607
608 assertQueryPositions(t, string(buff),
609 "$..[?(bool)]",
610 []interface{}{
611 queryTestNode{true, toml.Position{15, 1}},
612 })
613 }
614
View as plain text