github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/genconfig/generate_config_test.go (about) 1 package genconfig 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 "github.com/zclconf/go-cty/cty" 9 10 "github.com/terramate-io/tf/addrs" 11 "github.com/terramate-io/tf/configs/configschema" 12 ) 13 14 func TestConfigGeneration(t *testing.T) { 15 tcs := map[string]struct { 16 schema *configschema.Block 17 addr addrs.AbsResourceInstance 18 provider addrs.LocalProviderConfig 19 value cty.Value 20 expected string 21 }{ 22 "simple_resource": { 23 schema: &configschema.Block{ 24 BlockTypes: map[string]*configschema.NestedBlock{ 25 "list_block": { 26 Block: configschema.Block{ 27 Attributes: map[string]*configschema.Attribute{ 28 "nested_value": { 29 Type: cty.String, 30 Optional: true, 31 }, 32 }, 33 }, 34 Nesting: configschema.NestingSingle, 35 }, 36 }, 37 Attributes: map[string]*configschema.Attribute{ 38 "id": { 39 Type: cty.String, 40 Computed: true, 41 }, 42 "value": { 43 Type: cty.String, 44 Optional: true, 45 }, 46 }, 47 }, 48 addr: addrs.AbsResourceInstance{ 49 Module: nil, 50 Resource: addrs.ResourceInstance{ 51 Resource: addrs.Resource{ 52 Mode: addrs.ManagedResourceMode, 53 Type: "tfcoremock_simple_resource", 54 Name: "empty", 55 }, 56 Key: nil, 57 }, 58 }, 59 provider: addrs.LocalProviderConfig{ 60 LocalName: "tfcoremock", 61 }, 62 value: cty.NilVal, 63 expected: ` 64 resource "tfcoremock_simple_resource" "empty" { 65 value = null # OPTIONAL string 66 list_block { # OPTIONAL block 67 nested_value = null # OPTIONAL string 68 } 69 }`, 70 }, 71 "simple_resource_with_state": { 72 schema: &configschema.Block{ 73 BlockTypes: map[string]*configschema.NestedBlock{ 74 "list_block": { 75 Block: configschema.Block{ 76 Attributes: map[string]*configschema.Attribute{ 77 "nested_value": { 78 Type: cty.String, 79 Optional: true, 80 }, 81 }, 82 }, 83 Nesting: configschema.NestingSingle, 84 }, 85 }, 86 Attributes: map[string]*configschema.Attribute{ 87 "id": { 88 Type: cty.String, 89 Computed: true, 90 }, 91 "value": { 92 Type: cty.String, 93 Optional: true, 94 }, 95 }, 96 }, 97 addr: addrs.AbsResourceInstance{ 98 Module: nil, 99 Resource: addrs.ResourceInstance{ 100 Resource: addrs.Resource{ 101 Mode: addrs.ManagedResourceMode, 102 Type: "tfcoremock_simple_resource", 103 Name: "empty", 104 }, 105 Key: nil, 106 }, 107 }, 108 provider: addrs.LocalProviderConfig{ 109 LocalName: "tfcoremock", 110 }, 111 value: cty.ObjectVal(map[string]cty.Value{ 112 "id": cty.StringVal("D2320658"), 113 "value": cty.StringVal("Hello, world!"), 114 "list_block": cty.ObjectVal(map[string]cty.Value{ 115 "nested_value": cty.StringVal("Hello, solar system!"), 116 }), 117 }), 118 expected: ` 119 resource "tfcoremock_simple_resource" "empty" { 120 value = "Hello, world!" 121 list_block { 122 nested_value = "Hello, solar system!" 123 } 124 }`, 125 }, 126 "simple_resource_with_partial_state": { 127 schema: &configschema.Block{ 128 BlockTypes: map[string]*configschema.NestedBlock{ 129 "list_block": { 130 Block: configschema.Block{ 131 Attributes: map[string]*configschema.Attribute{ 132 "nested_value": { 133 Type: cty.String, 134 Optional: true, 135 }, 136 }, 137 }, 138 Nesting: configschema.NestingSingle, 139 }, 140 }, 141 Attributes: map[string]*configschema.Attribute{ 142 "id": { 143 Type: cty.String, 144 Computed: true, 145 }, 146 "value": { 147 Type: cty.String, 148 Optional: true, 149 }, 150 }, 151 }, 152 addr: addrs.AbsResourceInstance{ 153 Module: nil, 154 Resource: addrs.ResourceInstance{ 155 Resource: addrs.Resource{ 156 Mode: addrs.ManagedResourceMode, 157 Type: "tfcoremock_simple_resource", 158 Name: "empty", 159 }, 160 Key: nil, 161 }, 162 }, 163 provider: addrs.LocalProviderConfig{ 164 LocalName: "tfcoremock", 165 }, 166 value: cty.ObjectVal(map[string]cty.Value{ 167 "id": cty.StringVal("D2320658"), 168 "list_block": cty.ObjectVal(map[string]cty.Value{ 169 "nested_value": cty.StringVal("Hello, solar system!"), 170 }), 171 }), 172 expected: ` 173 resource "tfcoremock_simple_resource" "empty" { 174 value = null 175 list_block { 176 nested_value = "Hello, solar system!" 177 } 178 }`, 179 }, 180 "simple_resource_with_alternate_provider": { 181 schema: &configschema.Block{ 182 BlockTypes: map[string]*configschema.NestedBlock{ 183 "list_block": { 184 Block: configschema.Block{ 185 Attributes: map[string]*configschema.Attribute{ 186 "nested_value": { 187 Type: cty.String, 188 Optional: true, 189 }, 190 }, 191 }, 192 Nesting: configschema.NestingSingle, 193 }, 194 }, 195 Attributes: map[string]*configschema.Attribute{ 196 "id": { 197 Type: cty.String, 198 Computed: true, 199 }, 200 "value": { 201 Type: cty.String, 202 Optional: true, 203 }, 204 }, 205 }, 206 addr: addrs.AbsResourceInstance{ 207 Module: nil, 208 Resource: addrs.ResourceInstance{ 209 Resource: addrs.Resource{ 210 Mode: addrs.ManagedResourceMode, 211 Type: "tfcoremock_simple_resource", 212 Name: "empty", 213 }, 214 Key: nil, 215 }, 216 }, 217 provider: addrs.LocalProviderConfig{ 218 LocalName: "mock", 219 }, 220 value: cty.ObjectVal(map[string]cty.Value{ 221 "id": cty.StringVal("D2320658"), 222 "value": cty.StringVal("Hello, world!"), 223 "list_block": cty.ObjectVal(map[string]cty.Value{ 224 "nested_value": cty.StringVal("Hello, solar system!"), 225 }), 226 }), 227 expected: ` 228 resource "tfcoremock_simple_resource" "empty" { 229 provider = mock 230 value = "Hello, world!" 231 list_block { 232 nested_value = "Hello, solar system!" 233 } 234 }`, 235 }, 236 "simple_resource_with_aliased_provider": { 237 schema: &configschema.Block{ 238 BlockTypes: map[string]*configschema.NestedBlock{ 239 "list_block": { 240 Block: configschema.Block{ 241 Attributes: map[string]*configschema.Attribute{ 242 "nested_value": { 243 Type: cty.String, 244 Optional: true, 245 }, 246 }, 247 }, 248 Nesting: configschema.NestingSingle, 249 }, 250 }, 251 Attributes: map[string]*configschema.Attribute{ 252 "id": { 253 Type: cty.String, 254 Computed: true, 255 }, 256 "value": { 257 Type: cty.String, 258 Optional: true, 259 }, 260 }, 261 }, 262 addr: addrs.AbsResourceInstance{ 263 Module: nil, 264 Resource: addrs.ResourceInstance{ 265 Resource: addrs.Resource{ 266 Mode: addrs.ManagedResourceMode, 267 Type: "tfcoremock_simple_resource", 268 Name: "empty", 269 }, 270 Key: nil, 271 }, 272 }, 273 provider: addrs.LocalProviderConfig{ 274 LocalName: "tfcoremock", 275 Alias: "alternate", 276 }, 277 value: cty.ObjectVal(map[string]cty.Value{ 278 "id": cty.StringVal("D2320658"), 279 "value": cty.StringVal("Hello, world!"), 280 "list_block": cty.ObjectVal(map[string]cty.Value{ 281 "nested_value": cty.StringVal("Hello, solar system!"), 282 }), 283 }), 284 expected: ` 285 resource "tfcoremock_simple_resource" "empty" { 286 provider = tfcoremock.alternate 287 value = "Hello, world!" 288 list_block { 289 nested_value = "Hello, solar system!" 290 } 291 }`, 292 }, 293 "resource_with_nulls": { 294 schema: &configschema.Block{ 295 Attributes: map[string]*configschema.Attribute{ 296 "id": { 297 Type: cty.String, 298 Computed: true, 299 }, 300 "single": { 301 NestedType: &configschema.Object{ 302 Attributes: map[string]*configschema.Attribute{}, 303 Nesting: configschema.NestingSingle, 304 }, 305 Required: true, 306 }, 307 "list": { 308 NestedType: &configschema.Object{ 309 Attributes: map[string]*configschema.Attribute{ 310 "nested_id": { 311 Type: cty.String, 312 Optional: true, 313 }, 314 }, 315 Nesting: configschema.NestingList, 316 }, 317 Required: true, 318 }, 319 "map": { 320 NestedType: &configschema.Object{ 321 Attributes: map[string]*configschema.Attribute{ 322 "nested_id": { 323 Type: cty.String, 324 Optional: true, 325 }, 326 }, 327 Nesting: configschema.NestingMap, 328 }, 329 Required: true, 330 }, 331 }, 332 BlockTypes: map[string]*configschema.NestedBlock{ 333 "nested_single": { 334 Nesting: configschema.NestingSingle, 335 Block: configschema.Block{ 336 Attributes: map[string]*configschema.Attribute{ 337 "nested_id": { 338 Type: cty.String, 339 Optional: true, 340 }, 341 }, 342 }, 343 }, 344 // No configschema.NestingGroup example for this test, because this block type can never be null in state. 345 "nested_list": { 346 Nesting: configschema.NestingList, 347 Block: configschema.Block{ 348 Attributes: map[string]*configschema.Attribute{ 349 "nested_id": { 350 Type: cty.String, 351 Optional: true, 352 }, 353 }, 354 }, 355 }, 356 "nested_set": { 357 Nesting: configschema.NestingSet, 358 Block: configschema.Block{ 359 Attributes: map[string]*configschema.Attribute{ 360 "nested_id": { 361 Type: cty.String, 362 Optional: true, 363 }, 364 }, 365 }, 366 }, 367 "nested_map": { 368 Nesting: configschema.NestingMap, 369 Block: configschema.Block{ 370 Attributes: map[string]*configschema.Attribute{ 371 "nested_id": { 372 Type: cty.String, 373 Optional: true, 374 }, 375 }, 376 }, 377 }, 378 }, 379 }, 380 addr: addrs.AbsResourceInstance{ 381 Module: nil, 382 Resource: addrs.ResourceInstance{ 383 Resource: addrs.Resource{ 384 Mode: addrs.ManagedResourceMode, 385 Type: "tfcoremock_simple_resource", 386 Name: "empty", 387 }, 388 Key: nil, 389 }, 390 }, 391 provider: addrs.LocalProviderConfig{ 392 LocalName: "tfcoremock", 393 }, 394 value: cty.ObjectVal(map[string]cty.Value{ 395 "id": cty.StringVal("D2320658"), 396 "single": cty.NullVal(cty.Object(map[string]cty.Type{})), 397 "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ 398 "nested_id": cty.String, 399 }))), 400 "map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ 401 "nested_id": cty.String, 402 }))), 403 "nested_single": cty.NullVal(cty.Object(map[string]cty.Type{ 404 "nested_id": cty.String, 405 })), 406 "nested_list": cty.ListValEmpty(cty.Object(map[string]cty.Type{ 407 "nested_id": cty.String, 408 })), 409 "nested_set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ 410 "nested_id": cty.String, 411 })), 412 "nested_map": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 413 "nested_id": cty.String, 414 })), 415 }), 416 expected: ` 417 resource "tfcoremock_simple_resource" "empty" { 418 list = null 419 map = null 420 single = null 421 }`, 422 }, 423 } 424 for name, tc := range tcs { 425 t.Run(name, func(t *testing.T) { 426 err := tc.schema.InternalValidate() 427 if err != nil { 428 t.Fatalf("schema failed InternalValidate: %s", err) 429 } 430 contents, diags := GenerateResourceContents(tc.addr, tc.schema, tc.provider, tc.value) 431 if len(diags) > 0 { 432 t.Errorf("expected no diagnostics but found %s", diags) 433 } 434 435 got := WrapResourceContents(tc.addr, contents) 436 want := strings.TrimSpace(tc.expected) 437 if diff := cmp.Diff(got, want); len(diff) > 0 { 438 t.Errorf("got:\n%s\nwant:\n%s\ndiff:\n%s", got, want, diff) 439 } 440 }) 441 } 442 }