databricks-cli/libs/dyn/visit_map_test.go

249 lines
6.6 KiB
Go
Raw Normal View History

package dyn_test
import (
"fmt"
"testing"
"github.com/databricks/cli/libs/dyn"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMapWithEmptyPath(t *testing.T) {
// An empty path means to return the value itself.
vin := dyn.V(42)
vout, err := dyn.MapByPath(dyn.InvalidValue, dyn.EmptyPath, func(v dyn.Value) (dyn.Value, error) {
return vin, nil
})
assert.NoError(t, err)
assert.Equal(t, vin, vout)
}
func TestMapOnNilValue(t *testing.T) {
var err error
_, err = dyn.MapByPath(dyn.NilValue, dyn.NewPath(dyn.Key("foo")), nil)
assert.ErrorContains(t, err, `expected a map to index "foo", found nil`)
_, err = dyn.MapByPath(dyn.NilValue, dyn.NewPath(dyn.Index(42)), nil)
assert.ErrorContains(t, err, `expected a sequence to index "[42]", found nil`)
}
func TestMapFuncOnMap(t *testing.T) {
vin := dyn.V(map[string]dyn.Value{
"foo": dyn.V(42),
"bar": dyn.V(43),
})
var err error
_, err = dyn.MapByPath(vin, dyn.NewPath(dyn.Index(42)), nil)
assert.ErrorContains(t, err, `expected a sequence to index "[42]", found map`)
// A key that does not exist is not an error.
vout, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Key("baz")), nil)
assert.NoError(t, err)
assert.Equal(t, vin, vout)
// Note: in the test cases below we implicitly test that the original
// value is not modified as we repeatedly set values on it.
vfoo, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Key("foo")), func(v dyn.Value) (dyn.Value, error) {
assert.Equal(t, dyn.V(42), v)
return dyn.V(44), nil
})
assert.NoError(t, err)
assert.Equal(t, map[string]any{
"foo": 44,
"bar": 43,
}, vfoo.AsAny())
vbar, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Key("bar")), func(v dyn.Value) (dyn.Value, error) {
assert.Equal(t, dyn.V(43), v)
return dyn.V(45), nil
})
assert.NoError(t, err)
assert.Equal(t, map[string]any{
"foo": 42,
"bar": 45,
}, vbar.AsAny())
// Return error from map function.
var ref = fmt.Errorf("error")
verr, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Key("foo")), func(v dyn.Value) (dyn.Value, error) {
return dyn.InvalidValue, ref
})
assert.Equal(t, dyn.InvalidValue, verr)
assert.ErrorIs(t, err, ref)
}
func TestMapFuncOnMapWithEmptySequence(t *testing.T) {
variants := []dyn.Value{
// empty sequence
dyn.V([]dyn.Value{}),
// non-empty sequence
dyn.V([]dyn.Value{dyn.V(42)}),
}
for i := 0; i < len(variants); i++ {
vin := dyn.V(map[string]dyn.Value{
"key": variants[i],
})
for j := 0; j < len(variants); j++ {
vout, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Key("key")), func(v dyn.Value) (dyn.Value, error) {
return variants[j], nil
})
assert.NoError(t, err)
assert.Equal(t, variants[j], vout.Get("key"))
}
}
}
func TestMapFuncOnSequence(t *testing.T) {
vin := dyn.V([]dyn.Value{
dyn.V(42),
dyn.V(43),
})
var err error
_, err = dyn.MapByPath(vin, dyn.NewPath(dyn.Key("foo")), nil)
assert.ErrorContains(t, err, `expected a map to index "foo", found sequence`)
// An index that does not exist is not an error.
vout, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Index(2)), nil)
assert.NoError(t, err)
assert.Equal(t, vin, vout)
// Note: in the test cases below we implicitly test that the original
// value is not modified as we repeatedly set values on it.
v0, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Index(0)), func(v dyn.Value) (dyn.Value, error) {
assert.Equal(t, dyn.V(42), v)
return dyn.V(44), nil
})
assert.NoError(t, err)
assert.Equal(t, []any{44, 43}, v0.AsAny())
v1, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Index(1)), func(v dyn.Value) (dyn.Value, error) {
assert.Equal(t, dyn.V(43), v)
return dyn.V(45), nil
})
assert.NoError(t, err)
assert.Equal(t, []any{42, 45}, v1.AsAny())
// Return error from map function.
var ref = fmt.Errorf("error")
verr, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Index(0)), func(v dyn.Value) (dyn.Value, error) {
return dyn.InvalidValue, ref
})
assert.Equal(t, dyn.InvalidValue, verr)
assert.ErrorIs(t, err, ref)
}
func TestMapFuncOnSequenceWithEmptySequence(t *testing.T) {
variants := []dyn.Value{
// empty sequence
dyn.V([]dyn.Value{}),
// non-empty sequence
dyn.V([]dyn.Value{dyn.V(42)}),
}
for i := 0; i < len(variants); i++ {
vin := dyn.V([]dyn.Value{
variants[i],
})
for j := 0; j < len(variants); j++ {
vout, err := dyn.MapByPath(vin, dyn.NewPath(dyn.Index(0)), func(v dyn.Value) (dyn.Value, error) {
return variants[j], nil
})
assert.NoError(t, err)
assert.Equal(t, variants[j], vout.Index(0))
}
}
}
func TestMapForeachOnMap(t *testing.T) {
vin := dyn.V(map[string]dyn.Value{
"foo": dyn.V(42),
"bar": dyn.V(43),
})
var err error
// Run foreach, adding 1 to each of the elements.
vout, err := dyn.Map(vin, ".", dyn.Foreach(func(v dyn.Value) (dyn.Value, error) {
i, ok := v.AsInt()
require.True(t, ok, "expected an integer")
return dyn.V(int(i) + 1), nil
}))
assert.NoError(t, err)
assert.Equal(t, map[string]any{
"foo": 43,
"bar": 44,
}, vout.AsAny())
// Check that the original has not been modified.
assert.Equal(t, map[string]any{
"foo": 42,
"bar": 43,
}, vin.AsAny())
}
func TestMapForeachOnMapError(t *testing.T) {
vin := dyn.V(map[string]dyn.Value{
"foo": dyn.V(42),
"bar": dyn.V(43),
})
// Check that an error from the map function propagates.
var ref = fmt.Errorf("error")
_, err := dyn.Map(vin, ".", dyn.Foreach(func(v dyn.Value) (dyn.Value, error) {
return dyn.InvalidValue, ref
}))
assert.ErrorIs(t, err, ref)
}
func TestMapForeachOnSequence(t *testing.T) {
vin := dyn.V([]dyn.Value{
dyn.V(42),
dyn.V(43),
})
var err error
// Run foreach, adding 1 to each of the elements.
vout, err := dyn.Map(vin, ".", dyn.Foreach(func(v dyn.Value) (dyn.Value, error) {
i, ok := v.AsInt()
require.True(t, ok, "expected an integer")
return dyn.V(int(i) + 1), nil
}))
assert.NoError(t, err)
assert.Equal(t, []any{43, 44}, vout.AsAny())
// Check that the original has not been modified.
assert.Equal(t, []any{42, 43}, vin.AsAny())
}
func TestMapForeachOnSequenceError(t *testing.T) {
vin := dyn.V([]dyn.Value{
dyn.V(42),
dyn.V(43),
})
// Check that an error from the map function propagates.
var ref = fmt.Errorf("error")
_, err := dyn.Map(vin, ".", dyn.Foreach(func(v dyn.Value) (dyn.Value, error) {
return dyn.InvalidValue, ref
}))
assert.ErrorIs(t, err, ref)
}
func TestMapForeachOnOtherError(t *testing.T) {
vin := dyn.V(42)
// Check that if foreach is applied to something other than a map or a sequence, it returns an error.
_, err := dyn.Map(vin, ".", dyn.Foreach(func(v dyn.Value) (dyn.Value, error) {
return dyn.InvalidValue, nil
}))
assert.ErrorContains(t, err, "expected a map or sequence, found int")
}