databricks-cli/libs/dyn/walk_test.go

255 lines
5.7 KiB
Go

package dyn_test
import (
"errors"
"testing"
. "github.com/databricks/cli/libs/dyn"
assert "github.com/databricks/cli/libs/dyn/dynassert"
"github.com/stretchr/testify/require"
)
// Return values for specific paths.
type walkReturn struct {
path Path
// Return values.
fn func(Value) Value
err error
}
// Track the calls to the callback.
type walkCall struct {
path Path
value Value
}
// Track the calls to the callback.
type walkCallTracker struct {
returns []walkReturn
calls []walkCall
}
func (w *walkCallTracker) on(path string, fn func(Value) Value, err error) {
w.returns = append(w.returns, walkReturn{MustPathFromString(path), fn, err})
}
func (w *walkCallTracker) returnSkip(path string) {
w.on(path, func(v Value) Value { return v }, ErrSkip)
}
func (w *walkCallTracker) returnDrop(path string) {
w.on(path, func(v Value) Value { return InvalidValue }, ErrDrop)
}
func (w *walkCallTracker) track(p Path, v Value) (Value, error) {
w.calls = append(w.calls, walkCall{p, v})
// Look for matching return.
for _, r := range w.returns {
if p.Equal(r.path) {
return r.fn(v), r.err
}
}
return v, nil
}
func TestWalkEmpty(t *testing.T) {
var tracker walkCallTracker
value := V(nil)
out, err := Walk(value, tracker.track)
require.NoError(t, err)
assert.Equal(t, value, out)
// The callback should have been called once.
assert.Len(t, tracker.calls, 1)
// The call should have been made with the empty path.
assert.Equal(t, EmptyPath, tracker.calls[0].path)
// The value should be the same as the input.
assert.Equal(t, value, tracker.calls[0].value)
}
func TestWalkMapSkip(t *testing.T) {
var tracker walkCallTracker
// Skip traversal of the root value.
tracker.returnSkip(".")
value := V(map[string]Value{
"key": V("value"),
})
out, err := Walk(value, tracker.track)
require.NoError(t, err)
assert.Equal(
t,
V(map[string]Value{
"key": V("value"),
}),
out,
)
// The callback should have been called once.
assert.Len(t, tracker.calls, 1)
// The call should have been made with the empty path.
assert.Equal(t, EmptyPath, tracker.calls[0].path)
// The value should be the same as the input.
assert.Equal(t, value, tracker.calls[0].value)
}
func TestWalkMapDrop(t *testing.T) {
var tracker walkCallTracker
// Drop the value at key "foo".
tracker.returnDrop(".foo")
value := V(map[string]Value{
"foo": V("bar"),
"bar": V("baz"),
})
out, err := Walk(value, tracker.track)
require.NoError(t, err)
assert.Equal(
t,
V(map[string]Value{
"bar": V("baz"),
}),
out,
)
// The callback should have been called for the root and every key in the map.
assert.Len(t, tracker.calls, 3)
// Calls 2 and 3 have been made for the keys in the map.
assert.ElementsMatch(t,
[]Path{
tracker.calls[1].path,
tracker.calls[2].path,
}, []Path{
MustPathFromString(".foo"),
MustPathFromString(".bar"),
})
}
func TestWalkMapError(t *testing.T) {
var tracker walkCallTracker
// Return an error from the callback for key "foo".
cerr := errors.New("error!")
tracker.on(".foo", func(v Value) Value { return v }, cerr)
value := V(map[string]Value{
"foo": V("bar"),
})
out, err := Walk(value, tracker.track)
assert.Equal(t, cerr, err)
assert.Equal(t, InvalidValue, out)
// The callback should have been called twice.
assert.Len(t, tracker.calls, 2)
// The second call was for the value at key "foo".
assert.Equal(t, MustPathFromString(".foo"), tracker.calls[1].path)
}
func TestWalkSequenceSkip(t *testing.T) {
var tracker walkCallTracker
// Skip traversal of the root value.
tracker.returnSkip(".")
value := V([]Value{
V("foo"),
V("bar"),
})
out, err := Walk(value, tracker.track)
require.NoError(t, err)
assert.Equal(
t,
V([]Value{
V("foo"),
V("bar"),
}),
out,
)
// The callback should have been called once.
assert.Len(t, tracker.calls, 1)
// The call should have been made with the empty path.
assert.Equal(t, EmptyPath, tracker.calls[0].path)
// The value should be the same as the input.
assert.Equal(t, value, tracker.calls[0].value)
}
func TestWalkSequenceDrop(t *testing.T) {
var tracker walkCallTracker
// Drop the value at index 1.
tracker.returnDrop(".[1]")
value := V([]Value{
V("foo"),
V("bar"),
V("baz"),
})
out, err := Walk(value, tracker.track)
require.NoError(t, err)
assert.Equal(
t,
V([]Value{
V("foo"),
V("baz"),
}),
out,
)
// The callback should have been called for the root and every value in the sequence.
assert.Len(t, tracker.calls, 4)
// The second call was for the value at index 0.
assert.Equal(t, MustPathFromString(".[0]"), tracker.calls[1].path)
assert.Equal(t, V("foo"), tracker.calls[1].value)
// The third call was for the value at index 1.
assert.Equal(t, MustPathFromString(".[1]"), tracker.calls[2].path)
assert.Equal(t, V("bar"), tracker.calls[2].value)
// The fourth call was for the value at index 2.
assert.Equal(t, MustPathFromString(".[2]"), tracker.calls[3].path)
assert.Equal(t, V("baz"), tracker.calls[3].value)
}
func TestWalkSequenceError(t *testing.T) {
var tracker walkCallTracker
// Return an error from the callback for index 1.
cerr := errors.New("error!")
tracker.on(".[1]", func(v Value) Value { return v }, cerr)
value := V([]Value{
V("foo"),
V("bar"),
})
out, err := Walk(value, tracker.track)
assert.Equal(t, cerr, err)
assert.Equal(t, InvalidValue, out)
// The callback should have been called three times.
assert.Len(t, tracker.calls, 3)
// The second call was for the value at index 0.
assert.Equal(t, MustPathFromString(".[0]"), tracker.calls[1].path)
assert.Equal(t, V("foo"), tracker.calls[1].value)
// The third call was for the value at index 1.
assert.Equal(t, MustPathFromString(".[1]"), tracker.calls[2].path)
assert.Equal(t, V("bar"), tracker.calls[2].value)
}