package dynblock import ( "reflect" "testing" "github.com/hashicorp/hcl/v2/hcldec" "github.com/zclconf/go-cty/cty" "github.com/davecgh/go-spew/spew" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" ) func TestVariables(t *testing.T) { const src = ` # We have some references to things inside the "val" attribute inside each # of our "b" blocks, which should be included in the result of WalkVariables # but not WalkExpandVariables. a { dynamic "b" { for_each = [for i, v in some_list_0: "${i}=${v},${baz}"] labels = ["${b.value} ${something_else_0}"] content { val = "${b.value} ${something_else_1}" } } } dynamic "a" { for_each = some_list_1 content { b "foo" { val = "${a.value} ${something_else_2}" } dynamic "b" { for_each = some_list_2 iterator = dyn_b labels = ["${a.value} ${dyn_b.value} ${b} ${something_else_3}"] content { val = "${a.value} ${dyn_b.value} ${something_else_4}" } } } } dynamic "a" { for_each = some_list_3 iterator = dyn_a content { b "foo" { val = "${dyn_a.value} ${something_else_5}" } dynamic "b" { for_each = some_list_4 labels = ["${dyn_a.value} ${b.value} ${a} ${something_else_6}"] content { val = "${dyn_a.value} ${b.value} ${something_else_7}" } } } } ` f, diags := hclsyntax.ParseConfig([]byte(src), "", hcl.Pos{}) if len(diags) != 0 { t.Errorf("unexpected diagnostics during parse") for _, diag := range diags { t.Logf("- %s", diag) } return } spec := &hcldec.BlockListSpec{ TypeName: "a", Nested: &hcldec.BlockMapSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: &hcldec.AttrSpec{ Name: "val", Type: cty.String, }, }, } t.Run("WalkVariables", func(t *testing.T) { traversals := VariablesHCLDec(f.Body, spec) got := make([]string, len(traversals)) for i, traversal := range traversals { got[i] = traversal.RootName() } // The block structure is traversed one level at a time, so the ordering // here is reflecting first a pass of the root, then the first child // under the root, then the first child under that, etc. want := []string{ "some_list_1", "some_list_3", "some_list_0", "baz", "something_else_0", "something_else_1", // Would not be included for WalkExpandVariables because it only appears in content "some_list_2", "b", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_b "something_else_3", "something_else_2", // Would not be included for WalkExpandVariables because it only appears in content "something_else_4", // Would not be included for WalkExpandVariables because it only appears in content "some_list_4", "a", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_a "something_else_6", "something_else_5", // Would not be included for WalkExpandVariables because it only appears in content "something_else_7", // Would not be included for WalkExpandVariables because it only appears in content } if !reflect.DeepEqual(got, want) { t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) } }) t.Run("WalkExpandVariables", func(t *testing.T) { traversals := ExpandVariablesHCLDec(f.Body, spec) got := make([]string, len(traversals)) for i, traversal := range traversals { got[i] = traversal.RootName() } // The block structure is traversed one level at a time, so the ordering // here is reflecting first a pass of the root, then the first child // under the root, then the first child under that, etc. want := []string{ "some_list_1", "some_list_3", "some_list_0", "baz", "something_else_0", "some_list_2", "b", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_b "something_else_3", "some_list_4", "a", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_a "something_else_6", } if !reflect.DeepEqual(got, want) { t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) } }) }