github.com/terraform-linters/tflint@v0.51.2-0.20240520175844-3750771571b6/terraform/tfhcl/expand_body_test.go (about)

     1  package tfhcl
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/hashicorp/hcl/v2/hcldec"
     8  	"github.com/hashicorp/hcl/v2/hcltest"
     9  	"github.com/zclconf/go-cty/cty"
    10  )
    11  
    12  func TestExpand(t *testing.T) {
    13  	srcBody := hcltest.MockBody(&hcl.BodyContent{
    14  		Blocks: hcl.Blocks{
    15  			{
    16  				Type:        "a",
    17  				Labels:      []string{"static0"},
    18  				LabelRanges: []hcl.Range{hcl.Range{}},
    19  				Body: hcltest.MockBody(&hcl.BodyContent{
    20  					Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
    21  						"val": hcltest.MockExprLiteral(cty.StringVal("static a 0")),
    22  					}),
    23  				}),
    24  			},
    25  			{
    26  				Type: "b",
    27  				Body: hcltest.MockBody(&hcl.BodyContent{
    28  					Blocks: hcl.Blocks{
    29  						{
    30  							Type: "c",
    31  							Body: hcltest.MockBody(&hcl.BodyContent{
    32  								Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
    33  									"val0": hcltest.MockExprLiteral(cty.StringVal("static c 0")),
    34  								}),
    35  							}),
    36  						},
    37  						{
    38  							Type:        "dynamic",
    39  							Labels:      []string{"c"},
    40  							LabelRanges: []hcl.Range{hcl.Range{}},
    41  							Body: hcltest.MockBody(&hcl.BodyContent{
    42  								Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
    43  									"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
    44  										cty.StringVal("dynamic c 0"),
    45  										cty.StringVal("dynamic c 1"),
    46  									})),
    47  									"iterator": hcltest.MockExprVariable("dyn_c"),
    48  								}),
    49  								Blocks: hcl.Blocks{
    50  									{
    51  										Type: "content",
    52  										Body: hcltest.MockBody(&hcl.BodyContent{
    53  											Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
    54  												"val0": hcltest.MockExprTraversalSrc("dyn_c.value"),
    55  											}),
    56  										}),
    57  									},
    58  								},
    59  							}),
    60  						},
    61  					},
    62  				}),
    63  			},
    64  			{
    65  				Type:        "dynamic",
    66  				Labels:      []string{"a"},
    67  				LabelRanges: []hcl.Range{hcl.Range{}},
    68  				Body: hcltest.MockBody(&hcl.BodyContent{
    69  					Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
    70  						"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
    71  							cty.StringVal("dynamic a 0"),
    72  							cty.StringVal("dynamic a 1"),
    73  							cty.StringVal("dynamic a 2"),
    74  						})),
    75  						"labels": hcltest.MockExprList([]hcl.Expression{
    76  							hcltest.MockExprTraversalSrc("a.key"),
    77  						}),
    78  					}),
    79  					Blocks: hcl.Blocks{
    80  						{
    81  							Type: "content",
    82  							Body: hcltest.MockBody(&hcl.BodyContent{
    83  								Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
    84  									"val": hcltest.MockExprTraversalSrc("a.value"),
    85  								}),
    86  							}),
    87  						},
    88  					},
    89  				}),
    90  			},
    91  			{
    92  				Type:        "dynamic",
    93  				Labels:      []string{"b"},
    94  				LabelRanges: []hcl.Range{hcl.Range{}},
    95  				Body: hcltest.MockBody(&hcl.BodyContent{
    96  					Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
    97  						"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
    98  							cty.StringVal("dynamic b 0"),
    99  							cty.StringVal("dynamic b 1"),
   100  						})),
   101  						"iterator": hcltest.MockExprVariable("dyn_b"),
   102  					}),
   103  					Blocks: hcl.Blocks{
   104  						{
   105  							Type: "content",
   106  							Body: hcltest.MockBody(&hcl.BodyContent{
   107  								Blocks: hcl.Blocks{
   108  									{
   109  										Type: "c",
   110  										Body: hcltest.MockBody(&hcl.BodyContent{
   111  											Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
   112  												"val0": hcltest.MockExprLiteral(cty.StringVal("static c 1")),
   113  												"val1": hcltest.MockExprTraversalSrc("dyn_b.value"),
   114  											}),
   115  										}),
   116  									},
   117  									{
   118  										Type:        "dynamic",
   119  										Labels:      []string{"c"},
   120  										LabelRanges: []hcl.Range{hcl.Range{}},
   121  										Body: hcltest.MockBody(&hcl.BodyContent{
   122  											Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
   123  												"for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{
   124  													cty.StringVal("dynamic c 2"),
   125  													cty.StringVal("dynamic c 3"),
   126  												})),
   127  											}),
   128  											Blocks: hcl.Blocks{
   129  												{
   130  													Type: "content",
   131  													Body: hcltest.MockBody(&hcl.BodyContent{
   132  														Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
   133  															"val0": hcltest.MockExprTraversalSrc("c.value"),
   134  															"val1": hcltest.MockExprTraversalSrc("dyn_b.value"),
   135  														}),
   136  													}),
   137  												},
   138  											},
   139  										}),
   140  									},
   141  								},
   142  							}),
   143  						},
   144  					},
   145  				}),
   146  			},
   147  			{
   148  				Type:        "dynamic",
   149  				Labels:      []string{"b"},
   150  				LabelRanges: []hcl.Range{hcl.Range{}},
   151  				Body: hcltest.MockBody(&hcl.BodyContent{
   152  					Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
   153  						"for_each": hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
   154  							"foo": cty.ListVal([]cty.Value{
   155  								cty.StringVal("dynamic c nested 0"),
   156  								cty.StringVal("dynamic c nested 1"),
   157  							}),
   158  						})),
   159  						"iterator": hcltest.MockExprVariable("dyn_b"),
   160  					}),
   161  					Blocks: hcl.Blocks{
   162  						{
   163  							Type: "content",
   164  							Body: hcltest.MockBody(&hcl.BodyContent{
   165  								Blocks: hcl.Blocks{
   166  									{
   167  										Type:        "dynamic",
   168  										Labels:      []string{"c"},
   169  										LabelRanges: []hcl.Range{hcl.Range{}},
   170  										Body: hcltest.MockBody(&hcl.BodyContent{
   171  											Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
   172  												"for_each": hcltest.MockExprTraversalSrc("dyn_b.value"),
   173  											}),
   174  											Blocks: hcl.Blocks{
   175  												{
   176  													Type: "content",
   177  													Body: hcltest.MockBody(&hcl.BodyContent{
   178  														Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
   179  															"val0": hcltest.MockExprTraversalSrc("c.value"),
   180  															"val1": hcltest.MockExprTraversalSrc("dyn_b.key"),
   181  														}),
   182  													}),
   183  												},
   184  											},
   185  										}),
   186  									},
   187  								},
   188  							}),
   189  						},
   190  					},
   191  				}),
   192  			},
   193  			{
   194  				Type:        "a",
   195  				Labels:      []string{"static1"},
   196  				LabelRanges: []hcl.Range{hcl.Range{}},
   197  				Body: hcltest.MockBody(&hcl.BodyContent{
   198  					Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
   199  						"val": hcltest.MockExprLiteral(cty.StringVal("static a 1")),
   200  					}),
   201  				}),
   202  			},
   203  		},
   204  	})
   205  
   206  	dynBody := Expand(srcBody, nil)
   207  	var remain hcl.Body
   208  
   209  	t.Run("PartialDecode", func(t *testing.T) {
   210  		decSpec := &hcldec.BlockMapSpec{
   211  			TypeName:   "a",
   212  			LabelNames: []string{"key"},
   213  			Nested: &hcldec.AttrSpec{
   214  				Name:     "val",
   215  				Type:     cty.String,
   216  				Required: true,
   217  			},
   218  		}
   219  
   220  		var got cty.Value
   221  		var diags hcl.Diagnostics
   222  		got, remain, diags = hcldec.PartialDecode(dynBody, decSpec, nil)
   223  		if len(diags) != 0 {
   224  			t.Errorf("unexpected diagnostics")
   225  			for _, diag := range diags {
   226  				t.Logf("- %s", diag)
   227  			}
   228  			return
   229  		}
   230  
   231  		want := cty.MapVal(map[string]cty.Value{
   232  			"static0": cty.StringVal("static a 0"),
   233  			"static1": cty.StringVal("static a 1"),
   234  			"0":       cty.StringVal("dynamic a 0"),
   235  			"1":       cty.StringVal("dynamic a 1"),
   236  			"2":       cty.StringVal("dynamic a 2"),
   237  		})
   238  
   239  		if !got.RawEquals(want) {
   240  			t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, want)
   241  		}
   242  	})
   243  
   244  	t.Run("Decode", func(t *testing.T) {
   245  		decSpec := &hcldec.BlockListSpec{
   246  			TypeName: "b",
   247  			Nested: &hcldec.BlockListSpec{
   248  				TypeName: "c",
   249  				Nested: &hcldec.ObjectSpec{
   250  					"val0": &hcldec.AttrSpec{
   251  						Name: "val0",
   252  						Type: cty.String,
   253  					},
   254  					"val1": &hcldec.AttrSpec{
   255  						Name: "val1",
   256  						Type: cty.String,
   257  					},
   258  				},
   259  			},
   260  		}
   261  
   262  		var got cty.Value
   263  		var diags hcl.Diagnostics
   264  		got, diags = hcldec.Decode(remain, decSpec, nil)
   265  		if len(diags) != 0 {
   266  			t.Errorf("unexpected diagnostics")
   267  			for _, diag := range diags {
   268  				t.Logf("- %s", diag)
   269  			}
   270  			return
   271  		}
   272  
   273  		want := cty.ListVal([]cty.Value{
   274  			cty.ListVal([]cty.Value{
   275  				cty.ObjectVal(map[string]cty.Value{
   276  					"val0": cty.StringVal("static c 0"),
   277  					"val1": cty.NullVal(cty.String),
   278  				}),
   279  				cty.ObjectVal(map[string]cty.Value{
   280  					"val0": cty.StringVal("dynamic c 0"),
   281  					"val1": cty.NullVal(cty.String),
   282  				}),
   283  				cty.ObjectVal(map[string]cty.Value{
   284  					"val0": cty.StringVal("dynamic c 1"),
   285  					"val1": cty.NullVal(cty.String),
   286  				}),
   287  			}),
   288  			cty.ListVal([]cty.Value{
   289  				cty.ObjectVal(map[string]cty.Value{
   290  					"val0": cty.StringVal("static c 1"),
   291  					"val1": cty.StringVal("dynamic b 0"),
   292  				}),
   293  				cty.ObjectVal(map[string]cty.Value{
   294  					"val0": cty.StringVal("dynamic c 2"),
   295  					"val1": cty.StringVal("dynamic b 0"),
   296  				}),
   297  				cty.ObjectVal(map[string]cty.Value{
   298  					"val0": cty.StringVal("dynamic c 3"),
   299  					"val1": cty.StringVal("dynamic b 0"),
   300  				}),
   301  			}),
   302  			cty.ListVal([]cty.Value{
   303  				cty.ObjectVal(map[string]cty.Value{
   304  					"val0": cty.StringVal("static c 1"),
   305  					"val1": cty.StringVal("dynamic b 1"),
   306  				}),
   307  				cty.ObjectVal(map[string]cty.Value{
   308  					"val0": cty.StringVal("dynamic c 2"),
   309  					"val1": cty.StringVal("dynamic b 1"),
   310  				}),
   311  				cty.ObjectVal(map[string]cty.Value{
   312  					"val0": cty.StringVal("dynamic c 3"),
   313  					"val1": cty.StringVal("dynamic b 1"),
   314  				}),
   315  			}),
   316  			cty.ListVal([]cty.Value{
   317  				cty.ObjectVal(map[string]cty.Value{
   318  					"val0": cty.StringVal("dynamic c nested 0"),
   319  					"val1": cty.StringVal("foo"),
   320  				}),
   321  				cty.ObjectVal(map[string]cty.Value{
   322  					"val0": cty.StringVal("dynamic c nested 1"),
   323  					"val1": cty.StringVal("foo"),
   324  				}),
   325  			}),
   326  		})
   327  
   328  		if !got.RawEquals(want) {
   329  			t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, want)
   330  		}
   331  	})
   332  
   333  }