6c4344623b
The main HCL package is more visible this way, and so it's easier than having to pick it out from dozens of other package directories.
384 lines
11 KiB
Go
384 lines
11 KiB
Go
package dynblock
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hcldec"
|
|
"github.com/hashicorp/hcl/v2/hcltest"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestExpand(t *testing.T) {
|
|
srcBody := hcltest.MockBody(&hcl.BodyContent{
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "a",
|
|
Labels: []string{"static0"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val": hcltest.MockExprLiteral(cty.StringVal("static a 0")),
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
Type: "b",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "c",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val0": hcltest.MockExprLiteral(cty.StringVal("static c 0")),
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"c"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
|
|
cty.StringVal("dynamic c 0"),
|
|
cty.StringVal("dynamic c 1"),
|
|
})),
|
|
"iterator": hcltest.MockExprVariable("dyn_c"),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val0": hcltest.MockExprTraversalSrc("dyn_c.value"),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"a"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
|
|
cty.StringVal("dynamic a 0"),
|
|
cty.StringVal("dynamic a 1"),
|
|
cty.StringVal("dynamic a 2"),
|
|
})),
|
|
"labels": hcltest.MockExprList([]hcl.Expression{
|
|
hcltest.MockExprTraversalSrc("a.key"),
|
|
}),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val": hcltest.MockExprTraversalSrc("a.value"),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"b"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
|
|
cty.StringVal("dynamic b 0"),
|
|
cty.StringVal("dynamic b 1"),
|
|
})),
|
|
"iterator": hcltest.MockExprVariable("dyn_b"),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "c",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val0": hcltest.MockExprLiteral(cty.StringVal("static c 1")),
|
|
"val1": hcltest.MockExprTraversalSrc("dyn_b.value"),
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"c"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
|
|
cty.StringVal("dynamic c 2"),
|
|
cty.StringVal("dynamic c 3"),
|
|
})),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val0": hcltest.MockExprTraversalSrc("c.value"),
|
|
"val1": hcltest.MockExprTraversalSrc("dyn_b.value"),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"b"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
|
|
"foo": cty.ListVal([]cty.Value{
|
|
cty.StringVal("dynamic c nested 0"),
|
|
cty.StringVal("dynamic c nested 1"),
|
|
}),
|
|
})),
|
|
"iterator": hcltest.MockExprVariable("dyn_b"),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"c"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprTraversalSrc("dyn_b.value"),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val0": hcltest.MockExprTraversalSrc("c.value"),
|
|
"val1": hcltest.MockExprTraversalSrc("dyn_b.key"),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"b"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))),
|
|
"iterator": hcltest.MockExprVariable("dyn_b"),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "dynamic",
|
|
Labels: []string{"c"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"for_each": hcltest.MockExprTraversalSrc("dyn_b.value"),
|
|
}),
|
|
Blocks: hcl.Blocks{
|
|
{
|
|
Type: "content",
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val0": hcltest.MockExprTraversalSrc("c.value"),
|
|
"val1": hcltest.MockExprTraversalSrc("dyn_b.key"),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
{
|
|
Type: "a",
|
|
Labels: []string{"static1"},
|
|
LabelRanges: []hcl.Range{hcl.Range{}},
|
|
Body: hcltest.MockBody(&hcl.BodyContent{
|
|
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
|
|
"val": hcltest.MockExprLiteral(cty.StringVal("static a 1")),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
})
|
|
|
|
dynBody := Expand(srcBody, nil)
|
|
var remain hcl.Body
|
|
|
|
t.Run("PartialDecode", func(t *testing.T) {
|
|
decSpec := &hcldec.BlockMapSpec{
|
|
TypeName: "a",
|
|
LabelNames: []string{"key"},
|
|
Nested: &hcldec.AttrSpec{
|
|
Name: "val",
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
}
|
|
|
|
var got cty.Value
|
|
var diags hcl.Diagnostics
|
|
got, remain, diags = hcldec.PartialDecode(dynBody, decSpec, nil)
|
|
if len(diags) != 0 {
|
|
t.Errorf("unexpected diagnostics")
|
|
for _, diag := range diags {
|
|
t.Logf("- %s", diag)
|
|
}
|
|
return
|
|
}
|
|
|
|
want := cty.MapVal(map[string]cty.Value{
|
|
"static0": cty.StringVal("static a 0"),
|
|
"static1": cty.StringVal("static a 1"),
|
|
"0": cty.StringVal("dynamic a 0"),
|
|
"1": cty.StringVal("dynamic a 1"),
|
|
"2": cty.StringVal("dynamic a 2"),
|
|
})
|
|
|
|
if !got.RawEquals(want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("Decode", func(t *testing.T) {
|
|
decSpec := &hcldec.BlockListSpec{
|
|
TypeName: "b",
|
|
Nested: &hcldec.BlockListSpec{
|
|
TypeName: "c",
|
|
Nested: &hcldec.ObjectSpec{
|
|
"val0": &hcldec.AttrSpec{
|
|
Name: "val0",
|
|
Type: cty.String,
|
|
},
|
|
"val1": &hcldec.AttrSpec{
|
|
Name: "val1",
|
|
Type: cty.String,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
var got cty.Value
|
|
var diags hcl.Diagnostics
|
|
got, diags = hcldec.Decode(remain, decSpec, nil)
|
|
if len(diags) != 0 {
|
|
t.Errorf("unexpected diagnostics")
|
|
for _, diag := range diags {
|
|
t.Logf("- %s", diag)
|
|
}
|
|
return
|
|
}
|
|
|
|
want := cty.ListVal([]cty.Value{
|
|
cty.ListVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("static c 0"),
|
|
"val1": cty.NullVal(cty.String),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c 0"),
|
|
"val1": cty.NullVal(cty.String),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c 1"),
|
|
"val1": cty.NullVal(cty.String),
|
|
}),
|
|
}),
|
|
cty.ListVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("static c 1"),
|
|
"val1": cty.StringVal("dynamic b 0"),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c 2"),
|
|
"val1": cty.StringVal("dynamic b 0"),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c 3"),
|
|
"val1": cty.StringVal("dynamic b 0"),
|
|
}),
|
|
}),
|
|
cty.ListVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("static c 1"),
|
|
"val1": cty.StringVal("dynamic b 1"),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c 2"),
|
|
"val1": cty.StringVal("dynamic b 1"),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c 3"),
|
|
"val1": cty.StringVal("dynamic b 1"),
|
|
}),
|
|
}),
|
|
cty.ListVal([]cty.Value{
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c nested 0"),
|
|
"val1": cty.StringVal("foo"),
|
|
}),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.StringVal("dynamic c nested 1"),
|
|
"val1": cty.StringVal("foo"),
|
|
}),
|
|
}),
|
|
cty.ListVal([]cty.Value{
|
|
// This one comes from a dynamic block with an unknown for_each
|
|
// value, so we produce a single block object with all of the
|
|
// leaf attribute values set to unknown values.
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val0": cty.UnknownVal(cty.String),
|
|
"val1": cty.UnknownVal(cty.String),
|
|
}),
|
|
}),
|
|
})
|
|
|
|
if !got.RawEquals(want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want)
|
|
}
|
|
})
|
|
|
|
}
|