github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/plans/objchange/normalize_obj_test.go (about)

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