mirror of
https://github.com/mikefarah/yq.git
synced 2026-07-06 21:55:37 +00:00
Moar tests
This commit is contained in:
parent
a98921213f
commit
b9d9e2fbad
@ -2,9 +2,11 @@ package yqlib
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mikefarah/yq/v4/test"
|
"github.com/mikefarah/yq/v4/test"
|
||||||
|
logging "gopkg.in/op/go-logging.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChildContext(t *testing.T) {
|
func TestChildContext(t *testing.T) {
|
||||||
@ -49,3 +51,211 @@ func TestChildContextNoVariables(t *testing.T) {
|
|||||||
test.AssertResultComplex(t, make(map[string]*list.List), clone.Variables)
|
test.AssertResultComplex(t, make(map[string]*list.List), clone.Variables)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSingleReadonlyChildContext(t *testing.T) {
|
||||||
|
original := Context{
|
||||||
|
DontAutoCreate: false,
|
||||||
|
datetimeLayout: "2006-01-02",
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate := &CandidateNode{Value: "test"}
|
||||||
|
clone := original.SingleReadonlyChildContext(candidate)
|
||||||
|
|
||||||
|
// Should have DontAutoCreate set to true
|
||||||
|
test.AssertResultComplex(t, true, clone.DontAutoCreate)
|
||||||
|
|
||||||
|
// Should have the candidate node in MatchingNodes
|
||||||
|
test.AssertResultComplex(t, 1, clone.MatchingNodes.Len())
|
||||||
|
test.AssertResultComplex(t, candidate, clone.MatchingNodes.Front().Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleChildContext(t *testing.T) {
|
||||||
|
original := Context{
|
||||||
|
DontAutoCreate: true,
|
||||||
|
datetimeLayout: "2006-01-02",
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate := &CandidateNode{Value: "test"}
|
||||||
|
clone := original.SingleChildContext(candidate)
|
||||||
|
|
||||||
|
// Should preserve DontAutoCreate
|
||||||
|
test.AssertResultComplex(t, true, clone.DontAutoCreate)
|
||||||
|
|
||||||
|
// Should have the candidate node in MatchingNodes
|
||||||
|
test.AssertResultComplex(t, 1, clone.MatchingNodes.Len())
|
||||||
|
test.AssertResultComplex(t, candidate, clone.MatchingNodes.Front().Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetDateTimeLayout(t *testing.T) {
|
||||||
|
context := Context{}
|
||||||
|
|
||||||
|
// Test setting datetime layout
|
||||||
|
context.SetDateTimeLayout("2006-01-02T15:04:05Z07:00")
|
||||||
|
test.AssertResultComplex(t, "2006-01-02T15:04:05Z07:00", context.datetimeLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDateTimeLayout(t *testing.T) {
|
||||||
|
// Test with custom layout
|
||||||
|
context := Context{datetimeLayout: "2006-01-02"}
|
||||||
|
result := context.GetDateTimeLayout()
|
||||||
|
test.AssertResultComplex(t, "2006-01-02", result)
|
||||||
|
|
||||||
|
// Test with empty layout (should return default)
|
||||||
|
context = Context{}
|
||||||
|
result = context.GetDateTimeLayout()
|
||||||
|
test.AssertResultComplex(t, "2006-01-02T15:04:05Z07:00", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetVariable(t *testing.T) {
|
||||||
|
// Test with nil Variables
|
||||||
|
context := Context{}
|
||||||
|
result := context.GetVariable("nonexistent")
|
||||||
|
test.AssertResultComplex(t, (*list.List)(nil), result)
|
||||||
|
|
||||||
|
// Test with existing variable
|
||||||
|
variables := make(map[string]*list.List)
|
||||||
|
variables["test"] = list.New()
|
||||||
|
variables["test"].PushBack(&CandidateNode{Value: "value"})
|
||||||
|
|
||||||
|
context = Context{Variables: variables}
|
||||||
|
result = context.GetVariable("test")
|
||||||
|
test.AssertResultComplex(t, variables["test"], result)
|
||||||
|
|
||||||
|
// Test with non-existent variable
|
||||||
|
result = context.GetVariable("nonexistent")
|
||||||
|
test.AssertResultComplex(t, (*list.List)(nil), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetVariable(t *testing.T) {
|
||||||
|
// Test setting variable when Variables is nil
|
||||||
|
context := Context{}
|
||||||
|
value := list.New()
|
||||||
|
value.PushBack(&CandidateNode{Value: "test"})
|
||||||
|
|
||||||
|
context.SetVariable("key", value)
|
||||||
|
test.AssertResultComplex(t, value, context.Variables["key"])
|
||||||
|
|
||||||
|
// Test setting variable when Variables already exists
|
||||||
|
context.SetVariable("key2", value)
|
||||||
|
test.AssertResultComplex(t, value, context.Variables["key2"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToString(t *testing.T) {
|
||||||
|
context := Context{
|
||||||
|
DontAutoCreate: true,
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a node to test the full string representation
|
||||||
|
node := &CandidateNode{Value: "test"}
|
||||||
|
context.MatchingNodes.PushBack(node)
|
||||||
|
|
||||||
|
// Test with debug logging disabled (default)
|
||||||
|
result := context.ToString()
|
||||||
|
test.AssertResultComplex(t, "", result)
|
||||||
|
|
||||||
|
// Test with debug logging enabled
|
||||||
|
logging.SetLevel(logging.DEBUG, "")
|
||||||
|
defer logging.SetLevel(logging.INFO, "") // Reset to default
|
||||||
|
|
||||||
|
result2 := context.ToString()
|
||||||
|
test.AssertResultComplex(t, true, len(result2) > 0)
|
||||||
|
test.AssertResultComplex(t, true, strings.Contains(result2, "Context"))
|
||||||
|
test.AssertResultComplex(t, true, strings.Contains(result2, "DontAutoCreate: true"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeepClone(t *testing.T) {
|
||||||
|
// Create original context with variables and matching nodes
|
||||||
|
originalVariables := make(map[string]*list.List)
|
||||||
|
originalVariables["test"] = list.New()
|
||||||
|
originalVariables["test"].PushBack(&CandidateNode{Value: "original"})
|
||||||
|
|
||||||
|
original := Context{
|
||||||
|
DontAutoCreate: true,
|
||||||
|
datetimeLayout: "2006-01-02",
|
||||||
|
Variables: originalVariables,
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a node to MatchingNodes
|
||||||
|
node := &CandidateNode{Value: "test"}
|
||||||
|
original.MatchingNodes.PushBack(node)
|
||||||
|
|
||||||
|
clone := original.DeepClone()
|
||||||
|
|
||||||
|
// Should preserve DontAutoCreate and datetimeLayout
|
||||||
|
test.AssertResultComplex(t, original.DontAutoCreate, clone.DontAutoCreate)
|
||||||
|
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||||
|
|
||||||
|
// Should have copied variables
|
||||||
|
test.AssertResultComplex(t, 1, len(clone.Variables))
|
||||||
|
test.AssertResultComplex(t, "original", clone.Variables["test"].Front().Value.(*CandidateNode).Value)
|
||||||
|
|
||||||
|
// Should have deep copied MatchingNodes
|
||||||
|
test.AssertResultComplex(t, 1, clone.MatchingNodes.Len())
|
||||||
|
|
||||||
|
// Verify it's a deep copy by modifying the original
|
||||||
|
original.MatchingNodes.Front().Value.(*CandidateNode).Value = "modified"
|
||||||
|
test.AssertResultComplex(t, "test", clone.MatchingNodes.Front().Value.(*CandidateNode).Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClone(t *testing.T) {
|
||||||
|
// Create original context
|
||||||
|
original := Context{
|
||||||
|
DontAutoCreate: true,
|
||||||
|
datetimeLayout: "2006-01-02",
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &CandidateNode{Value: "test"}
|
||||||
|
original.MatchingNodes.PushBack(node)
|
||||||
|
|
||||||
|
clone := original.Clone()
|
||||||
|
|
||||||
|
// Should preserve DontAutoCreate and datetimeLayout
|
||||||
|
test.AssertResultComplex(t, original.DontAutoCreate, clone.DontAutoCreate)
|
||||||
|
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||||
|
|
||||||
|
// Should have the same MatchingNodes reference
|
||||||
|
test.AssertResultComplex(t, original.MatchingNodes, clone.MatchingNodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadOnlyClone(t *testing.T) {
|
||||||
|
original := Context{
|
||||||
|
DontAutoCreate: false,
|
||||||
|
datetimeLayout: "2006-01-02",
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &CandidateNode{Value: "test"}
|
||||||
|
original.MatchingNodes.PushBack(node)
|
||||||
|
|
||||||
|
clone := original.ReadOnlyClone()
|
||||||
|
|
||||||
|
// Should set DontAutoCreate to true
|
||||||
|
test.AssertResultComplex(t, true, clone.DontAutoCreate)
|
||||||
|
|
||||||
|
// Should preserve other fields
|
||||||
|
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||||
|
test.AssertResultComplex(t, original.MatchingNodes, clone.MatchingNodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWritableClone(t *testing.T) {
|
||||||
|
original := Context{
|
||||||
|
DontAutoCreate: true,
|
||||||
|
datetimeLayout: "2006-01-02",
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
node := &CandidateNode{Value: "test"}
|
||||||
|
original.MatchingNodes.PushBack(node)
|
||||||
|
|
||||||
|
clone := original.WritableClone()
|
||||||
|
|
||||||
|
// Should set DontAutoCreate to false
|
||||||
|
test.AssertResultComplex(t, false, clone.DontAutoCreate)
|
||||||
|
|
||||||
|
// Should preserve other fields
|
||||||
|
test.AssertResultComplex(t, original.datetimeLayout, clone.datetimeLayout)
|
||||||
|
test.AssertResultComplex(t, original.MatchingNodes, clone.MatchingNodes)
|
||||||
|
}
|
||||||
|
|||||||
@ -64,6 +64,6 @@ func (d *dataTreeNavigator) GetMatchingNodes(context Context, expressionNode *Ex
|
|||||||
if handler != nil {
|
if handler != nil {
|
||||||
return handler(d, context, expressionNode)
|
return handler(d, context, expressionNode)
|
||||||
}
|
}
|
||||||
return Context{}, fmt.Errorf("unknown operator %v", expressionNode.Operation.OperationType)
|
return Context{}, fmt.Errorf("unknown operator %v", expressionNode.Operation.OperationType.Type)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
437
pkg/yqlib/data_tree_navigator_test.go
Normal file
437
pkg/yqlib/data_tree_navigator_test.go
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mikefarah/yq/v4/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetMatchingNodes_NilExpressionNode(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := navigator.GetMatchingNodes(context, nil)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
test.AssertResultComplex(t, context, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMatchingNodes_UnknownOperator(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an expression node with an unknown operation type
|
||||||
|
unknownOpType := &operationType{Type: "UNKNOWN", Handler: nil}
|
||||||
|
expressionNode := &ExpressionNode{
|
||||||
|
Operation: &Operation{OperationType: unknownOpType},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, "unknown operator UNKNOWN", err.Error())
|
||||||
|
test.AssertResultComplex(t, Context{}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMatchingNodes_ValidOperator(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a simple context with a scalar node
|
||||||
|
scalarNode := &CandidateNode{
|
||||||
|
Kind: ScalarNode,
|
||||||
|
Tag: "!!str",
|
||||||
|
Value: "test",
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(scalarNode)
|
||||||
|
|
||||||
|
// Create an expression node with a valid operation (self reference)
|
||||||
|
expressionNode := &ExpressionNode{
|
||||||
|
Operation: &Operation{OperationType: selfReferenceOpType},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
test.AssertResult(t, 1, result.MatchingNodes.Len())
|
||||||
|
|
||||||
|
// Verify the result contains the same node
|
||||||
|
resultNode := result.MatchingNodes.Front().Value.(*CandidateNode)
|
||||||
|
test.AssertResult(t, scalarNode, resultNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeeplyAssign_ScalarNode(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with a root mapping node
|
||||||
|
rootNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "existing", IsMapKey: true},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "old_value"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(rootNode)
|
||||||
|
|
||||||
|
// Create a scalar node to assign
|
||||||
|
scalarNode := &CandidateNode{
|
||||||
|
Kind: ScalarNode,
|
||||||
|
Tag: "!!str",
|
||||||
|
Value: "new_value",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to path ["new_key"]
|
||||||
|
path := []interface{}{"new_key"}
|
||||||
|
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
|
||||||
|
// Verify the assignment was made
|
||||||
|
// The root node should now have the new key-value pair
|
||||||
|
test.AssertResult(t, 4, len(rootNode.Content)) // 2 original + 2 new
|
||||||
|
|
||||||
|
// Find the new key-value pair
|
||||||
|
found := false
|
||||||
|
for i := 0; i < len(rootNode.Content)-1; i += 2 {
|
||||||
|
key := rootNode.Content[i]
|
||||||
|
value := rootNode.Content[i+1]
|
||||||
|
if key.Value == "new_key" && value.Value == "new_value" {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test.AssertResult(t, true, found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeeplyAssign_MappingNode(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with a root mapping node
|
||||||
|
rootNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "existing", IsMapKey: true},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "old_value"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(rootNode)
|
||||||
|
|
||||||
|
// Create a mapping node to assign (this should trigger deep merge)
|
||||||
|
mappingNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "nested_key", IsMapKey: true},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "nested_value"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to path ["new_map"]
|
||||||
|
path := []interface{}{"new_map"}
|
||||||
|
err := navigator.DeeplyAssign(context, path, mappingNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
|
||||||
|
// Verify the assignment was made
|
||||||
|
// The root node should now have the new mapping
|
||||||
|
test.AssertResult(t, 4, len(rootNode.Content)) // 2 original + 2 new
|
||||||
|
|
||||||
|
// Find the new mapping
|
||||||
|
found := false
|
||||||
|
for i := 0; i < len(rootNode.Content); i += 2 {
|
||||||
|
if i+1 < len(rootNode.Content) {
|
||||||
|
key := rootNode.Content[i]
|
||||||
|
value := rootNode.Content[i+1]
|
||||||
|
if key.Value == "new_map" && value.Kind == MappingNode {
|
||||||
|
found = true
|
||||||
|
// Verify the nested content
|
||||||
|
test.AssertResult(t, 2, len(value.Content))
|
||||||
|
test.AssertResult(t, "nested_key", value.Content[0].Value)
|
||||||
|
test.AssertResult(t, "nested_value", value.Content[1].Value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test.AssertResult(t, true, found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeeplyAssign_DeepPath(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with a root mapping node
|
||||||
|
rootNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "level1", IsMapKey: true},
|
||||||
|
{Kind: MappingNode, Tag: "!!map", Content: []*CandidateNode{}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(rootNode)
|
||||||
|
|
||||||
|
// Create a scalar node to assign
|
||||||
|
scalarNode := &CandidateNode{
|
||||||
|
Kind: ScalarNode,
|
||||||
|
Tag: "!!str",
|
||||||
|
Value: "deep_value",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to deep path ["level1", "level2", "level3"]
|
||||||
|
path := []interface{}{"level1", "level2", "level3"}
|
||||||
|
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
|
||||||
|
// Verify the deep assignment was made
|
||||||
|
level1Node := rootNode.Content[1] // The mapping node
|
||||||
|
test.AssertResult(t, 2, len(level1Node.Content)) // Should have level2 key-value
|
||||||
|
|
||||||
|
level2Key := level1Node.Content[0]
|
||||||
|
level2Value := level1Node.Content[1]
|
||||||
|
test.AssertResult(t, "level2", level2Key.Value)
|
||||||
|
test.AssertResult(t, MappingNode, level2Value.Kind)
|
||||||
|
|
||||||
|
level3Key := level2Value.Content[0]
|
||||||
|
level3Value := level2Value.Content[1]
|
||||||
|
test.AssertResult(t, "level3", level3Key.Value)
|
||||||
|
test.AssertResult(t, "deep_value", level3Value.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeeplyAssign_ArrayPath(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with a root mapping node containing an array
|
||||||
|
rootNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "array", IsMapKey: true},
|
||||||
|
{Kind: SequenceNode, Tag: "!!seq", Content: []*CandidateNode{}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(rootNode)
|
||||||
|
|
||||||
|
// Create a scalar node to assign
|
||||||
|
scalarNode := &CandidateNode{
|
||||||
|
Kind: ScalarNode,
|
||||||
|
Tag: "!!str",
|
||||||
|
Value: "array_value",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to array path ["array", 0]
|
||||||
|
path := []interface{}{"array", 0}
|
||||||
|
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
|
||||||
|
// Verify the array assignment was made
|
||||||
|
arrayNode := rootNode.Content[1] // The sequence node
|
||||||
|
test.AssertResult(t, 1, len(arrayNode.Content)) // Should have one element
|
||||||
|
|
||||||
|
arrayElement := arrayNode.Content[0]
|
||||||
|
test.AssertResult(t, "array_value", arrayElement.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeeplyAssign_OverwriteExisting(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with a root mapping node
|
||||||
|
rootNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "key", IsMapKey: true},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "old_value"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(rootNode)
|
||||||
|
|
||||||
|
// Create a scalar node to assign
|
||||||
|
scalarNode := &CandidateNode{
|
||||||
|
Kind: ScalarNode,
|
||||||
|
Tag: "!!str",
|
||||||
|
Value: "new_value",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to existing path ["key"]
|
||||||
|
path := []interface{}{"key"}
|
||||||
|
err := navigator.DeeplyAssign(context, path, scalarNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
|
||||||
|
// Verify the value was overwritten
|
||||||
|
test.AssertResult(t, 2, len(rootNode.Content)) // Should still have 2 elements
|
||||||
|
|
||||||
|
key := rootNode.Content[0]
|
||||||
|
value := rootNode.Content[1]
|
||||||
|
test.AssertResult(t, "key", key.Value)
|
||||||
|
test.AssertResult(t, "new_value", value.Value) // Should be overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeeplyAssign_ErrorHandling(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with a scalar node (not a mapping)
|
||||||
|
scalarNode := &CandidateNode{
|
||||||
|
Kind: ScalarNode,
|
||||||
|
Tag: "!!str",
|
||||||
|
Value: "not_a_map",
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(scalarNode)
|
||||||
|
|
||||||
|
// Create a scalar node to assign
|
||||||
|
assignNode := &CandidateNode{
|
||||||
|
Kind: ScalarNode,
|
||||||
|
Tag: "!!str",
|
||||||
|
Value: "value",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to assign to a path on a scalar (should fail)
|
||||||
|
path := []interface{}{"key"}
|
||||||
|
err := navigator.DeeplyAssign(context, path, assignNode)
|
||||||
|
|
||||||
|
// Print the actual error for debugging
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Actual error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should fail because we can't assign to a scalar
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMatchingNodes_WithVariables(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with variables
|
||||||
|
variables := make(map[string]*list.List)
|
||||||
|
varList := list.New()
|
||||||
|
varList.PushBack(&CandidateNode{Kind: ScalarNode, Tag: "!!str", Value: "var_value"})
|
||||||
|
variables["test_var"] = varList
|
||||||
|
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
Variables: variables,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an expression node that gets a variable
|
||||||
|
expressionNode := &ExpressionNode{
|
||||||
|
Operation: &Operation{OperationType: getVariableOpType, StringValue: "test_var"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
test.AssertResult(t, 1, result.MatchingNodes.Len())
|
||||||
|
|
||||||
|
// Verify the variable was retrieved
|
||||||
|
resultNode := result.MatchingNodes.Front().Value.(*CandidateNode)
|
||||||
|
test.AssertResult(t, "var_value", resultNode.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMatchingNodes_EmptyContext(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create an empty context
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an expression node with self reference
|
||||||
|
expressionNode := &ExpressionNode{
|
||||||
|
Operation: &Operation{OperationType: selfReferenceOpType},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := navigator.GetMatchingNodes(context, expressionNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
test.AssertResult(t, 0, result.MatchingNodes.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeeplyAssign_ComplexMappingMerge(t *testing.T) {
|
||||||
|
navigator := NewDataTreeNavigator()
|
||||||
|
|
||||||
|
// Create a context with a root mapping node containing nested data
|
||||||
|
rootNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "config", IsMapKey: true},
|
||||||
|
{Kind: MappingNode, Tag: "!!map", Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "existing_key", IsMapKey: true},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "existing_value"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
context := Context{
|
||||||
|
MatchingNodes: list.New(),
|
||||||
|
}
|
||||||
|
context.MatchingNodes.PushBack(rootNode)
|
||||||
|
|
||||||
|
// Create a mapping node to merge
|
||||||
|
mappingNode := &CandidateNode{
|
||||||
|
Kind: MappingNode,
|
||||||
|
Tag: "!!map",
|
||||||
|
Content: []*CandidateNode{
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "new_key", IsMapKey: true},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "new_value"},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "existing_key", IsMapKey: true},
|
||||||
|
{Kind: ScalarNode, Tag: "!!str", Value: "updated_value"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to path ["config"] (should merge with existing mapping)
|
||||||
|
path := []interface{}{"config"}
|
||||||
|
err := navigator.DeeplyAssign(context, path, mappingNode)
|
||||||
|
|
||||||
|
test.AssertResult(t, nil, err)
|
||||||
|
|
||||||
|
// Verify the merge was successful
|
||||||
|
configNode := rootNode.Content[1] // The config mapping node
|
||||||
|
test.AssertResult(t, 4, len(configNode.Content)) // Should have 2 key-value pairs
|
||||||
|
|
||||||
|
// Check that both existing and new keys are present
|
||||||
|
foundExisting := false
|
||||||
|
foundNew := false
|
||||||
|
for i := 0; i < len(configNode.Content); i += 2 {
|
||||||
|
if i+1 < len(configNode.Content) {
|
||||||
|
key := configNode.Content[i]
|
||||||
|
value := configNode.Content[i+1]
|
||||||
|
switch key.Value {
|
||||||
|
case "existing_key":
|
||||||
|
foundExisting = true
|
||||||
|
test.AssertResult(t, "updated_value", value.Value) // Should be updated
|
||||||
|
case "new_key":
|
||||||
|
foundNew = true
|
||||||
|
test.AssertResult(t, "new_value", value.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test.AssertResult(t, true, foundExisting)
|
||||||
|
test.AssertResult(t, true, foundNew)
|
||||||
|
}
|
||||||
@ -258,13 +258,11 @@ nolint
|
|||||||
shortfile
|
shortfile
|
||||||
Unmarshalling
|
Unmarshalling
|
||||||
noini
|
noini
|
||||||
<<<<<<< Updated upstream
|
|
||||||
nocsv
|
nocsv
|
||||||
nobase64
|
nobase64
|
||||||
nouri
|
nouri
|
||||||
noprops
|
noprops
|
||||||
nosh
|
nosh
|
||||||
noshell
|
noshell
|
||||||
=======
|
|
||||||
tinygo
|
tinygo
|
||||||
>>>>>>> Stashed changes
|
nonexistent
|
||||||
Loading…
Reference in New Issue
Block a user