Files
kustomize/kyaml/yaml/fns_test.go
Karl Isenberg 43868688d5 Use require for Error and NoError
Assert keeps going after failure, but require immediately fails
the tests, making it easier to find the output related to the test
failure, rather than having to comb through a bunch of subsequent
assertion failures. For equality tests, we may or may not want to
continue, but for error checks we almost always want to immediately
fail the test. Exceptions can be changed as-needed.
2024-03-20 13:19:18 -07:00

1307 lines
31 KiB
Go

// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/kyaml/errors"
. "sigs.k8s.io/kustomize/kyaml/yaml"
yaml "sigs.k8s.io/yaml/goyaml.v3"
)
const (
NodeSampleData = `n: o
a: b
c: d
`
)
func TestResourceNode_SetValue(t *testing.T) {
instance := *NewScalarRNode("foo")
instanceCopy := instance
instance.SetYNode(&yaml.Node{Kind: yaml.ScalarNode, Value: "bar"})
assert.Equal(t, `bar
`, assertNoErrorString(t)(instanceCopy.String()))
assert.Equal(t, `bar
`, assertNoErrorString(t)(instance.String()))
instance = *NewScalarRNode("foo")
instanceCopy = instance
instance.SetYNode(nil)
instance.SetYNode(&yaml.Node{Kind: yaml.ScalarNode, Value: "bar"})
assert.Equal(t, `foo
`, assertNoErrorString(t)(instanceCopy.String()))
assert.Equal(t, `bar
`, assertNoErrorString(t)(instance.String()))
}
func TestAppend(t *testing.T) {
node, err := Parse(NodeSampleData)
require.NoError(t, err)
rn, err := node.Pipe(Append(NewScalarRNode("").YNode()))
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "wrong node kind")
}
assert.Nil(t, rn)
s := `- a
- b
`
node, err = Parse(s)
require.NoError(t, err)
rn, err = node.Pipe(Append())
require.NoError(t, err)
assert.Nil(t, rn)
}
func TestGetElementByIndex(t *testing.T) {
node, err := Parse(`
- 0
- 1
- 2
`)
require.NoError(t, err)
rn, err := node.Pipe(GetElementByIndex(0))
require.NoError(t, err)
assert.Equal(t, "0\n", assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(GetElementByIndex(2))
require.NoError(t, err)
assert.Equal(t, "2\n", assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(GetElementByIndex(-1))
require.NoError(t, err)
assert.Equal(t, "2\n", assertNoErrorString(t)(rn.String()))
}
func TestGetElementByKey(t *testing.T) {
node, err := Parse(`
- b: c
- i
- d: e
- f: g
- f: h
`)
require.NoError(t, err)
rn, err := node.Pipe(GetElementByKey("b"))
require.NoError(t, err)
assert.Equal(t, "b: c\n", assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(GetElementByKey("f"))
require.NoError(t, err)
assert.Equal(t, "f: g\n", assertNoErrorString(t)(rn.String()))
}
func TestElementSetter(t *testing.T) {
orig := MustParse(`
- a: b
- scalarValue
- c: d
# null will be removed
- null
`)
// ElementSetter will update node, so make a copy
node := orig.Copy()
// Remove an element, because ElementSetter.Element is left nil.
rn, err := node.Pipe(ElementSetter{Keys: []string{"a"}, Values: []string{"b"}})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, `- scalarValue
- c: d
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Nothing happens because no element is matched
rn, err = node.Pipe(ElementSetter{Keys: []string{"a"}, Values: []string{"zebra"}})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, `- a: b
- scalarValue
- c: d
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// When the element is not found, return an empty ist
_, err = node.Pipe(ElementSetter{Keys: []string{"a"}})
require.NoError(t, err)
assert.Equal(t, `[]
`, assertNoErrorString(t)(node.String()))
_, err = node.Pipe(ElementSetter{Values: []string{"b"}})
require.NoError(t, err)
assert.Equal(t, `[]
`, assertNoErrorString(t)(node.String()))
node = MustParse(`
- a: b
- c: d
`)
// If given a key and no values, ElementSetter will
// change node to be an empty list
rn, err = node.Pipe(ElementSetter{Keys: []string{"a"}})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, `[]
`, assertNoErrorString(t)(node.String()))
node = MustParse(`
- a: b
- c: d
`)
// Return error because ElementSetter will assume all elements are scalar when
// there is only value provided.
_, err = node.Pipe(ElementSetter{Values: []string{"b"}})
assert.EqualError(t, err, `wrong node kind: expected ScalarNode but got MappingNode: node contents:
a: b
`)
node = MustParse(`
- a
- b
`)
// b is removed since ElementSetter use the value "b" to match the
// scalar values.
rn, err = node.Pipe(ElementSetter{Values: []string{"b"}})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, `- a
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Set an element, replacing 'a: b' with 'e: f'
newElement := NewMapRNode(&map[string]string{
"e": "f",
})
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a"},
Values: []string{"b"},
Element: newElement.YNode(),
})
require.NoError(t, err)
assert.Equal(t, rn, newElement)
assert.Equal(t, `- e: f
- scalarValue
- c: d
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Set an element with scalar, {"a": "b"} to "foo"
newElement = NewScalarRNode("foo")
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a"},
Values: []string{"b"},
Element: newElement.YNode(),
})
require.NoError(t, err)
assert.Equal(t, rn, newElement)
assert.Equal(t, `- foo
- scalarValue
- c: d
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Append an element, {"x": "y"} is not in the list
// so the element will be appended.
newElement = NewMapRNode(&map[string]string{
"e": "f",
})
rn, err = node.Pipe(ElementSetter{
Keys: []string{"x"},
Values: []string{"y"},
Element: newElement.YNode(),
})
require.NoError(t, err)
assert.Equal(t, rn, newElement)
assert.Equal(t, `- a: b
- scalarValue
- c: d
- e: f
`, assertNoErrorString(t)(node.String()))
}
func TestElementSetterMultipleKeys(t *testing.T) {
orig := MustParse(`
- a: b
c: d
- scalarValue
- e: f
# null will be removed
- null
`)
// ElementSetter will update node, so make a copy
node := orig.Copy()
// Remove an element using one key-value pair,
// because ElementSetter.Element is left nil.
rn, err := node.Pipe(ElementSetter{
Keys: []string{"a"},
Values: []string{"b"},
})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, `- scalarValue
- e: f
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Remove an element using multiple key-value pairs,
// because ElementSetter.Element is left nil.
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a", "c"},
Values: []string{"b", "d"},
})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, `- scalarValue
- e: f
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Should do nothing, because Element is nil
// and there is no element which matches all
// give key-value pairs
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a", "c"},
Values: []string{"b", "wrong value"},
})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, `- a: b
c: d
- scalarValue
- e: f
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Set an element, with a single key-value pair
// replacing 'a: b, c: d' with 'g: h'
newElement := NewMapRNode(&map[string]string{
"g": "h",
})
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a"},
Values: []string{"b"},
Element: newElement.YNode(),
})
require.NoError(t, err)
assert.Equal(t, rn, newElement)
assert.Equal(t, `- g: h
- scalarValue
- e: f
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Set an element, with multiple key-value pairs
// replacing 'a: b, c: d' with 'g: h'
newElement = NewMapRNode(&map[string]string{
"g": "h",
})
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a", "c"},
Values: []string{"b", "d"},
Element: newElement.YNode(),
})
require.NoError(t, err)
assert.Equal(t, rn, newElement)
assert.Equal(t, `- g: h
- scalarValue
- e: f
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Set an element scalar,
// {'a: b, c: d'} to "foo"
newElement = NewScalarRNode("foo")
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a", "c"},
Values: []string{"b", "d"},
Element: newElement.YNode(),
})
require.NoError(t, err)
assert.Equal(t, rn, newElement)
assert.Equal(t, `- foo
- scalarValue
- e: f
`, assertNoErrorString(t)(node.String()))
node = orig.Copy()
// Append an element
// There is no element which matches all given
// key-value pairs, so the element will be appended.
newElement = NewMapRNode(&map[string]string{
"g": "h",
})
rn, err = node.Pipe(ElementSetter{
Keys: []string{"a", "c"},
Values: []string{"b", "wrong value"},
Element: newElement.YNode(),
})
require.NoError(t, err)
assert.Equal(t, rn, newElement)
assert.Equal(t, `- a: b
c: d
- scalarValue
- e: f
- g: h
`, assertNoErrorString(t)(node.String()))
}
func TestElementMatcherWithNoValue(t *testing.T) {
node, err := Parse(`
- a: c
- b: ""
`)
require.NoError(t, err)
rn, err := node.Pipe(ElementMatcher{Keys: []string{"b"}})
require.NoError(t, err)
assert.Equal(t, "b: \"\"\n", assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(ElementMatcher{Keys: []string{"a"}})
require.NoError(t, err)
assert.Nil(t, rn)
rn, err = node.Pipe(ElementMatcher{Keys: []string{"a"}, MatchAnyValue: true})
require.NoError(t, err)
assert.Equal(t, "a: c\n", assertNoErrorString(t)(rn.String()))
_, err = node.Pipe(ElementMatcher{Keys: []string{"a"}, Values: []string{"c"}, MatchAnyValue: true})
require.Errorf(t, err, "Values must be empty when MatchAnyValue is set to true")
}
func TestElementMatcherMultipleKeys(t *testing.T) {
node, err := Parse(`
- a: b
c: d
- e: f
`)
require.NoError(t, err)
// matches all key-value pairs
rn, err := node.Pipe(MatchElementList(
[]string{"a", "c"}, // keys
[]string{"b", "d"}, // values
))
require.NoError(t, err)
assert.NotEmpty(t, rn)
// matches one key value pair but not the other
rn, err = node.Pipe(MatchElementList(
[]string{"a", "c"}, // keys
[]string{"b", "f"}, // values
))
require.NoError(t, err)
assert.Nil(t, rn)
// matches single given key value pair
rn, err = node.Pipe(MatchElementList(
[]string{"e"}, // keys
[]string{"f"}, // values
))
require.NoError(t, err)
assert.NotEmpty(t, rn)
// matching key, but value doesn't match
rn, err = node.Pipe(MatchElementList(
[]string{"e"}, // keys
[]string{"g"}, // values
))
require.NoError(t, err)
assert.Nil(t, rn)
}
func TestClearField_Fn(t *testing.T) {
node, err := Parse(NodeSampleData)
require.NoError(t, err)
rn, err := node.Pipe(FieldClearer{Name: "a"})
require.NoError(t, err)
assert.Equal(t, "n: o\nc: d\n", assertNoErrorString(t)(node.String()))
assert.Equal(t, "b\n", assertNoErrorString(t)(rn.String()))
node, err = Parse(NodeSampleData)
require.NoError(t, err)
rn, err = node.Pipe(FieldClearer{Name: "n"})
require.NoError(t, err)
assert.Equal(t, "a: b\nc: d\n", assertNoErrorString(t)(node.String()))
assert.Equal(t, "o\n", assertNoErrorString(t)(rn.String()))
node, err = Parse(NodeSampleData)
require.NoError(t, err)
rn, err = node.Pipe(FieldClearer{Name: "c"})
require.NoError(t, err)
assert.Equal(t, "n: o\na: b\n", assertNoErrorString(t)(node.String()))
assert.Equal(t, "d\n", assertNoErrorString(t)(rn.String()))
s := `n: o
a: b
`
node, err = Parse(s)
require.NoError(t, err)
rn, err = node.Pipe(FieldClearer{Name: "o"})
require.NoError(t, err)
assert.Nil(t, rn)
assert.Equal(t, "n: o\na: b\n", assertNoErrorString(t)(node.String()))
s = `- a
- b
`
node, err = Parse(s)
require.NoError(t, err)
rn, err = node.Pipe(FieldClearer{Name: "a"})
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "wrong node kind")
}
assert.Nil(t, rn)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
// should not clear n because it is not empty
s = `n:
k: v
a: b
c: d
`
node, err = Parse(s)
require.NoError(t, err)
rn, err = node.Pipe(FieldClearer{Name: "n", IfEmpty: true})
require.NoError(t, err)
assert.Equal(t, "n:\n k: v\na: b\nc: d\n", assertNoErrorString(t)(node.String()))
assert.Equal(t, "", assertNoErrorString(t)(rn.String()))
// should clear n because it is empty
s = `n: {}
a: b
c: d
`
node, err = Parse(s)
require.NoError(t, err)
rn, err = node.Pipe(FieldClearer{Name: "n", IfEmpty: true})
require.NoError(t, err)
assert.Equal(t, "a: b\nc: d\n", assertNoErrorString(t)(node.String()))
assert.Equal(t, "{}\n", assertNoErrorString(t)(rn.String()))
}
var s = `n: o
a:
l: m
b:
- f: g
- c: e
- h: i
r: s
`
func TestLookup_Fn_create(t *testing.T) {
// primitive
node, err := Parse(s)
require.NoError(t, err)
rn, err := node.Pipe(PathGetter{
Path: []string{"a", "b", "[c=d]", "t", "f", "[=h]"},
Create: yaml.ScalarNode,
})
require.NoError(t, err)
assert.Equal(t, `n: o
a:
l: m
b:
- f: g
- c: e
- h: i
- c: d
t:
f:
- h
r: s
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `h
`, assertNoErrorString(t)(rn.String()))
}
func TestLookup_Fn_create2(t *testing.T) {
node, err := Parse(s)
require.NoError(t, err)
rn, err := node.Pipe(PathGetter{
Path: []string{"a", "b", "[c=d]", "t", "f"},
Create: yaml.SequenceNode,
})
require.NoError(t, err)
assert.Equal(t, `n: o
a:
l: m
b:
- f: g
- c: e
- h: i
- c: d
t:
f: []
r: s
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `[]
`, assertNoErrorString(t)(rn.String()))
}
func TestLookup_Fn_create3(t *testing.T) {
node, err := Parse(s)
require.NoError(t, err)
rn, err := node.Pipe(LookupCreate(yaml.MappingNode, "a", "b", "[c=d]", "t"))
require.NoError(t, err)
assert.Equal(t, `n: o
a:
l: m
b:
- f: g
- c: e
- h: i
- c: d
t: {}
r: s
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `{}
`, assertNoErrorString(t)(rn.String()))
}
func TestLookupCreate_4(t *testing.T) {
node, err := Parse(`
a: {}
`)
require.NoError(t, err)
rn, err := node.Pipe(
LookupCreate(yaml.MappingNode, "a", "b", "[c=d]", "t", "f", "[=h]"))
node.YNode().Style = yaml.FlowStyle
require.NoError(t, err)
assert.Equal(t, "{a: {b: [{c: d, t: {f: [h]}}]}}\n", assertNoErrorString(t)(node.String()))
assert.Equal(t, "h\n", assertNoErrorString(t)(rn.String()))
}
func TestLookup_Fn_create_with_wildcard_error(t *testing.T) {
node, err := Parse(s)
require.NoError(t, err)
_, err = node.Pipe(LookupCreate(yaml.MappingNode, "a", "b", "*", "t"))
require.Error(t, err, "wildcard is not supported in PathGetter")
}
func TestLookup(t *testing.T) {
s := `n: o
a:
l: m
b:
- f: g
- c: e
- c: d
t:
u: v
f:
- g
- h
- i
j: k
- h: i
p: q
r: s
`
node, err := Parse(s)
require.NoError(t, err)
// primitive
rn, err := node.Pipe(Lookup("a", "b", "[c=d]", "t", "f", "[=h]"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `h
`, assertNoErrorString(t)(rn.String()))
// seq
rn, err = node.Pipe(Lookup("a", "b", "[c=d]", "t", "f"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `- g
- h
- i
`, assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(Lookup("a", "b", "[c=d]", "t"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `u: v
f:
- g
- h
- i
`, assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(Lookup("a", "b", "[c=d]"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `c: d
t:
u: v
f:
- g
- h
- i
j: k
`, assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(Lookup("a", "b"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `- f: g
- c: e
- c: d
t:
u: v
f:
- g
- h
- i
j: k
- h: i
p: q
`, assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(Lookup("a", "b", "0"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `f: g
`, assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(Lookup("a", "b", "-", "h"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `i
`, assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(Lookup("l"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("zzz"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("[a=b]"))
require.Error(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("a", "b", "f"))
require.Error(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("a", "b", "c=zzz"))
require.Error(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup(" ", "a", "", "b", " ", "[c=d]", "\n", "t", "\t", "f", " ", "[=h]", " "))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Equal(t, `h
`, assertNoErrorString(t)(rn.String()))
rn, err = node.Pipe(Lookup(" ", "a", "", "b", " ", "[]"))
require.Error(t, err)
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("a", "b", "[c=d]", "t", "f", "[=c]"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("a", "b", "[z=z]"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("a", "b", "-1"))
require.Errorf(t, err, "array index -1 cannot be negative")
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
rn, err = node.Pipe(Lookup("a", "b", "99"))
require.NoError(t, err)
assert.Equal(t, s, assertNoErrorString(t)(node.String()))
assert.Nil(t, rn)
}
func TestLookupFirstMatch(t *testing.T) {
tests := []struct {
name string
paths [][]string
wantPath []string
}{
{
name: "finds path that exists",
paths: [][]string{{"spec", "jobTemplate", "spec", "template", "spec", "containers"}},
wantPath: []string{"spec", "jobTemplate", "spec", "template", "spec", "containers"},
},
{
name: "chooses first path when multiple exist: containers example",
paths: ConventionalContainerPaths,
wantPath: []string{"spec", "template", "spec", "containers"},
},
{
name: "chooses first path when multiple exist: annotations example",
paths: [][]string{
{"metadata", "annotations", "example.kustomize.io/new"},
{"metadata", "annotations", "example.kustomize.io/deprecated"},
},
wantPath: []string{"metadata", "annotations", "example.kustomize.io/new"},
},
{
name: "returns nil when path does not exist",
paths: [][]string{
{"metadata", "annotations", "example.kustomize.io/does-not-exist"},
{"metadata", "annotations", "example.kustomize.io/also-not-exist"},
},
wantPath: nil,
},
}
for _, tt := range tests {
s := `
apiVersion: example.kustomize.io/v1
kind: Custom
metadata:
annotations:
example.kustomize.io/deprecated: foo
example.kustomize.io/new: foo
spec:
template:
spec:
containers:
- name: foo
jobTemplate:
spec:
template:
spec:
containers:
- name: foo
`
resource := MustParse(s)
t.Run(tt.name, func(t *testing.T) {
result, err := LookupFirstMatch(tt.paths).Filter(resource)
require.NoError(t, err)
if tt.wantPath != nil {
assert.Equal(t, tt.wantPath, result.FieldPath())
} else {
assert.Nil(t, result)
}
})
}
}
func TestMapEntrySetter(t *testing.T) {
withStyle := func(rn *RNode, style yaml.Style) *RNode {
rn.YNode().Style = style
return rn
}
testCases := []struct {
desc string
input string
setter MapEntrySetter
expected string
expectedErr error
}{
{
desc: "it should override Key's value map entry",
input: "foo: baz\n",
setter: MapEntrySetter{
Key: NewScalarRNode("foo"),
Value: NewScalarRNode("bar"),
},
expected: "foo: bar\n",
},
{
desc: "it should override Name's map entry",
input: "foo: baz\n",
setter: MapEntrySetter{
Name: "foo",
Key: NewScalarRNode("bar"),
Value: NewScalarRNode("baz"),
},
expected: "bar: baz\n",
},
{
desc: "it should insert new map entry",
input: "foo: baz\n",
setter: MapEntrySetter{
Key: NewScalarRNode("bar"),
Value: NewScalarRNode("42"),
},
expected: "foo: baz\nbar: 42\n",
},
{
desc: "it should override the style",
input: "foo: baz\n",
setter: MapEntrySetter{
Key: withStyle(NewScalarRNode("foo"), yaml.DoubleQuotedStyle),
Value: withStyle(NewScalarRNode("bar"), yaml.SingleQuotedStyle),
},
expected: `"foo": 'bar'` + "\n",
},
{
desc: "it should return error on sequence nodes",
input: "- foo: baz\n",
setter: MapEntrySetter{
Key: NewScalarRNode("foo"),
Value: NewScalarRNode("bar"),
},
expectedErr: errors.Errorf(`wrong node kind: expected MappingNode but got SequenceNode: node contents:
- foo: baz
`),
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
node, err := Parse(tc.input)
require.NoError(t, err)
k, err := tc.setter.Filter(node)
if tc.expectedErr == nil {
require.NoError(t, err)
assert.Equal(t, tc.expected, assertNoErrorString(t)(node.String()))
assert.Equal(t, tc.expected, assertNoErrorString(t)(k.String()))
} else {
assert.NotNil(t, err)
assert.Equal(t, tc.expectedErr.Error(), err.Error())
}
})
}
}
func TestFieldSetter(t *testing.T) {
// Change field
node, err := Parse(`
foo: baz
`)
require.NoError(t, err)
instance := FieldSetter{
Name: "foo",
Value: NewScalarRNode("bar"),
}
k, err := instance.Filter(node)
require.NoError(t, err)
assert.Equal(t, `foo: bar
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `bar
`, assertNoErrorString(t)(k.String()))
// Add field
node, err = Parse(`
foo: baz
`)
require.NoError(t, err)
instance = FieldSetter{
Name: "bar",
Value: NewScalarRNode("buz"),
}
k, err = instance.Filter(node)
require.NoError(t, err)
assert.Equal(t, `foo: baz
bar: buz
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `buz
`, assertNoErrorString(t)(k.String()))
// Clear field
node, err = Parse(`
foo: baz
bar: buz
`)
require.NoError(t, err)
instance = FieldSetter{
Name: "foo",
}
k, err = instance.Filter(node)
require.NoError(t, err)
assert.Equal(t, `bar: buz
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `baz
`, assertNoErrorString(t)(k.String()))
// Empty value
node, err = Parse(`
foo
`)
require.NoError(t, err)
instance = FieldSetter{}
k, err = instance.Filter(node)
require.NoError(t, err)
assert.Equal(t, `foo
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `foo
`, assertNoErrorString(t)(k.String()))
// Encounter error
node, err = Parse(`
-a
-b
`)
require.NoError(t, err)
instance = FieldSetter{
Name: "foo",
Value: NewScalarRNode("v"),
}
k, err = instance.Filter(node)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "wrong node kind")
}
assert.Nil(t, k)
}
func TestFieldSetterNumberInKeyRegression(t *testing.T) {
node := NewMapRNode(&map[string]string{"river": "mississippi"})
k, err := FieldSetter{
Name: "forty 2",
Value: NewScalarRNode("number key one"),
}.Filter(node)
require.NoError(t, err)
assert.Equal(t, `number key one
`, assertNoErrorString(t)(k.String()))
k, err = FieldSetter{
Name: "fortytwo",
Value: NewScalarRNode("number key two"),
}.Filter(node)
require.NoError(t, err)
assert.Equal(t, `number key two
`, assertNoErrorString(t)(k.String()))
k, err = FieldSetter{
Name: "42",
Value: NewScalarRNode("number key three"),
}.Filter(node)
require.NoError(t, err)
assert.Equal(t, `number key three
`, assertNoErrorString(t)(k.String()))
assert.Equal(t, `river: mississippi
forty 2: number key one
fortytwo: number key two
42: number key three
`, assertNoErrorString(t)(node.String()))
}
func TestSet_Fn(t *testing.T) {
node, err := Parse(`
foo: baz
`)
require.NoError(t, err)
k, err := node.Pipe(Get("foo"), Set(NewScalarRNode("bar")))
require.NoError(t, err)
assert.Equal(t, `foo: bar
`, assertNoErrorString(t)(node.String()))
assert.Equal(t, `bar
`, assertNoErrorString(t)(k.String()))
node, err = Parse(`
foo: baz
`)
require.NoError(t, err)
_, err = node.Pipe(Set(NewScalarRNode("bar")))
if !assert.Error(t, err) {
return
}
assert.Contains(t, err.Error(), "wrong node kind")
assert.Equal(t, `foo: baz
`, assertNoErrorString(t)(node.String()))
}
func TestErrorIfInvalid(t *testing.T) {
err := ErrorIfInvalid(
NewRNode(&yaml.Node{Kind: yaml.SequenceNode}), yaml.SequenceNode)
require.NoError(t, err)
// nil values should pass validation -- they were not specified
err = ErrorIfInvalid(&RNode{}, yaml.SequenceNode)
if !assert.NoError(t, err) {
t.FailNow()
}
err = ErrorIfInvalid(NewRNode(&Node{Content: []*yaml.Node{{Value: "hello"}}}), yaml.SequenceNode)
if !assert.Error(t, err) {
t.FailNow()
}
assert.Contains(t, err.Error(), "wrong node kind")
err = ErrorIfInvalid(NewRNode(&yaml.Node{}), yaml.SequenceNode)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "wrong node kind")
}
err = ErrorIfInvalid(NewRNode(&yaml.Node{}), yaml.MappingNode)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "wrong node kind")
}
err = ErrorIfInvalid(NewRNode(&yaml.Node{
Kind: yaml.MappingNode,
Content: []*yaml.Node{{}, {}},
}), yaml.MappingNode)
require.NoError(t, err)
err = ErrorIfInvalid(NewRNode(&yaml.Node{}), yaml.SequenceNode)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "wrong node kind")
}
err = ErrorIfInvalid(NewRNode(&yaml.Node{
Kind: yaml.MappingNode,
Content: []*yaml.Node{{}},
}), yaml.MappingNode)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "even length")
}
}
func TestSplitIndexNameValue(t *testing.T) {
k, v, err := SplitIndexNameValue("")
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "fieldName=fieldValue")
}
assert.Equal(t, "", k)
assert.Equal(t, "", v)
k, v, err = SplitIndexNameValue("a=b")
require.NoError(t, err)
assert.Equal(t, "a", k)
assert.Equal(t, "b", v)
k, v, err = SplitIndexNameValue("=b")
require.NoError(t, err)
assert.Equal(t, "", k)
assert.Equal(t, "b", v)
k, v, err = SplitIndexNameValue("a=b=c")
require.NoError(t, err)
assert.Equal(t, "a", k)
assert.Equal(t, "b=c", v)
k, v, err = SplitIndexNameValue("=-jar")
require.NoError(t, err)
assert.Equal(t, "", k)
assert.Equal(t, "-jar", v)
}
type filter struct {
fn func(object *RNode) (*RNode, error)
}
func (c filter) Filter(object *RNode) (*RNode, error) {
return c.fn(object)
}
func TestResourceNode_Pipe(t *testing.T) {
var r0, r1, r2, r3 *RNode
var called []string
// check the nil value case
_, err := r0.Pipe(FieldMatcher{Name: "foo"})
require.NoError(t, err)
r0, r1, r2, r3 = &RNode{}, &RNode{}, &RNode{}, &RNode{}
// all filters successful
v, err := r0.Pipe(
filter{fn: func(object *RNode) (*RNode, error) {
assert.True(t, r0 == object)
called = append(called, "a")
return r1, nil
}},
filter{fn: func(object *RNode) (*RNode, error) {
assert.True(t, object == r1, "function arg doesn't match last function output")
called = append(called, "b")
return r2, nil
}},
filter{fn: func(object *RNode) (*RNode, error) {
assert.True(t, object == r2, "function arg doesn't match last function output")
return r3, nil
}},
)
assert.True(t, v == r3, "expected r3")
assert.Nil(t, err)
assert.Equal(t, called, []string{"a", "b"})
// filter returns nil
called = []string{}
v, err = r0.Pipe(
filter{fn: func(object *RNode) (*RNode, error) {
assert.True(t, r0 == object)
called = append(called, "a")
return r1, nil
}},
filter{fn: func(object *RNode) (*RNode, error) {
assert.True(t, object == r1, "function arg doesn't match last function output")
called = append(called, "b")
return nil, nil
}},
filter{fn: func(object *RNode) (*RNode, error) {
assert.Fail(t, "function should be run after error")
return nil, nil
}},
)
assert.Nil(t, v)
assert.Nil(t, err)
assert.Equal(t, called, []string{"a", "b"})
// filter returns an error
called = []string{}
v, err = r0.Pipe(
filter{fn: func(object *RNode) (*RNode, error) {
assert.True(t, r0 == object)
called = append(called, "a")
return r1, nil
}},
filter{fn: func(object *RNode) (*RNode, error) {
assert.True(t, object == r1, "function arg doesn't match last function output")
called = append(called, "b")
return r1, fmt.Errorf("expected-error")
}},
filter{fn: func(object *RNode) (*RNode, error) {
assert.Fail(t, "function should be run after error")
return nil, nil
}},
)
assert.True(t, v == r1, "expected r1 as value")
assert.EqualError(t, err, "expected-error")
assert.Equal(t, called, []string{"a", "b"})
}
func TestClearAnnotation(t *testing.T) {
// create metadata.annotations field
r0 := assertNoError(t)(Parse(`apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
z: y
a.b.c: d.e.f
s: t
`))
rn := assertNoError(t)(r0.Pipe(ClearAnnotation("a.b.c")))
assert.Equal(t, "d.e.f\n", assertNoErrorString(t)(rn.String()))
assert.Equal(t, `apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
z: y
s: t
`, assertNoErrorString(t)(r0.String()))
}
func TestGetAnnotation(t *testing.T) {
r0 := assertNoError(t)(Parse(`apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: java
annotations:
a.b.c: d.e.f
g: h
i: j
k: l
name: app`))
rn := assertNoError(t)(
r0.Pipe(GetAnnotation("a.b.c")))
assert.Equal(t, "d.e.f\n", assertNoErrorString(t)(rn.String()))
}
func TestSetAnnotation_Fn(t *testing.T) {
// create metadata.annotations field
r0 := assertNoError(t)(Parse(`apiVersion: apps/v1
kind: Deployment`))
rn := assertNoError(t)(r0.Pipe(SetAnnotation("a.b.c", "d.e.f")))
assert.Equal(t, "'d.e.f'\n", assertNoErrorString(t)(rn.String()))
assert.Equal(t, `apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a.b.c: 'd.e.f'
`, assertNoErrorString(t)(r0.String()))
}
func TestUpdateAnnotation_Fn(t *testing.T) {
// create metadata.annotations field
r0 := assertNoError(t)(Parse(`apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a.b.c: "h.i.j"
`))
rn := assertNoError(t)(r0.Pipe(SetAnnotation("a.b.c", "d.e.f")))
assert.Equal(t, "\"d.e.f\"\n", assertNoErrorString(t)(rn.String()))
assert.Equal(t, `apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a.b.c: "d.e.f"
`, assertNoErrorString(t)(r0.String()))
}
func TestRNode_GetMeta(t *testing.T) {
s := `apiVersion: v1/apps
kind: Deployment
metadata:
name: foo
namespace: bar
labels:
kl: vl
annotations:
ka: va
`
node, err := Parse(s)
if !assert.NoError(t, err) {
return
}
meta, err := node.GetMeta()
if !assert.NoError(t, err) {
return
}
assert.Equal(t, ResourceMeta{
TypeMeta: TypeMeta{
Kind: "Deployment",
APIVersion: "v1/apps",
},
ObjectMeta: ObjectMeta{
NameMeta: NameMeta{
Name: "foo",
Namespace: "bar",
},
Annotations: map[string]string{
"ka": "va",
},
Labels: map[string]string{
"kl": "vl",
},
},
}, meta)
}
func assertNoError(t *testing.T) func(o *RNode, err error) *RNode {
t.Helper()
return func(o *RNode, err error) *RNode {
require.NoError(t, err)
return o
}
}
func assertNoErrorString(t *testing.T) func(string, error) string {
t.Helper()
return func(s string, err error) string {
require.NoError(t, err)
return s
}
}