github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/terraform/reduce_plan_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package terraform
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/hashicorp/hcl/v2"
    10  	"github.com/hashicorp/hcl/v2/hclsyntax"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestProcessIgnoreChangesIndividual(t *testing.T) {
    15  	tests := map[string]struct {
    16  		Old, New cty.Value
    17  		Ignore   []string
    18  		Want     cty.Value
    19  	}{
    20  		"string": {
    21  			cty.ObjectVal(map[string]cty.Value{
    22  				"a": cty.StringVal("a value"),
    23  				"b": cty.StringVal("b value"),
    24  			}),
    25  			cty.ObjectVal(map[string]cty.Value{
    26  				"a": cty.StringVal("new a value"),
    27  				"b": cty.StringVal("new b value"),
    28  			}),
    29  			[]string{"a"},
    30  			cty.ObjectVal(map[string]cty.Value{
    31  				"a": cty.StringVal("a value"),
    32  				"b": cty.StringVal("new b value"),
    33  			}),
    34  		},
    35  		"changed type": {
    36  			cty.ObjectVal(map[string]cty.Value{
    37  				"a": cty.StringVal("a value"),
    38  				"b": cty.StringVal("b value"),
    39  			}),
    40  			cty.ObjectVal(map[string]cty.Value{
    41  				"a": cty.NumberIntVal(1),
    42  				"b": cty.StringVal("new b value"),
    43  			}),
    44  			[]string{"a"},
    45  			cty.ObjectVal(map[string]cty.Value{
    46  				"a": cty.StringVal("a value"),
    47  				"b": cty.StringVal("new b value"),
    48  			}),
    49  		},
    50  		"list": {
    51  			cty.ObjectVal(map[string]cty.Value{
    52  				"a": cty.ListVal([]cty.Value{
    53  					cty.StringVal("a0 value"),
    54  					cty.StringVal("a1 value"),
    55  				}),
    56  				"b": cty.StringVal("b value"),
    57  			}),
    58  			cty.ObjectVal(map[string]cty.Value{
    59  				"a": cty.ListVal([]cty.Value{
    60  					cty.StringVal("new a0 value"),
    61  					cty.StringVal("new a1 value"),
    62  				}),
    63  				"b": cty.StringVal("new b value"),
    64  			}),
    65  			[]string{"a"},
    66  			cty.ObjectVal(map[string]cty.Value{
    67  				"a": cty.ListVal([]cty.Value{
    68  					cty.StringVal("a0 value"),
    69  					cty.StringVal("a1 value"),
    70  				}),
    71  				"b": cty.StringVal("new b value"),
    72  			}),
    73  		},
    74  		"list_index": {
    75  			cty.ObjectVal(map[string]cty.Value{
    76  				"a": cty.ListVal([]cty.Value{
    77  					cty.StringVal("a0 value"),
    78  					cty.StringVal("a1 value"),
    79  				}),
    80  				"b": cty.StringVal("b value"),
    81  			}),
    82  			cty.ObjectVal(map[string]cty.Value{
    83  				"a": cty.ListVal([]cty.Value{
    84  					cty.StringVal("new a0 value"),
    85  					cty.StringVal("new a1 value"),
    86  				}),
    87  				"b": cty.StringVal("new b value"),
    88  			}),
    89  			[]string{"a[1]"},
    90  			cty.ObjectVal(map[string]cty.Value{
    91  				"a": cty.ListVal([]cty.Value{
    92  					cty.StringVal("new a0 value"),
    93  					cty.StringVal("a1 value"),
    94  				}),
    95  				"b": cty.StringVal("new b value"),
    96  			}),
    97  		},
    98  		"map": {
    99  			cty.ObjectVal(map[string]cty.Value{
   100  				"a": cty.MapVal(map[string]cty.Value{
   101  					"a0": cty.StringVal("a0 value"),
   102  					"a1": cty.StringVal("a1 value"),
   103  				}),
   104  				"b": cty.StringVal("b value"),
   105  			}),
   106  			cty.ObjectVal(map[string]cty.Value{
   107  				"a": cty.MapVal(map[string]cty.Value{
   108  					"a0": cty.StringVal("new a0 value"),
   109  					"a1": cty.UnknownVal(cty.String),
   110  				}),
   111  				"b": cty.StringVal("b value"),
   112  			}),
   113  			[]string{`a`},
   114  			cty.ObjectVal(map[string]cty.Value{
   115  				"a": cty.MapVal(map[string]cty.Value{
   116  					"a0": cty.StringVal("a0 value"),
   117  					"a1": cty.StringVal("a1 value"),
   118  				}),
   119  				"b": cty.StringVal("b value"),
   120  			}),
   121  		},
   122  		"map_index": {
   123  			cty.ObjectVal(map[string]cty.Value{
   124  				"a": cty.MapVal(map[string]cty.Value{
   125  					"a0": cty.StringVal("a0 value"),
   126  					"a1": cty.StringVal("a1 value"),
   127  				}),
   128  				"b": cty.StringVal("b value"),
   129  			}),
   130  			cty.ObjectVal(map[string]cty.Value{
   131  				"a": cty.MapVal(map[string]cty.Value{
   132  					"a0": cty.StringVal("new a0 value"),
   133  					"a1": cty.StringVal("new a1 value"),
   134  				}),
   135  				"b": cty.StringVal("b value"),
   136  			}),
   137  			[]string{`a["a1"]`},
   138  			cty.ObjectVal(map[string]cty.Value{
   139  				"a": cty.MapVal(map[string]cty.Value{
   140  					"a0": cty.StringVal("new a0 value"),
   141  					"a1": cty.StringVal("a1 value"),
   142  				}),
   143  				"b": cty.StringVal("b value"),
   144  			}),
   145  		},
   146  		"map_index_no_config": {
   147  			cty.ObjectVal(map[string]cty.Value{
   148  				"a": cty.MapVal(map[string]cty.Value{
   149  					"a0": cty.StringVal("a0 value"),
   150  					"a1": cty.StringVal("a1 value"),
   151  				}),
   152  				"b": cty.StringVal("b value"),
   153  			}),
   154  			cty.ObjectVal(map[string]cty.Value{
   155  				"a": cty.NullVal(cty.Map(cty.String)),
   156  				"b": cty.StringVal("b value"),
   157  			}),
   158  			[]string{`a["a1"]`},
   159  			cty.ObjectVal(map[string]cty.Value{
   160  				"a": cty.MapVal(map[string]cty.Value{
   161  					"a1": cty.StringVal("a1 value"),
   162  				}),
   163  				"b": cty.StringVal("b value"),
   164  			}),
   165  		},
   166  		"map_index_unknown_value": {
   167  			cty.ObjectVal(map[string]cty.Value{
   168  				"a": cty.MapVal(map[string]cty.Value{
   169  					"a0": cty.StringVal("a0 value"),
   170  					"a1": cty.StringVal("a1 value"),
   171  				}),
   172  				"b": cty.StringVal("b value"),
   173  			}),
   174  			cty.ObjectVal(map[string]cty.Value{
   175  				"a": cty.MapVal(map[string]cty.Value{
   176  					"a0": cty.StringVal("a0 value"),
   177  					"a1": cty.UnknownVal(cty.String),
   178  				}),
   179  				"b": cty.StringVal("b value"),
   180  			}),
   181  			[]string{`a["a1"]`},
   182  			cty.ObjectVal(map[string]cty.Value{
   183  				"a": cty.MapVal(map[string]cty.Value{
   184  					"a0": cty.StringVal("a0 value"),
   185  					"a1": cty.StringVal("a1 value"),
   186  				}),
   187  				"b": cty.StringVal("b value"),
   188  			}),
   189  		},
   190  		"map_index_multiple_keys": {
   191  			cty.ObjectVal(map[string]cty.Value{
   192  				"a": cty.MapVal(map[string]cty.Value{
   193  					"a0": cty.StringVal("a0 value"),
   194  					"a1": cty.StringVal("a1 value"),
   195  					"a2": cty.StringVal("a2 value"),
   196  					"a3": cty.StringVal("a3 value"),
   197  				}),
   198  				"b": cty.StringVal("b value"),
   199  			}),
   200  			cty.ObjectVal(map[string]cty.Value{
   201  				"a": cty.NullVal(cty.Map(cty.String)),
   202  				"b": cty.StringVal("new b value"),
   203  			}),
   204  			[]string{`a["a1"]`, `a["a2"]`, `a["a3"]`, `b`},
   205  			cty.ObjectVal(map[string]cty.Value{
   206  				"a": cty.MapVal(map[string]cty.Value{
   207  					"a1": cty.StringVal("a1 value"),
   208  					"a2": cty.StringVal("a2 value"),
   209  					"a3": cty.StringVal("a3 value"),
   210  				}),
   211  				"b": cty.StringVal("b value"),
   212  			}),
   213  		},
   214  		"map_index_redundant": {
   215  			cty.ObjectVal(map[string]cty.Value{
   216  				"a": cty.MapVal(map[string]cty.Value{
   217  					"a0": cty.StringVal("a0 value"),
   218  					"a1": cty.StringVal("a1 value"),
   219  					"a2": cty.StringVal("a2 value"),
   220  				}),
   221  				"b": cty.StringVal("b value"),
   222  			}),
   223  			cty.ObjectVal(map[string]cty.Value{
   224  				"a": cty.NullVal(cty.Map(cty.String)),
   225  				"b": cty.StringVal("new b value"),
   226  			}),
   227  			[]string{`a["a1"]`, `a`, `b`},
   228  			cty.ObjectVal(map[string]cty.Value{
   229  				"a": cty.MapVal(map[string]cty.Value{
   230  					"a0": cty.StringVal("a0 value"),
   231  					"a1": cty.StringVal("a1 value"),
   232  					"a2": cty.StringVal("a2 value"),
   233  				}),
   234  				"b": cty.StringVal("b value"),
   235  			}),
   236  		},
   237  		"missing_map_index": {
   238  			cty.ObjectVal(map[string]cty.Value{
   239  				"a": cty.MapVal(map[string]cty.Value{
   240  					"a0": cty.StringVal("a0 value"),
   241  					"a1": cty.StringVal("a1 value"),
   242  				}),
   243  				"b": cty.StringVal("b value"),
   244  			}),
   245  			cty.ObjectVal(map[string]cty.Value{
   246  				"a": cty.MapValEmpty(cty.String),
   247  				"b": cty.StringVal("b value"),
   248  			}),
   249  			[]string{`a["a1"]`},
   250  			cty.ObjectVal(map[string]cty.Value{
   251  				"a": cty.MapVal(map[string]cty.Value{
   252  					"a1": cty.StringVal("a1 value"),
   253  				}),
   254  				"b": cty.StringVal("b value"),
   255  			}),
   256  		},
   257  		"missing_map_index_empty": {
   258  			cty.ObjectVal(map[string]cty.Value{
   259  				"a": cty.MapValEmpty(cty.String),
   260  			}),
   261  			cty.ObjectVal(map[string]cty.Value{
   262  				"a": cty.MapVal(map[string]cty.Value{
   263  					"a": cty.StringVal("a0 value"),
   264  				}),
   265  			}),
   266  			[]string{`a["a"]`},
   267  			cty.ObjectVal(map[string]cty.Value{
   268  				"a": cty.MapValEmpty(cty.String),
   269  			}),
   270  		},
   271  		"missing_map_index_to_object": {
   272  			cty.ObjectVal(map[string]cty.Value{
   273  				"a": cty.MapVal(map[string]cty.Value{
   274  					"a": cty.ObjectVal(map[string]cty.Value{
   275  						"a": cty.StringVal("aa0"),
   276  						"b": cty.StringVal("ab0"),
   277  					}),
   278  					"b": cty.ObjectVal(map[string]cty.Value{
   279  						"a": cty.StringVal("ba0"),
   280  						"b": cty.StringVal("bb0"),
   281  					}),
   282  				}),
   283  			}),
   284  			cty.ObjectVal(map[string]cty.Value{
   285  				"a": cty.MapValEmpty(
   286  					cty.Object(map[string]cty.Type{
   287  						"a": cty.String,
   288  						"b": cty.String,
   289  					}),
   290  				),
   291  			}),
   292  			// we expect the config to be used here, as the ignore changes was
   293  			// `a["a"].b`, but the change was larger than that removing
   294  			// `a["a"]` entirely.
   295  			[]string{`a["a"].b`},
   296  			cty.ObjectVal(map[string]cty.Value{
   297  				"a": cty.MapValEmpty(
   298  					cty.Object(map[string]cty.Type{
   299  						"a": cty.String,
   300  						"b": cty.String,
   301  					}),
   302  				),
   303  			}),
   304  		},
   305  		"missing_prior_map_index": {
   306  			cty.ObjectVal(map[string]cty.Value{
   307  				"a": cty.MapVal(map[string]cty.Value{
   308  					"a0": cty.StringVal("a0 value"),
   309  				}),
   310  				"b": cty.StringVal("b value"),
   311  			}),
   312  			cty.ObjectVal(map[string]cty.Value{
   313  				"a": cty.MapVal(map[string]cty.Value{
   314  					"a0": cty.StringVal("a0 value"),
   315  					"a1": cty.StringVal("new a1 value"),
   316  				}),
   317  				"b": cty.StringVal("b value"),
   318  			}),
   319  			[]string{`a["a1"]`},
   320  			cty.ObjectVal(map[string]cty.Value{
   321  				"a": cty.MapVal(map[string]cty.Value{
   322  					"a0": cty.StringVal("a0 value"),
   323  				}),
   324  				"b": cty.StringVal("b value"),
   325  			}),
   326  		},
   327  		"object attribute": {
   328  			cty.ObjectVal(map[string]cty.Value{
   329  				"a": cty.ObjectVal(map[string]cty.Value{
   330  					"foo": cty.StringVal("a.foo value"),
   331  					"bar": cty.StringVal("a.bar value"),
   332  				}),
   333  				"b": cty.StringVal("b value"),
   334  			}),
   335  			cty.ObjectVal(map[string]cty.Value{
   336  				"a": cty.ObjectVal(map[string]cty.Value{
   337  					"foo": cty.StringVal("new a.foo value"),
   338  					"bar": cty.StringVal("new a.bar value"),
   339  				}),
   340  				"b": cty.StringVal("new b value"),
   341  			}),
   342  			[]string{"a.bar"},
   343  			cty.ObjectVal(map[string]cty.Value{
   344  				"a": cty.ObjectVal(map[string]cty.Value{
   345  					"foo": cty.StringVal("new a.foo value"),
   346  					"bar": cty.StringVal("a.bar value"),
   347  				}),
   348  				"b": cty.StringVal("new b value"),
   349  			}),
   350  		},
   351  		"unknown_object_attribute": {
   352  			cty.ObjectVal(map[string]cty.Value{
   353  				"a": cty.ObjectVal(map[string]cty.Value{
   354  					"foo": cty.StringVal("a.foo value"),
   355  					"bar": cty.StringVal("a.bar value"),
   356  				}),
   357  				"b": cty.StringVal("b value"),
   358  			}),
   359  			cty.ObjectVal(map[string]cty.Value{
   360  				"a": cty.ObjectVal(map[string]cty.Value{
   361  					"foo": cty.StringVal("new a.foo value"),
   362  					"bar": cty.UnknownVal(cty.String),
   363  				}),
   364  				"b": cty.StringVal("new b value"),
   365  			}),
   366  			[]string{"a.bar"},
   367  			cty.ObjectVal(map[string]cty.Value{
   368  				"a": cty.ObjectVal(map[string]cty.Value{
   369  					"foo": cty.StringVal("new a.foo value"),
   370  					"bar": cty.StringVal("a.bar value"),
   371  				}),
   372  				"b": cty.StringVal("new b value"),
   373  			}),
   374  		},
   375  		"null_map": {
   376  			cty.ObjectVal(map[string]cty.Value{
   377  				"a": cty.StringVal("ok"),
   378  				"list": cty.ListVal([]cty.Value{
   379  					cty.ObjectVal(map[string]cty.Value{
   380  						"s":   cty.StringVal("ok"),
   381  						"map": cty.NullVal(cty.Map(cty.String)),
   382  					}),
   383  				}),
   384  			}),
   385  			cty.ObjectVal(map[string]cty.Value{
   386  				"a": cty.NullVal(cty.String),
   387  				"list": cty.ListVal([]cty.Value{
   388  					cty.ObjectVal(map[string]cty.Value{
   389  						"s":   cty.StringVal("ok"),
   390  						"map": cty.NullVal(cty.Map(cty.String)),
   391  					}),
   392  				}),
   393  			}),
   394  			[]string{"a"},
   395  			cty.ObjectVal(map[string]cty.Value{
   396  				"a": cty.StringVal("ok"),
   397  				"list": cty.ListVal([]cty.Value{
   398  					cty.ObjectVal(map[string]cty.Value{
   399  						"s":   cty.StringVal("ok"),
   400  						"map": cty.NullVal(cty.Map(cty.String)),
   401  					}),
   402  				}),
   403  			}),
   404  		},
   405  		"marked_map": {
   406  			cty.ObjectVal(map[string]cty.Value{
   407  				"map": cty.MapVal(map[string]cty.Value{
   408  					"key": cty.StringVal("val"),
   409  				}).Mark("marked"),
   410  			}),
   411  			cty.ObjectVal(map[string]cty.Value{
   412  				"map": cty.MapVal(map[string]cty.Value{
   413  					"key": cty.StringVal("new val"),
   414  				}).Mark("marked"),
   415  			}),
   416  			[]string{`map["key"]`},
   417  			cty.ObjectVal(map[string]cty.Value{
   418  				"map": cty.MapVal(map[string]cty.Value{
   419  					"key": cty.StringVal("val"),
   420  				}).Mark("marked"),
   421  			}),
   422  		},
   423  	}
   424  
   425  	for name, test := range tests {
   426  		t.Run(name, func(t *testing.T) {
   427  			ignore := make([]hcl.Traversal, len(test.Ignore))
   428  			for i, ignoreStr := range test.Ignore {
   429  				trav, diags := hclsyntax.ParseTraversalAbs([]byte(ignoreStr), "", hcl.Pos{Line: 1, Column: 1})
   430  				if diags.HasErrors() {
   431  					t.Fatalf("failed to parse %q: %s", ignoreStr, diags.Error())
   432  				}
   433  				ignore[i] = trav
   434  			}
   435  
   436  			ret, diags := processIgnoreChangesIndividual(test.Old, test.New, traversalsToPaths(ignore))
   437  			if diags.HasErrors() {
   438  				t.Fatal(diags.Err())
   439  			}
   440  
   441  			if got, want := ret, test.Want; !want.RawEquals(got) {
   442  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, want)
   443  			}
   444  		})
   445  	}
   446  }