github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plans/objchange/normalize_obj_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package objchange
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/apparentlymart/go-dump/dump"
    10  	"github.com/terramate-io/tf/configs/configschema"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestNormalizeObjectFromLegacySDK(t *testing.T) {
    15  	tests := map[string]struct {
    16  		Schema *configschema.Block
    17  		Input  cty.Value
    18  		Want   cty.Value
    19  	}{
    20  		"empty": {
    21  			&configschema.Block{},
    22  			cty.EmptyObjectVal,
    23  			cty.EmptyObjectVal,
    24  		},
    25  		"attributes only": {
    26  			&configschema.Block{
    27  				Attributes: map[string]*configschema.Attribute{
    28  					"a": {Type: cty.String, Required: true},
    29  					"b": {Type: cty.String, Optional: true},
    30  				},
    31  			},
    32  			cty.ObjectVal(map[string]cty.Value{
    33  				"a": cty.StringVal("a value"),
    34  				"b": cty.StringVal("b value"),
    35  			}),
    36  			cty.ObjectVal(map[string]cty.Value{
    37  				"a": cty.StringVal("a value"),
    38  				"b": cty.StringVal("b value"),
    39  			}),
    40  		},
    41  		"null block single": {
    42  			&configschema.Block{
    43  				BlockTypes: map[string]*configschema.NestedBlock{
    44  					"a": {
    45  						Nesting: configschema.NestingSingle,
    46  						Block: configschema.Block{
    47  							Attributes: map[string]*configschema.Attribute{
    48  								"b": {Type: cty.String, Optional: true},
    49  							},
    50  						},
    51  					},
    52  				},
    53  			},
    54  			cty.ObjectVal(map[string]cty.Value{
    55  				"a": cty.NullVal(cty.Object(map[string]cty.Type{
    56  					"b": cty.String,
    57  				})),
    58  			}),
    59  			cty.ObjectVal(map[string]cty.Value{
    60  				"a": cty.NullVal(cty.Object(map[string]cty.Type{
    61  					"b": cty.String,
    62  				})),
    63  			}),
    64  		},
    65  		"unknown block single": {
    66  			&configschema.Block{
    67  				BlockTypes: map[string]*configschema.NestedBlock{
    68  					"a": {
    69  						Nesting: configschema.NestingSingle,
    70  						Block: configschema.Block{
    71  							Attributes: map[string]*configschema.Attribute{
    72  								"b": {Type: cty.String, Optional: true},
    73  							},
    74  							BlockTypes: map[string]*configschema.NestedBlock{
    75  								"c": {Nesting: configschema.NestingSingle},
    76  							},
    77  						},
    78  					},
    79  				},
    80  			},
    81  			cty.ObjectVal(map[string]cty.Value{
    82  				"a": cty.UnknownVal(cty.Object(map[string]cty.Type{
    83  					"b": cty.String,
    84  					"c": cty.EmptyObject,
    85  				})),
    86  			}),
    87  			cty.ObjectVal(map[string]cty.Value{
    88  				"a": cty.ObjectVal(map[string]cty.Value{
    89  					"b": cty.UnknownVal(cty.String),
    90  					"c": cty.EmptyObjectVal,
    91  				}),
    92  			}),
    93  		},
    94  		"null block list": {
    95  			&configschema.Block{
    96  				BlockTypes: map[string]*configschema.NestedBlock{
    97  					"a": {
    98  						Nesting: configschema.NestingList,
    99  						Block: configschema.Block{
   100  							Attributes: map[string]*configschema.Attribute{
   101  								"b": {Type: cty.String, Optional: true},
   102  							},
   103  							BlockTypes: map[string]*configschema.NestedBlock{
   104  								"c": {Nesting: configschema.NestingSingle},
   105  							},
   106  						},
   107  					},
   108  				},
   109  			},
   110  			cty.ObjectVal(map[string]cty.Value{
   111  				"a": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{
   112  					"b": cty.String,
   113  					"c": cty.EmptyObject,
   114  				}))),
   115  			}),
   116  			cty.ObjectVal(map[string]cty.Value{
   117  				"a": cty.ListValEmpty(cty.Object(map[string]cty.Type{
   118  					"b": cty.String,
   119  					"c": cty.EmptyObject,
   120  				})),
   121  			}),
   122  		},
   123  		"unknown block list": {
   124  			&configschema.Block{
   125  				BlockTypes: map[string]*configschema.NestedBlock{
   126  					"a": {
   127  						Nesting: configschema.NestingList,
   128  						Block: configschema.Block{
   129  							Attributes: map[string]*configschema.Attribute{
   130  								"b": {Type: cty.String, Optional: true},
   131  							},
   132  						},
   133  					},
   134  				},
   135  			},
   136  			cty.ObjectVal(map[string]cty.Value{
   137  				"a": cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{
   138  					"b": cty.String,
   139  				}))),
   140  			}),
   141  			cty.ObjectVal(map[string]cty.Value{
   142  				"a": cty.ListVal([]cty.Value{
   143  					cty.ObjectVal(map[string]cty.Value{
   144  						"b": cty.UnknownVal(cty.String),
   145  					}),
   146  				}),
   147  			}),
   148  		},
   149  		"null block set": {
   150  			&configschema.Block{
   151  				BlockTypes: map[string]*configschema.NestedBlock{
   152  					"a": {
   153  						Nesting: configschema.NestingSet,
   154  						Block: configschema.Block{
   155  							Attributes: map[string]*configschema.Attribute{
   156  								"b": {Type: cty.String, Optional: true},
   157  							},
   158  						},
   159  					},
   160  				},
   161  			},
   162  			cty.ObjectVal(map[string]cty.Value{
   163  				"a": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
   164  					"b": cty.String,
   165  				}))),
   166  			}),
   167  			cty.ObjectVal(map[string]cty.Value{
   168  				"a": cty.SetValEmpty(cty.Object(map[string]cty.Type{
   169  					"b": cty.String,
   170  				})),
   171  			}),
   172  		},
   173  		"unknown block set": {
   174  			&configschema.Block{
   175  				BlockTypes: map[string]*configschema.NestedBlock{
   176  					"a": {
   177  						Nesting: configschema.NestingSet,
   178  						Block: configschema.Block{
   179  							Attributes: map[string]*configschema.Attribute{
   180  								"b": {Type: cty.String, Optional: true},
   181  							},
   182  						},
   183  					},
   184  				},
   185  			},
   186  			cty.ObjectVal(map[string]cty.Value{
   187  				"a": cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{
   188  					"b": cty.String,
   189  				}))),
   190  			}),
   191  			cty.ObjectVal(map[string]cty.Value{
   192  				"a": cty.SetVal([]cty.Value{
   193  					cty.ObjectVal(map[string]cty.Value{
   194  						"b": cty.UnknownVal(cty.String),
   195  					}),
   196  				}),
   197  			}),
   198  		},
   199  		"map block passes through": {
   200  			// Legacy SDK doesn't use NestingMap, so we don't do any transforms
   201  			// related to it but we still need to verify that map blocks pass
   202  			// through unscathed.
   203  			&configschema.Block{
   204  				BlockTypes: map[string]*configschema.NestedBlock{
   205  					"a": {
   206  						Nesting: configschema.NestingMap,
   207  						Block: configschema.Block{
   208  							Attributes: map[string]*configschema.Attribute{
   209  								"b": {Type: cty.String, Optional: true},
   210  							},
   211  						},
   212  					},
   213  				},
   214  			},
   215  			cty.ObjectVal(map[string]cty.Value{
   216  				"a": cty.MapVal(map[string]cty.Value{
   217  					"foo": cty.ObjectVal(map[string]cty.Value{
   218  						"b": cty.StringVal("b value"),
   219  					}),
   220  				}),
   221  			}),
   222  			cty.ObjectVal(map[string]cty.Value{
   223  				"a": cty.MapVal(map[string]cty.Value{
   224  					"foo": cty.ObjectVal(map[string]cty.Value{
   225  						"b": cty.StringVal("b value"),
   226  					}),
   227  				}),
   228  			}),
   229  		},
   230  		"block list with dynamic type": {
   231  			&configschema.Block{
   232  				BlockTypes: map[string]*configschema.NestedBlock{
   233  					"a": {
   234  						Nesting: configschema.NestingList,
   235  						Block: configschema.Block{
   236  							Attributes: map[string]*configschema.Attribute{
   237  								"b": {Type: cty.DynamicPseudoType, Optional: true},
   238  							},
   239  						},
   240  					},
   241  				},
   242  			},
   243  			cty.ObjectVal(map[string]cty.Value{
   244  				"a": cty.TupleVal([]cty.Value{
   245  					cty.ObjectVal(map[string]cty.Value{
   246  						"b": cty.StringVal("hello"),
   247  					}),
   248  					cty.ObjectVal(map[string]cty.Value{
   249  						"b": cty.True,
   250  					}),
   251  				}),
   252  			}),
   253  			cty.ObjectVal(map[string]cty.Value{
   254  				"a": cty.TupleVal([]cty.Value{
   255  					cty.ObjectVal(map[string]cty.Value{
   256  						"b": cty.StringVal("hello"),
   257  					}),
   258  					cty.ObjectVal(map[string]cty.Value{
   259  						"b": cty.True,
   260  					}),
   261  				}),
   262  			}),
   263  		},
   264  		"block map with dynamic type": {
   265  			&configschema.Block{
   266  				BlockTypes: map[string]*configschema.NestedBlock{
   267  					"a": {
   268  						Nesting: configschema.NestingMap,
   269  						Block: configschema.Block{
   270  							Attributes: map[string]*configschema.Attribute{
   271  								"b": {Type: cty.DynamicPseudoType, Optional: true},
   272  							},
   273  						},
   274  					},
   275  				},
   276  			},
   277  			cty.ObjectVal(map[string]cty.Value{
   278  				"a": cty.ObjectVal(map[string]cty.Value{
   279  					"one": cty.ObjectVal(map[string]cty.Value{
   280  						"b": cty.StringVal("hello"),
   281  					}),
   282  					"another": cty.ObjectVal(map[string]cty.Value{
   283  						"b": cty.True,
   284  					}),
   285  				}),
   286  			}),
   287  			cty.ObjectVal(map[string]cty.Value{
   288  				"a": cty.ObjectVal(map[string]cty.Value{
   289  					"one": cty.ObjectVal(map[string]cty.Value{
   290  						"b": cty.StringVal("hello"),
   291  					}),
   292  					"another": cty.ObjectVal(map[string]cty.Value{
   293  						"b": cty.True,
   294  					}),
   295  				}),
   296  			}),
   297  		},
   298  	}
   299  
   300  	for name, test := range tests {
   301  		t.Run(name, func(t *testing.T) {
   302  			got := NormalizeObjectFromLegacySDK(test.Input, test.Schema)
   303  			if !got.RawEquals(test.Want) {
   304  				t.Errorf(
   305  					"wrong result\ngot:  %s\nwant: %s",
   306  					dump.Value(got), dump.Value(test.Want),
   307  				)
   308  			}
   309  		})
   310  	}
   311  }