1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-21 12:16:54 +02:00
lazygit/pkg/utils/yaml_utils/yaml_utils_test.go
Stefan Haller 87c3e75811 Use indentation of 2 when rewriting auto-migrated config file
This seems to be what most people use when indenting yaml files, and it seems to
make more sense than the default of 4.

We also use it the example config in Config.md.
2025-02-23 11:32:12 +01:00

390 lines
9.1 KiB
Go

package yaml_utils
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
func TestUpdateYamlValue(t *testing.T) {
tests := []struct {
name string
in string
path []string
value string
expectedOut string
expectedErr string
}{
{
name: "update value",
in: "foo: bar\n",
path: []string{"foo"},
value: "baz",
expectedOut: "foo: baz\n",
expectedErr: "",
},
{
name: "add new key and value",
in: "foo: bar\n",
path: []string{"foo2"},
value: "baz",
expectedOut: "foo: bar\nfoo2: baz\n",
expectedErr: "",
},
{
name: "add new key and value when document was empty",
in: "",
path: []string{"foo"},
value: "bar",
expectedOut: "foo: bar\n",
expectedErr: "",
},
{
name: "preserve inline comment",
in: "foo: bar # my comment\n",
path: []string{"foo2"},
value: "baz",
expectedOut: "foo: bar # my comment\nfoo2: baz\n",
expectedErr: "",
},
{
name: "nested update",
in: "foo:\n bar: baz\n",
path: []string{"foo", "bar"},
value: "qux",
expectedOut: "foo:\n bar: qux\n",
expectedErr: "",
},
{
name: "nested where parents doesn't exist yet",
in: "",
path: []string{"foo", "bar", "baz"},
value: "qux",
expectedOut: "foo:\n bar:\n baz: qux\n",
expectedErr: "",
},
{
name: "don't rewrite file if value didn't change",
in: "foo:\n bar: baz\n",
path: []string{"foo", "bar"},
value: "baz",
expectedOut: "foo:\n bar: baz\n",
expectedErr: "",
},
// Error cases
{
name: "existing document is not a dictionary",
in: "42\n",
path: []string{"foo"},
value: "bar",
expectedOut: "42\n",
expectedErr: "yaml document is not a dictionary",
},
{
name: "trying to update a note that is not a scalar",
in: "foo: [1, 2, 3]\n",
path: []string{"foo"},
value: "bar",
expectedOut: "foo: [1, 2, 3]\n",
expectedErr: "yaml node is not a scalar",
},
{
name: "not all path elements are dictionaries",
in: "foo:\n bar: [1, 2, 3]\n",
path: []string{"foo", "bar", "baz"},
value: "qux",
expectedOut: "foo:\n bar: [1, 2, 3]\n",
expectedErr: "yaml node in path is not a dictionary",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
out, actualErr := UpdateYamlValue([]byte(test.in), test.path, test.value)
if test.expectedErr == "" {
assert.NoError(t, actualErr)
} else {
assert.EqualError(t, actualErr, test.expectedErr)
}
assert.Equal(t, test.expectedOut, string(out))
})
}
}
func TestRenameYamlKey(t *testing.T) {
tests := []struct {
name string
in string
path []string
newKey string
expectedOut string
expectedErr string
}{
{
name: "rename key",
in: "foo: 5\n",
path: []string{"foo"},
newKey: "bar",
expectedOut: "bar: 5\n",
expectedErr: "",
},
{
name: "rename key, nested",
in: "foo:\n bar: 5\n",
path: []string{"foo", "bar"},
newKey: "baz",
expectedOut: "foo:\n baz: 5\n",
expectedErr: "",
},
{
name: "rename non-scalar key",
in: "foo:\n bar: 5\n",
path: []string{"foo"},
newKey: "qux",
expectedOut: "qux:\n bar: 5\n",
expectedErr: "",
},
{
name: "don't rewrite file if value didn't change",
in: "foo:\n bar: 5\n",
path: []string{"nonExistingKey"},
newKey: "qux",
expectedOut: "foo:\n bar: 5\n",
expectedErr: "",
},
// Error cases
{
name: "existing document is not a dictionary",
in: "42\n",
path: []string{"foo"},
newKey: "bar",
expectedOut: "42\n",
expectedErr: "yaml node in path is not a dictionary",
},
{
name: "not all path elements are dictionaries",
in: "foo:\n bar: [1, 2, 3]\n",
path: []string{"foo", "bar", "baz"},
newKey: "qux",
expectedOut: "foo:\n bar: [1, 2, 3]\n",
expectedErr: "yaml node in path is not a dictionary",
},
{
name: "new key exists",
in: "foo: 5\nbar: 7\n",
path: []string{"foo"},
newKey: "bar",
expectedOut: "foo: 5\nbar: 7\n",
expectedErr: "new key `bar' already exists",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
out, actualErr := RenameYamlKey([]byte(test.in), test.path, test.newKey)
if test.expectedErr == "" {
assert.NoError(t, actualErr)
} else {
assert.EqualError(t, actualErr, test.expectedErr)
}
assert.Equal(t, test.expectedOut, string(out))
})
}
}
func TestWalk_paths(t *testing.T) {
tests := []struct {
name string
document string
expectedPaths []string
}{
{
name: "empty document",
document: "",
expectedPaths: []string{},
},
{
name: "scalar",
document: "x: 5",
expectedPaths: []string{"", "x"}, // called with an empty path for the root node
},
{
name: "nested",
document: "foo:\n x: 5",
expectedPaths: []string{"", "foo", "foo.x"},
},
{
name: "deeply nested",
document: "foo:\n bar:\n baz: 5",
expectedPaths: []string{"", "foo", "foo.bar", "foo.bar.baz"},
},
{
name: "array",
document: "foo:\n bar: [3, 7]",
expectedPaths: []string{"", "foo", "foo.bar", "foo.bar[0]", "foo.bar[1]"},
},
{
name: "nested arrays",
document: "foo:\n bar: [[3, 7], [8, 9]]",
expectedPaths: []string{"", "foo", "foo.bar", "foo.bar[0]", "foo.bar[0][0]", "foo.bar[0][1]", "foo.bar[1]", "foo.bar[1][0]", "foo.bar[1][1]"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
paths := []string{}
_, err := Walk([]byte(test.document), func(node *yaml.Node, path string) bool {
paths = append(paths, path)
return true
})
assert.NoError(t, err)
assert.Equal(t, test.expectedPaths, paths)
})
}
}
func TestWalk_inPlaceChanges(t *testing.T) {
tests := []struct {
name string
in string
callback func(node *yaml.Node, path string) bool
expectedOut string
}{
{
name: "no change",
in: "x: 5",
callback: func(node *yaml.Node, path string) bool { return false },
expectedOut: "x: 5",
},
{
name: "change value",
in: "x: 5\ny: 3",
callback: func(node *yaml.Node, path string) bool {
if path == "x" {
node.Value = "7"
return true
}
return false
},
expectedOut: "x: 7\ny: 3\n",
},
{
name: "change nested value",
in: "x:\n y: 5",
callback: func(node *yaml.Node, path string) bool {
if path == "x.y" {
node.Value = "7"
return true
}
return false
},
expectedOut: "x:\n y: 7\n",
},
{
name: "change array value",
in: "x:\n - y: 5",
callback: func(node *yaml.Node, path string) bool {
if path == "x[0].y" {
node.Value = "7"
return true
}
return false
},
expectedOut: "x:\n - y: 7\n",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, err := Walk([]byte(test.in), test.callback)
assert.NoError(t, err)
assert.Equal(t, test.expectedOut, string(result))
})
}
}
func TestTransformNode(t *testing.T) {
transformIntValueToString := func(node *yaml.Node) (bool, error) {
if node.Kind == yaml.ScalarNode {
if node.ShortTag() == "!!int" {
node.Tag = "!!str"
return true, nil
} else if node.ShortTag() == "!!str" {
// We have already transformed it,
return false, nil
} else {
return false, fmt.Errorf("Node was of bad type")
}
} else {
return false, fmt.Errorf("Node was not a scalar")
}
}
tests := []struct {
name string
in string
path []string
transform func(node *yaml.Node) (bool, error)
expectedOut string
}{
{
name: "Path not present",
in: "foo: 1",
path: []string{"bar"},
transform: transformIntValueToString,
expectedOut: "foo: 1",
},
{
name: "Part of path present",
in: `
foo:
bar: 2`,
path: []string{"foo", "baz"},
transform: transformIntValueToString,
expectedOut: `
foo:
bar: 2`,
},
{
name: "Successfully Transforms to string",
in: `
foo:
bar: 2`,
path: []string{"foo", "bar"},
transform: transformIntValueToString,
expectedOut: `foo:
bar: "2"
`, // Note the trailing newline changes because of how it re-marshalls
},
{
name: "Does nothing when already transformed",
in: `
foo:
bar: "2"`,
path: []string{"foo", "bar"},
transform: transformIntValueToString,
expectedOut: `
foo:
bar: "2"`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, err := TransformNode([]byte(test.in), test.path, test.transform)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expectedOut, string(result))
})
}
}