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 }