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  }