1
0
mirror of https://github.com/TomWright/dasel.git synced 2022-05-22 02:32:45 +03:00
Files
dasel-data-selector/node_test.go
Tom Wright 9eb8a5ab6e Search selector (#43)
Implement search selector
2020-11-16 22:44:28 +00:00

722 lines
18 KiB
Go

package dasel_test
import (
"errors"
"fmt"
"github.com/tomwright/dasel"
"github.com/tomwright/dasel/internal/storage"
"reflect"
"testing"
)
var (
tom = map[string]interface{}{
"name": "Tom",
"age": 28,
}
amelia = map[string]interface{}{
"name": "Amelia",
"age": 26,
}
people = []map[string]interface{}{tom, amelia}
mapC = map[string]interface{}{
"thing": "1",
}
mapB = map[string]interface{}{
"c": mapC,
"people": people,
}
mapA = map[string]interface{}{
"b": mapB,
}
mapRoot = map[string]interface{}{
"a": mapA,
}
)
func testParseSelector(in string, exp dasel.Selector) func(t *testing.T) {
return func(t *testing.T) {
got, err := dasel.ParseSelector(in)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
if !reflect.DeepEqual(exp, got) {
t.Errorf("expected %v, got %v", exp, got)
}
}
}
func TestParseSelector(t *testing.T) {
t.Run("NonIntIndex", func(t *testing.T) {
_, err := dasel.ParseSelector(".[a]")
exp := &dasel.InvalidIndexErr{Index: "a"}
if err == nil || err.Error() != exp.Error() {
t.Errorf("expected error %v, got %v", exp, err)
}
})
t.Run("InvalidDynamicBracketCount", func(t *testing.T) {
_, err := dasel.ParseSelector(".((name=x)")
exp := dasel.ErrDynamicSelectorBracketMismatch
if err == nil || !errors.Is(err, exp) {
t.Errorf("expected error %v, got %v", exp, err)
}
})
t.Run("InvalidDynamicComparison", func(t *testing.T) {
_, err := dasel.ParseSelector(".(x<2)")
exp := &dasel.UnknownComparisonOperatorErr{Operator: "<"}
if err == nil || err.Error() != exp.Error() {
t.Errorf("expected error %v, got %v", exp, err)
}
})
t.Run("MultipleSearchGroups", func(t *testing.T) {
_, err := dasel.ParseSelector(".(?:a=b)(a=b)")
exp := "require exactly 1 group in search selector"
if err == nil || err.Error() != exp {
t.Errorf("expected error %v, got %v", exp, err)
}
})
t.Run("UnknownComparisonOperator", func(t *testing.T) {
_, err := dasel.ParseSelector(".(a>b)")
exp := "unknown comparison operator: >"
if err == nil || err.Error() != exp {
t.Errorf("expected error %v, got %v", exp, err)
}
})
t.Run("UnknownSearchComparisonOperator", func(t *testing.T) {
_, err := dasel.ParseSelector(".(?:a>b)")
exp := "unknown comparison operator: >"
if err == nil || err.Error() != exp {
t.Errorf("expected error %v, got %v", exp, err)
}
})
t.Run("UnknownSearchKeyComparisonOperator", func(t *testing.T) {
_, err := dasel.ParseSelector(".(?:->b)")
exp := "unknown comparison operator: >"
if err == nil || err.Error() != exp {
t.Errorf("expected error %v, got %v", exp, err)
}
})
t.Run("Search", testParseSelector(".(?:name=asd)", dasel.Selector{
Raw: ".(?:name=asd)",
Current: ".(?:name=asd)",
Remaining: "",
Type: "SEARCH",
Conditions: []dasel.Condition{
&dasel.EqualCondition{
Key: "name",
Value: "asd",
},
},
}))
t.Run("SearchKey", testParseSelector(".(?:-=asd)", dasel.Selector{
Raw: ".(?:-=asd)",
Current: ".(?:-=asd)",
Remaining: "",
Type: "SEARCH",
Conditions: []dasel.Condition{
&dasel.KeyEqualCondition{
Value: "asd",
},
},
}))
}
func TestNode_QueryMultiple(t *testing.T) {
value := []map[string]interface{}{
{
"name": "Tom",
"age": "27",
},
{
"name": "Jim",
"age": "27",
},
{
"name": "Amelia",
"age": "25",
},
}
extractValues := func(nodes []*dasel.Node) []interface{} {
gotValues := make([]interface{}, len(nodes))
for i, n := range nodes {
gotValues[i] = n.InterfaceValue()
}
return gotValues
}
t.Run("SingleResult", func(t *testing.T) {
nodes, err := dasel.New(value).QueryMultiple(".[0].name")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
gotValues := extractValues(nodes)
expValues := []interface{}{
"Tom",
}
if !reflect.DeepEqual(expValues, gotValues) {
t.Errorf("expected %v, got %v", expValues, gotValues)
}
})
t.Run("SingleResultDynamic", func(t *testing.T) {
nodes, err := dasel.New(value).QueryMultiple(".(age=25).name")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
gotValues := extractValues(nodes)
expValues := []interface{}{
"Amelia",
}
if !reflect.DeepEqual(expValues, gotValues) {
t.Errorf("expected %v, got %v", expValues, gotValues)
}
})
t.Run("MultipleResultDynamic", func(t *testing.T) {
nodes, err := dasel.New(value).QueryMultiple(".(age=27).name")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
gotValues := extractValues(nodes)
expValues := []interface{}{
"Tom",
"Jim",
}
if !reflect.DeepEqual(expValues, gotValues) {
t.Errorf("expected %v, got %v", expValues, gotValues)
}
})
t.Run("MultipleResultAnyIndex", func(t *testing.T) {
nodes, err := dasel.New(value).QueryMultiple(".[*].name")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
gotValues := extractValues(nodes)
expValues := []interface{}{
"Tom",
"Jim",
"Amelia",
}
if !reflect.DeepEqual(expValues, gotValues) {
t.Errorf("expected %v, got %v", expValues, gotValues)
}
})
}
func TestNode_PutMultiple(t *testing.T) {
t.Run("SingleResult", func(t *testing.T) {
value := []map[string]interface{}{
{
"name": "Tom",
"age": "27",
},
{
"name": "Jim",
"age": "27",
},
{
"name": "Amelia",
"age": "25",
},
}
err := dasel.New(value).PutMultiple(".[0].name", "Frank")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
exp := []map[string]interface{}{
{
"name": "Frank",
"age": "27",
},
{
"name": "Jim",
"age": "27",
},
{
"name": "Amelia",
"age": "25",
},
}
if !reflect.DeepEqual(exp, value) {
t.Errorf("expected %v, got %v", exp, value)
}
})
t.Run("SingleResultDynamic", func(t *testing.T) {
value := []map[string]interface{}{
{
"name": "Frank",
"age": "27",
},
{
"name": "Jim",
"age": "27",
},
{
"name": "Amelia",
"age": "25",
},
}
err := dasel.New(value).PutMultiple(".(age=25).name", "Frank")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
exp := []map[string]interface{}{
{
"name": "Frank",
"age": "27",
},
{
"name": "Jim",
"age": "27",
},
{
"name": "Frank",
"age": "25",
},
}
if !reflect.DeepEqual(exp, value) {
t.Errorf("expected %v, got %v", exp, value)
}
})
t.Run("MultipleResultDynamic", func(t *testing.T) {
value := []map[string]interface{}{
{
"name": "Tom",
"age": "27",
},
{
"name": "Jim",
"age": "27",
},
{
"name": "Amelia",
"age": "25",
},
}
err := dasel.New(value).PutMultiple(".(age=27).name", "Frank")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
exp := []map[string]interface{}{
{
"name": "Frank",
"age": "27",
},
{
"name": "Frank",
"age": "27",
},
{
"name": "Amelia",
"age": "25",
},
}
if !reflect.DeepEqual(exp, value) {
t.Errorf("expected %v, got %v", exp, value)
}
})
t.Run("MultipleResultAnyIndex", func(t *testing.T) {
value := []map[string]interface{}{
{
"name": "Tom",
"age": "27",
},
{
"name": "Jim",
"age": "27",
},
{
"name": "Amelia",
"age": "25",
},
}
err := dasel.New(value).PutMultiple(".[*].name", "Frank")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
exp := []map[string]interface{}{
{
"name": "Frank",
"age": "27",
},
{
"name": "Frank",
"age": "27",
},
{
"name": "Frank",
"age": "25",
},
}
if !reflect.DeepEqual(exp, value) {
t.Errorf("expected %v, got %v", exp, value)
}
})
}
func TestNode_Query(t *testing.T) {
parser, err := storage.NewParserFromFilename("./tests/assets/example.json")
if err != nil {
t.Errorf("could not get parser: %s", err)
return
}
value, err := storage.LoadFromFile("./tests/assets/example.json", parser)
if err != nil {
t.Errorf("could not load value from file: %s", err)
return
}
t.Run("Valid", func(t *testing.T) {
node, err := dasel.New(value).Query("preferences.favouriteColour")
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
if exp, got := "red", fmt.Sprint(node.InterfaceValue()); exp != got {
t.Errorf("expected value `%s`, got `%s`", exp, got)
}
})
t.Run("NotFound", func(t *testing.T) {
_, err := dasel.New(value).Query(".colours.[0].a")
expErr := fmt.Errorf("could not find value: selector [PROPERTY] does not support value: reflect.Kind: interface")
if err == nil {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
if err.Error() != expErr.Error() {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
})
t.Run("InvalidSelector", func(t *testing.T) {
_, err := dasel.New(value).Query(".colours.[a]")
expErr := fmt.Errorf("failed to parse selector: %w", &dasel.InvalidIndexErr{Index: "a"})
if err == nil {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
if err.Error() != expErr.Error() {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
})
}
func TestNode_Query_File(t *testing.T) {
tests := []struct {
Name string
Selector string
Exp string
}{
{Name: "Property", Selector: "name", Exp: "Tom"},
{Name: "ChildProperty", Selector: "preferences.favouriteColour", Exp: "red"},
{Name: "Index", Selector: "colours.[0]", Exp: "red"},
{Name: "Index", Selector: "colours.[1]", Exp: "green"},
{Name: "Index", Selector: "colours.[2]", Exp: "blue"},
{Name: "IndexProperty", Selector: "colourCodes.[0].name", Exp: "red"},
{Name: "IndexProperty", Selector: "colourCodes.[1].name", Exp: "green"},
{Name: "IndexProperty", Selector: "colourCodes.[2].name", Exp: "blue"},
{Name: "DynamicProperty", Selector: "colourCodes.(name=red).rgb", Exp: "ff0000"},
{Name: "DynamicProperty", Selector: "colourCodes.(name=green).rgb", Exp: "00ff00"},
{Name: "DynamicProperty", Selector: "colourCodes.(name=blue).rgb", Exp: "0000ff"},
{Name: "MultipleDynamicProperty", Selector: "colourCodes.(name=red)(rgb=ff0000).name", Exp: "red"},
{Name: "MultipleDynamicProperty", Selector: "colourCodes.(name=green)(rgb=00ff00).name", Exp: "green"},
{Name: "MultipleDynamicProperty", Selector: "colourCodes.(name=blue)(rgb=0000ff).name", Exp: "blue"},
}
fileTest := func(filename string) func(t *testing.T) {
return func(t *testing.T) {
parser, err := storage.NewParserFromFilename(filename)
if err != nil {
t.Errorf("could not get parser: %s", err)
return
}
value, err := storage.LoadFromFile(filename, parser)
if err != nil {
t.Errorf("could not load value from file: %s", err)
return
}
for _, testCase := range tests {
tc := testCase
t.Run(tc.Name, func(t *testing.T) {
node, err := dasel.New(value).Query(tc.Selector)
if err != nil {
t.Errorf("unexpected query error: %s", err)
return
}
if exp, got := tc.Exp, fmt.Sprint(node.InterfaceValue()); exp != got {
t.Errorf("expected value `%s`, got `%s`", exp, got)
}
})
}
}
}
t.Run("JSON", fileTest("./tests/assets/example.json"))
t.Run("YAML", fileTest("./tests/assets/example.yaml"))
}
func TestNode_Query_Data(t *testing.T) {
t.Run("ParentChildPathToProperty", func(t *testing.T) {
rootNode := dasel.New(mapRoot)
got, err := rootNode.Query(".a.b.c.thing")
if err != nil {
t.Errorf("unexpected query error: %v", err)
return
}
if exp, got := "1", got.InterfaceValue().(string); exp != got {
t.Errorf("expected %s, got %s", exp, got)
}
})
t.Run("ParentChildPathToIndexProperty", func(t *testing.T) {
rootNode := dasel.New(mapRoot)
got, err := rootNode.Query(".a.b.people.[1].name")
if err != nil {
t.Errorf("unexpected query error: %v", err)
return
}
if exp, got := "Amelia", got.InterfaceValue().(string); exp != got {
t.Errorf("expected %s, got %s", exp, got)
}
})
t.Run("ParentChildPathToDynamicProperty", func(t *testing.T) {
rootNode := dasel.New(mapRoot)
got, err := rootNode.Query(".a.b.people.(name=Tom).name")
if err != nil {
t.Errorf("unexpected query error: %v", err)
return
}
if exp, got := "Tom", got.InterfaceValue().(string); exp != got {
t.Errorf("expected %s, got %s", exp, got)
}
})
t.Run("ParentChildPathToMultipleDynamicProperty", func(t *testing.T) {
rootNode := dasel.New(mapRoot)
got, err := rootNode.Query(".a.b.people.(name=Tom)(age=28).name")
if err != nil {
t.Errorf("unexpected query error: %v", err)
return
}
if exp, got := "Tom", got.InterfaceValue().(string); exp != got {
t.Errorf("expected %s, got %s", exp, got)
}
})
}
func putQueryTest(rootNode *dasel.Node, putSelector string, newValue interface{}, querySelector string) func(t *testing.T) {
return func(t *testing.T) {
err := rootNode.Put(putSelector, newValue)
if err != nil {
t.Errorf("unexpected put error: %v", err)
return
}
got, err := rootNode.Query(querySelector)
if err != nil {
t.Errorf("unexpected query error: %v", err)
return
}
if !reflect.DeepEqual(newValue, got.InterfaceValue()) {
t.Errorf("expected %v, got %v", newValue, got.InterfaceValue())
}
}
}
func putQueryMultipleTest(rootNode *dasel.Node, putSelector string, newValue interface{}, querySelector string) func(t *testing.T) {
return func(t *testing.T) {
err := rootNode.PutMultiple(putSelector, newValue)
if err != nil {
t.Errorf("unexpected put error: %v", err)
return
}
got, err := rootNode.QueryMultiple(querySelector)
if err != nil {
t.Errorf("unexpected query error: %v", err)
return
}
for _, n := range got {
if !reflect.DeepEqual(newValue, n.InterfaceValue()) {
t.Errorf("expected %v, got %v", newValue, n.InterfaceValue())
}
}
}
}
func TestNode_Put_Query(t *testing.T) {
data := map[string]interface{}{
"id": "123",
"people": []map[string]interface{}{
{
"id": 1,
"name": "Tom",
},
{
"id": 2,
"name": "Jim",
},
},
"names": []string{
"Tom",
"Jim",
},
}
rootNode := dasel.New(data)
t.Run("InvalidSelector", func(t *testing.T) {
err := rootNode.Put("people.[a].name", "Thomas")
expErr := fmt.Errorf("failed to parse selector: %w", &dasel.InvalidIndexErr{Index: "a"})
if err == nil {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
if err.Error() != expErr.Error() {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
})
t.Run("ExistingSingleString", putQueryTest(rootNode, "id", "456", "id"))
t.Run("ExistingStringValue", putQueryTest(rootNode, "people.[0].name", "Thomas", "people.(id=1).name"))
t.Run("ExistingIntValue", putQueryTest(rootNode, "people.(id=1).id", 3, "people.(id=3).id"))
t.Run("NewPropertyOnExistingObject", putQueryTest(rootNode, "people.(id=3).age", 27, "people.[0].age"))
t.Run("AppendObjectToList", func(t *testing.T) {
err := rootNode.Put("people.[]", map[string]interface{}{
"id": 1,
"name": "Bob",
})
if err != nil {
t.Errorf("unexpected put error: %v", err)
return
}
got, err := rootNode.Query("people.[2].id")
if err != nil {
t.Errorf("unexpected query [1] error: %v", err)
return
}
if exp, got := 1, got.InterfaceValue().(int); exp != got {
t.Errorf("expected %d, got %d", exp, got)
}
got, err = rootNode.Query("people.[2].name")
if err != nil {
t.Errorf("unexpected query [2] error: %v", err)
return
}
if exp, got := "Bob", got.InterfaceValue().(string); exp != got {
t.Errorf("expected %s, got %s", exp, got)
}
})
t.Run("AppendStringToList", putQueryTest(rootNode, "names.[]", "Bob", "names.[2]"))
t.Run("NilRootNode", putQueryTest(dasel.New(nil), "name", "Thomas", "name"))
t.Run("NilChain", putQueryTest(dasel.New(nil), "my.name", "Thomas", "my.name"))
t.Run("NilChainToListIndex", putQueryTest(dasel.New(nil), "my.favourite.people.[0]", "Tom", "my.favourite.people.[0]"))
t.Run("NilChainToListNextAvailableIndex", putQueryTest(dasel.New(nil), "my.favourite.people.[]", "Tom", "my.favourite.people.[0]"))
t.Run("NilChainToDynamic", putQueryTest(dasel.New(nil), "(name=Jim).name", "Tom", "[0].name"))
}
func TestNode_PutMultiple_Query(t *testing.T) {
data := map[string]interface{}{
"id": "123",
"people": []map[string]interface{}{
{
"id": 1,
"name": "Tom",
},
{
"id": 2,
"name": "Jim",
},
},
"names": []string{
"Tom",
"Jim",
},
}
rootNode := dasel.New(data)
t.Run("InvalidSelector", func(t *testing.T) {
err := rootNode.PutMultiple("people.[a].name", "Thomas")
expErr := fmt.Errorf("failed to parse selector: %w", &dasel.InvalidIndexErr{Index: "a"})
if err == nil {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
if err.Error() != expErr.Error() {
t.Errorf("expected err %v, got %v", expErr, err)
return
}
})
t.Run("ExistingSingleString", putQueryMultipleTest(rootNode, "id", "456", "id"))
t.Run("ExistingStringValue", putQueryMultipleTest(rootNode, "people.[0].name", "Thomas", "people.(id=1).name"))
t.Run("ExistingIntValue", putQueryMultipleTest(rootNode, "people.(id=1).id", 3, "people.(id=3).id"))
t.Run("NewPropertyOnExistingObject", putQueryMultipleTest(rootNode, "people.(id=3).age", 27, "people.[0].age"))
t.Run("AppendObjectToList", func(t *testing.T) {
err := rootNode.PutMultiple("people.[]", map[string]interface{}{
"id": 1,
"name": "Bob",
})
if err != nil {
t.Errorf("unexpected put error: %v", err)
return
}
got, err := rootNode.Query("people.[2].id")
if err != nil {
t.Errorf("unexpected query [1] error: %v", err)
return
}
if exp, got := 1, got.InterfaceValue().(int); exp != got {
t.Errorf("expected %d, got %d", exp, got)
}
got, err = rootNode.Query("people.[2].name")
if err != nil {
t.Errorf("unexpected query [2] error: %v", err)
return
}
if exp, got := "Bob", got.InterfaceValue().(string); exp != got {
t.Errorf("expected %s, got %s", exp, got)
}
})
t.Run("AppendStringToList", putQueryMultipleTest(rootNode, "names.[]", "Bob", "names.[2]"))
t.Run("NilRootNode", putQueryMultipleTest(dasel.New(nil), "name", "Thomas", "name"))
t.Run("NilChain", putQueryMultipleTest(dasel.New(nil), "my.name", "Thomas", "my.name"))
t.Run("NilChainToListIndex", putQueryMultipleTest(dasel.New(nil), "my.favourite.people.[0]", "Tom", "my.favourite.people.[0]"))
t.Run("NilChainToListNextAvailableIndex", putQueryMultipleTest(dasel.New(nil), "my.favourite.people.[]", "Tom", "my.favourite.people.[0]"))
t.Run("NilChainToDynamic", putQueryMultipleTest(dasel.New(nil), "(name=Jim).name", "Tom", "[0].name"))
}