2022-12-01 08:33:42 +00:00
|
|
|
package interpolation
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
2023-05-16 16:35:39 +00:00
|
|
|
"github.com/databricks/cli/bundle/config"
|
|
|
|
"github.com/databricks/cli/bundle/config/variable"
|
2022-12-01 08:33:42 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
type nest struct {
|
|
|
|
X string `json:"x"`
|
|
|
|
Y *string `json:"y"`
|
|
|
|
Z map[string]string `json:"z"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type foo struct {
|
|
|
|
A string `json:"a"`
|
|
|
|
B string `json:"b"`
|
|
|
|
C string `json:"c"`
|
|
|
|
|
|
|
|
// Pointer field
|
|
|
|
D *string `json:"d"`
|
|
|
|
|
|
|
|
// Struct field
|
|
|
|
E nest `json:"e"`
|
|
|
|
|
|
|
|
// Map field
|
|
|
|
F map[string]string `json:"f"`
|
|
|
|
}
|
|
|
|
|
2022-12-01 21:38:49 +00:00
|
|
|
func expand(v any) error {
|
|
|
|
a := accumulator{}
|
|
|
|
a.start(v)
|
|
|
|
return a.expand(DefaultLookup)
|
|
|
|
}
|
|
|
|
|
2022-12-01 08:33:42 +00:00
|
|
|
func TestInterpolationVariables(t *testing.T) {
|
|
|
|
f := foo{
|
|
|
|
A: "a",
|
|
|
|
B: "${a}",
|
|
|
|
C: "${a}",
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&f)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "a", f.A)
|
|
|
|
assert.Equal(t, "a", f.B)
|
|
|
|
assert.Equal(t, "a", f.C)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInterpolationWithPointers(t *testing.T) {
|
|
|
|
fd := "${a}"
|
|
|
|
f := foo{
|
|
|
|
A: "a",
|
|
|
|
D: &fd,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&f)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "a", f.A)
|
|
|
|
assert.Equal(t, "a", *f.D)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInterpolationWithStruct(t *testing.T) {
|
|
|
|
fy := "${e.x}"
|
|
|
|
f := foo{
|
|
|
|
A: "${e.x}",
|
|
|
|
E: nest{
|
|
|
|
X: "x",
|
|
|
|
Y: &fy,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&f)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "x", f.A)
|
|
|
|
assert.Equal(t, "x", f.E.X)
|
|
|
|
assert.Equal(t, "x", *f.E.Y)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInterpolationWithMap(t *testing.T) {
|
|
|
|
f := foo{
|
|
|
|
A: "${f.a}",
|
|
|
|
F: map[string]string{
|
|
|
|
"a": "a",
|
|
|
|
"b": "${f.a}",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&f)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "a", f.A)
|
|
|
|
assert.Equal(t, "a", f.F["a"])
|
|
|
|
assert.Equal(t, "a", f.F["b"])
|
|
|
|
}
|
2023-04-19 23:13:33 +00:00
|
|
|
|
|
|
|
func TestInterpolationWithResursiveVariableReferences(t *testing.T) {
|
|
|
|
f := foo{
|
|
|
|
A: "a",
|
|
|
|
B: "(${a})",
|
|
|
|
C: "${a} ${b}",
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&f)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, "a", f.A)
|
|
|
|
assert.Equal(t, "(a)", f.B)
|
|
|
|
assert.Equal(t, "a (a)", f.C)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInterpolationVariableLoopError(t *testing.T) {
|
|
|
|
d := "${b}"
|
|
|
|
f := foo{
|
|
|
|
A: "a",
|
|
|
|
B: "${c}",
|
|
|
|
C: "${d}",
|
|
|
|
D: &d,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&f)
|
|
|
|
assert.ErrorContains(t, err, "cycle detected in field resolution: b -> c -> d -> b")
|
|
|
|
}
|
2023-05-15 09:34:05 +00:00
|
|
|
|
|
|
|
func TestInterpolationForVariables(t *testing.T) {
|
|
|
|
foo := "abc"
|
|
|
|
bar := "${var.foo} def"
|
|
|
|
apple := "${var.foo} ${var.bar}"
|
|
|
|
config := config.Root{
|
|
|
|
Variables: map[string]*variable.Variable{
|
|
|
|
"foo": {
|
|
|
|
Value: &foo,
|
|
|
|
},
|
|
|
|
"bar": {
|
|
|
|
Value: &bar,
|
|
|
|
},
|
|
|
|
"apple": {
|
|
|
|
Value: &apple,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Bundle: config.Bundle{
|
|
|
|
Name: "${var.apple} ${var.foo}",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&config)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, "abc", *(config.Variables["foo"].Value))
|
|
|
|
assert.Equal(t, "abc def", *(config.Variables["bar"].Value))
|
|
|
|
assert.Equal(t, "abc abc def", *(config.Variables["apple"].Value))
|
|
|
|
assert.Equal(t, "abc abc def abc", config.Bundle.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInterpolationLoopForVariables(t *testing.T) {
|
|
|
|
foo := "${var.bar}"
|
|
|
|
bar := "${var.foo}"
|
|
|
|
config := config.Root{
|
|
|
|
Variables: map[string]*variable.Variable{
|
|
|
|
"foo": {
|
|
|
|
Value: &foo,
|
|
|
|
},
|
|
|
|
"bar": {
|
|
|
|
Value: &bar,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Bundle: config.Bundle{
|
|
|
|
Name: "${var.foo}",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&config)
|
|
|
|
assert.ErrorContains(t, err, "cycle detected in field resolution: bundle.name -> var.foo -> var.bar -> var.foo")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInterpolationInvalidVariableReference(t *testing.T) {
|
|
|
|
foo := "abc"
|
|
|
|
config := config.Root{
|
|
|
|
Variables: map[string]*variable.Variable{
|
|
|
|
"foo": {
|
|
|
|
Value: &foo,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Bundle: config.Bundle{
|
|
|
|
Name: "${vars.foo}",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := expand(&config)
|
|
|
|
assert.ErrorContains(t, err, "could not resolve reference vars.foo")
|
|
|
|
}
|