2020-11-03 23:48:43 +00:00
|
|
|
package yqlib
|
2020-10-20 02:53:26 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2020-10-30 01:00:48 +00:00
|
|
|
var mergeDocSample = `
|
|
|
|
foo: &foo
|
|
|
|
a: foo_a
|
|
|
|
thing: foo_thing
|
|
|
|
c: foo_c
|
|
|
|
|
|
|
|
bar: &bar
|
|
|
|
b: bar_b
|
|
|
|
thing: bar_thing
|
|
|
|
c: bar_c
|
|
|
|
|
|
|
|
foobarList:
|
|
|
|
b: foobarList_b
|
|
|
|
<<: [*foo,*bar]
|
|
|
|
c: foobarList_c
|
|
|
|
|
|
|
|
foobar:
|
|
|
|
c: foobar_c
|
|
|
|
<<: *foo
|
|
|
|
thing: foobar_thing
|
|
|
|
`
|
|
|
|
|
2020-10-20 02:53:26 +00:00
|
|
|
var traversePathOperatorScenarios = []expressionScenario{
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Simple map navigation",
|
|
|
|
document: `{a: {b: apple}}`,
|
|
|
|
expression: `.a`,
|
2020-10-20 02:53:26 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[a], (!!map)::{b: apple}\n",
|
|
|
|
},
|
|
|
|
},
|
2020-10-20 04:33:20 +00:00
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Splat",
|
|
|
|
subdescription: "Often used to pipe children into other operators",
|
|
|
|
document: `[{b: apple}, {c: banana}]`,
|
|
|
|
expression: `.[]`,
|
2020-10-20 04:33:20 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[0], (!!map)::{b: apple}\n",
|
|
|
|
"D0, P[1], (!!map)::{c: banana}\n",
|
|
|
|
},
|
|
|
|
},
|
2020-11-22 02:16:54 +00:00
|
|
|
{
|
|
|
|
description: "Special characters",
|
|
|
|
subdescription: "Use quotes around path elements with special characters",
|
|
|
|
document: `{"{}": frog}`,
|
|
|
|
expression: `."{}"`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[{}], (!!str)::frog\n",
|
|
|
|
},
|
|
|
|
},
|
2021-01-09 01:06:19 +00:00
|
|
|
{
|
|
|
|
description: "Dynamic keys",
|
|
|
|
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
|
|
|
|
document: `{b: apple, apple: crispy yum, banana: soft yum}`,
|
|
|
|
expression: `.[.b]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[apple], (!!str)::crispy yum\n",
|
|
|
|
},
|
|
|
|
},
|
2020-10-20 04:33:20 +00:00
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Children don't exist",
|
|
|
|
subdescription: "Nodes are added dynamically while traversing",
|
|
|
|
document: `{c: banana}`,
|
|
|
|
expression: `.a.b`,
|
2020-10-20 04:33:20 +00:00
|
|
|
expected: []string{
|
2020-10-21 01:54:58 +00:00
|
|
|
"D0, P[a b], (!!null)::null\n",
|
2020-10-20 04:33:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-12-26 10:37:08 +00:00
|
|
|
document: ``,
|
2020-10-20 04:33:20 +00:00
|
|
|
expression: `.[1].a`,
|
|
|
|
expected: []string{
|
2020-10-21 01:54:58 +00:00
|
|
|
"D0, P[1 a], (!!null)::null\n",
|
2020-10-20 04:33:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-20 04:33:20 +00:00
|
|
|
document: `{}`,
|
|
|
|
expression: `.a.[1]`,
|
|
|
|
expected: []string{
|
2020-10-21 01:54:58 +00:00
|
|
|
"D0, P[a 1], (!!null)::null\n",
|
2020-10-20 04:33:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Wildcard matching",
|
|
|
|
document: `{a: {cat: apple, mad: things}}`,
|
|
|
|
expression: `.a."*a*"`,
|
2020-10-20 04:33:20 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[a cat], (!!str)::apple\n",
|
|
|
|
"D0, P[a mad], (!!str)::things\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-20 04:33:20 +00:00
|
|
|
document: `{a: {cat: {b: 3}, mad: {b: 4}, fad: {c: t}}}`,
|
|
|
|
expression: `.a."*a*".b`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a cat b], (!!int)::3\n",
|
|
|
|
"D0, P[a mad b], (!!int)::4\n",
|
2020-10-21 01:54:58 +00:00
|
|
|
"D0, P[a fad b], (!!null)::null\n",
|
2020-10-20 04:33:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-20 04:33:20 +00:00
|
|
|
document: `{a: {cat: apple, mad: things}}`,
|
|
|
|
expression: `.a | (.cat, .mad)`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a cat], (!!str)::apple\n",
|
|
|
|
"D0, P[a mad], (!!str)::things\n",
|
|
|
|
},
|
|
|
|
},
|
2020-10-20 05:27:30 +00:00
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-20 05:27:30 +00:00
|
|
|
document: `{a: {cat: apple, mad: things}}`,
|
|
|
|
expression: `.a | (.cat, .mad, .fad)`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a cat], (!!str)::apple\n",
|
|
|
|
"D0, P[a mad], (!!str)::things\n",
|
2020-10-21 01:54:58 +00:00
|
|
|
"D0, P[a fad], (!!null)::null\n",
|
2020-10-20 05:27:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-20 05:27:30 +00:00
|
|
|
document: `{a: {cat: apple, mad: things}}`,
|
|
|
|
expression: `.a | (.cat, .mad, .fad) | select( (. == null) | not)`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a cat], (!!str)::apple\n",
|
|
|
|
"D0, P[a mad], (!!str)::things\n",
|
|
|
|
},
|
|
|
|
},
|
2020-10-29 23:56:45 +00:00
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Aliases",
|
|
|
|
document: `{a: &cat {c: frog}, b: *cat}`,
|
|
|
|
expression: `.b`,
|
2020-10-29 23:56:45 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[b], (alias)::*cat\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Traversing aliases with splat",
|
|
|
|
document: `{a: &cat {c: frog}, b: *cat}`,
|
2020-12-27 12:00:46 +00:00
|
|
|
expression: `.b[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[b c], (!!str)::frog\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: &cat {c: frog}, b: *cat}`,
|
|
|
|
expression: `.b.[]`,
|
2020-10-29 23:56:45 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[b c], (!!str)::frog\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Traversing aliases explicitly",
|
|
|
|
document: `{a: &cat {c: frog}, b: *cat}`,
|
|
|
|
expression: `.b.c`,
|
2020-10-29 23:56:45 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[b c], (!!str)::frog\n",
|
|
|
|
},
|
|
|
|
},
|
2020-11-03 23:48:43 +00:00
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Traversing arrays by index",
|
|
|
|
document: `[1,2,3]`,
|
2020-11-28 00:24:16 +00:00
|
|
|
expression: `.[0]`,
|
2020-11-03 23:48:43 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[0], (!!int)::1\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Maps with numeric keys",
|
|
|
|
document: `{2: cat}`,
|
2020-11-28 00:24:16 +00:00
|
|
|
expression: `.[2]`,
|
2020-11-17 23:32:30 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[2], (!!str)::cat\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
description: "Maps with non existing numeric keys",
|
2020-11-03 23:48:43 +00:00
|
|
|
document: `{a: b}`,
|
2020-11-28 00:24:16 +00:00
|
|
|
expression: `.[0]`,
|
2020-11-03 23:48:43 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[0], (!!null)::null\n",
|
|
|
|
},
|
|
|
|
},
|
2020-10-30 01:00:48 +00:00
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-30 01:00:48 +00:00
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobar`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobar], (!!map)::c: foobar_c\n!!merge <<: *foo\nthing: foobar_thing\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Traversing merge anchors",
|
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobar.a`,
|
2020-10-30 01:00:48 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobar a], (!!str)::foo_a\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Traversing merge anchors with override",
|
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobar.c`,
|
2020-10-30 01:00:48 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobar c], (!!str)::foo_c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Traversing merge anchors with local override",
|
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobar.thing`,
|
2020-10-30 01:00:48 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobar thing], (!!str)::foobar_thing\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Splatting merge anchors",
|
|
|
|
document: mergeDocSample,
|
2020-12-27 12:00:46 +00:00
|
|
|
expression: `.foobar[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobar c], (!!str)::foo_c\n",
|
|
|
|
"D0, P[foobar a], (!!str)::foo_a\n",
|
|
|
|
"D0, P[foobar thing], (!!str)::foobar_thing\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobar.[]`,
|
2020-10-30 01:00:48 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobar c], (!!str)::foo_c\n",
|
|
|
|
"D0, P[foobar a], (!!str)::foo_a\n",
|
|
|
|
"D0, P[foobar thing], (!!str)::foobar_thing\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-30 01:00:48 +00:00
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobarList`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobarList], (!!map)::b: foobarList_b\n!!merge <<: [*foo, *bar]\nc: foobarList_c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-30 01:00:48 +00:00
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobarList.a`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobarList a], (!!str)::foo_a\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Traversing merge anchor lists",
|
|
|
|
subdescription: "Note that the later merge anchors override previous",
|
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobarList.thing`,
|
2020-10-30 01:00:48 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobarList thing], (!!str)::bar_thing\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-30 01:00:48 +00:00
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobarList.c`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobarList c], (!!str)::foobarList_c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
skipDoc: true,
|
2020-10-30 01:00:48 +00:00
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobarList.b`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobarList b], (!!str)::bar_b\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-11-17 23:32:30 +00:00
|
|
|
description: "Splatting merge anchor lists",
|
|
|
|
document: mergeDocSample,
|
2020-12-27 12:00:46 +00:00
|
|
|
expression: `.foobarList[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobarList b], (!!str)::bar_b\n",
|
|
|
|
"D0, P[foobarList a], (!!str)::foo_a\n",
|
|
|
|
"D0, P[foobarList thing], (!!str)::bar_thing\n",
|
|
|
|
"D0, P[foobarList c], (!!str)::foobarList_c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: mergeDocSample,
|
|
|
|
expression: `.foobarList.[]`,
|
2020-10-30 01:00:48 +00:00
|
|
|
expected: []string{
|
|
|
|
"D0, P[foobarList b], (!!str)::bar_b\n",
|
|
|
|
"D0, P[foobarList a], (!!str)::foo_a\n",
|
|
|
|
"D0, P[foobarList thing], (!!str)::bar_thing\n",
|
|
|
|
"D0, P[foobarList c], (!!str)::foobarList_c\n",
|
|
|
|
},
|
|
|
|
},
|
2020-12-26 10:37:08 +00:00
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `[a,b,c]`,
|
|
|
|
expression: `.[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[0], (!!str)::a\n",
|
|
|
|
"D0, P[1], (!!str)::b\n",
|
|
|
|
"D0, P[2], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `[a,b,c]`,
|
|
|
|
expression: `[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[], (!!seq)::[]\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a[0]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a 0], (!!str)::a\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
description: "Select multiple indices",
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a[0, 2]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a 0], (!!str)::a\n",
|
|
|
|
"D0, P[a 2], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a.[0, 2]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a 0], (!!str)::a\n",
|
|
|
|
"D0, P[a 2], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a[-1]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a -1], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a.[-1]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a -1], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a[-2]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a -2], (!!str)::b\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a.[-2]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a -2], (!!str)::b\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a 0], (!!str)::a\n",
|
|
|
|
"D0, P[a 1], (!!str)::b\n",
|
|
|
|
"D0, P[a 2], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a.[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a 0], (!!str)::a\n",
|
|
|
|
"D0, P[a 1], (!!str)::b\n",
|
|
|
|
"D0, P[a 2], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
skipDoc: true,
|
|
|
|
document: `{a: [a,b,c]}`,
|
|
|
|
expression: `.a | .[]`,
|
|
|
|
expected: []string{
|
|
|
|
"D0, P[a 0], (!!str)::a\n",
|
|
|
|
"D0, P[a 1], (!!str)::b\n",
|
|
|
|
"D0, P[a 2], (!!str)::c\n",
|
|
|
|
},
|
|
|
|
},
|
2020-10-20 02:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestTraversePathOperatorScenarios(t *testing.T) {
|
|
|
|
for _, tt := range traversePathOperatorScenarios {
|
|
|
|
testScenario(t, &tt)
|
|
|
|
}
|
2020-11-22 02:16:54 +00:00
|
|
|
documentScenarios(t, "Traverse", traversePathOperatorScenarios)
|
2020-10-20 02:53:26 +00:00
|
|
|
}
|