github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plans/objchange/objchange_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/zclconf/go-cty/cty"
    11  
    12  	"github.com/terramate-io/tf/configs/configschema"
    13  )
    14  
    15  func TestProposedNew(t *testing.T) {
    16  	tests := map[string]struct {
    17  		Schema *configschema.Block
    18  		Prior  cty.Value
    19  		Config cty.Value
    20  		Want   cty.Value
    21  	}{
    22  		"empty": {
    23  			&configschema.Block{},
    24  			cty.EmptyObjectVal,
    25  			cty.EmptyObjectVal,
    26  			cty.EmptyObjectVal,
    27  		},
    28  		"no prior": {
    29  			&configschema.Block{
    30  				Attributes: map[string]*configschema.Attribute{
    31  					"foo": {
    32  						Type:     cty.String,
    33  						Optional: true,
    34  					},
    35  					"bar": {
    36  						Type:     cty.String,
    37  						Computed: true,
    38  					},
    39  					"bloop": {
    40  						NestedType: &configschema.Object{
    41  							Nesting: configschema.NestingSingle,
    42  							Attributes: map[string]*configschema.Attribute{
    43  								"blop": {
    44  									Type:     cty.String,
    45  									Required: true,
    46  								},
    47  							},
    48  						},
    49  						Computed: true,
    50  					},
    51  				},
    52  				BlockTypes: map[string]*configschema.NestedBlock{
    53  					"baz": {
    54  						Nesting: configschema.NestingSingle,
    55  						Block: configschema.Block{
    56  							Attributes: map[string]*configschema.Attribute{
    57  								"boz": {
    58  									Type:     cty.String,
    59  									Optional: true,
    60  									Computed: true,
    61  								},
    62  								"biz": {
    63  									Type:     cty.String,
    64  									Optional: true,
    65  									Computed: true,
    66  								},
    67  							},
    68  						},
    69  					},
    70  				},
    71  			},
    72  			cty.NullVal(cty.DynamicPseudoType),
    73  			cty.ObjectVal(map[string]cty.Value{
    74  				"foo": cty.StringVal("hello"),
    75  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
    76  					"blop": cty.String,
    77  				})),
    78  				"bar": cty.NullVal(cty.String),
    79  				"baz": cty.ObjectVal(map[string]cty.Value{
    80  					"boz": cty.StringVal("world"),
    81  
    82  					// An unknown in the config represents a situation where
    83  					// an argument is explicitly set to an expression result
    84  					// that is derived from an unknown value. This is distinct
    85  					// from leaving it null, which allows the provider itself
    86  					// to decide the value during PlanResourceChange.
    87  					"biz": cty.UnknownVal(cty.String),
    88  				}),
    89  			}),
    90  			cty.ObjectVal(map[string]cty.Value{
    91  				"foo": cty.StringVal("hello"),
    92  
    93  				// unset computed attributes are null in the proposal; provider
    94  				// usually changes them to "unknown" during PlanResourceChange,
    95  				// to indicate that the value will be decided during apply.
    96  				"bar": cty.NullVal(cty.String),
    97  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
    98  					"blop": cty.String,
    99  				})),
   100  
   101  				"baz": cty.ObjectVal(map[string]cty.Value{
   102  					"boz": cty.StringVal("world"),
   103  					"biz": cty.UnknownVal(cty.String), // explicit unknown preserved from config
   104  				}),
   105  			}),
   106  		},
   107  		"null block remains null": {
   108  			&configschema.Block{
   109  				Attributes: map[string]*configschema.Attribute{
   110  					"foo": {
   111  						Type:     cty.String,
   112  						Optional: true,
   113  					},
   114  					"bloop": {
   115  						NestedType: &configschema.Object{
   116  							Nesting: configschema.NestingSingle,
   117  							Attributes: map[string]*configschema.Attribute{
   118  								"blop": {
   119  									Type:     cty.String,
   120  									Required: true,
   121  								},
   122  							},
   123  						},
   124  						Computed: true,
   125  					},
   126  				},
   127  				BlockTypes: map[string]*configschema.NestedBlock{
   128  					"baz": {
   129  						Nesting: configschema.NestingSingle,
   130  						Block: configschema.Block{
   131  							Attributes: map[string]*configschema.Attribute{
   132  								"boz": {
   133  									Type:     cty.String,
   134  									Optional: true,
   135  									Computed: true,
   136  								},
   137  							},
   138  						},
   139  					},
   140  				},
   141  			},
   142  			cty.NullVal(cty.DynamicPseudoType),
   143  			cty.ObjectVal(map[string]cty.Value{
   144  				"foo": cty.StringVal("bar"),
   145  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
   146  					"blop": cty.String,
   147  				})),
   148  				"baz": cty.NullVal(cty.Object(map[string]cty.Type{
   149  					"boz": cty.String,
   150  				})),
   151  			}),
   152  			// The bloop attribue and baz block does not exist in the config,
   153  			// and therefore shouldn't be planned.
   154  			cty.ObjectVal(map[string]cty.Value{
   155  				"foo": cty.StringVal("bar"),
   156  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
   157  					"blop": cty.String,
   158  				})),
   159  				"baz": cty.NullVal(cty.Object(map[string]cty.Type{
   160  					"boz": cty.String,
   161  				})),
   162  			}),
   163  		},
   164  		"no prior with set": {
   165  			// This one is here because our handling of sets is more complex
   166  			// than others (due to the fuzzy correlation heuristic) and
   167  			// historically that caused us some panic-related grief.
   168  			&configschema.Block{
   169  				BlockTypes: map[string]*configschema.NestedBlock{
   170  					"baz": {
   171  						Nesting: configschema.NestingSet,
   172  						Block: configschema.Block{
   173  							Attributes: map[string]*configschema.Attribute{
   174  								"boz": {
   175  									Type:     cty.String,
   176  									Optional: true,
   177  									Computed: true,
   178  								},
   179  							},
   180  						},
   181  					},
   182  				},
   183  				Attributes: map[string]*configschema.Attribute{
   184  					"bloop": {
   185  						NestedType: &configschema.Object{
   186  							Nesting: configschema.NestingSet,
   187  							Attributes: map[string]*configschema.Attribute{
   188  								"blop": {
   189  									Type:     cty.String,
   190  									Required: true,
   191  								},
   192  							},
   193  						},
   194  						Computed: true,
   195  						Optional: true,
   196  					},
   197  				},
   198  			},
   199  			cty.NullVal(cty.DynamicPseudoType),
   200  			cty.ObjectVal(map[string]cty.Value{
   201  				"baz": cty.SetVal([]cty.Value{
   202  					cty.ObjectVal(map[string]cty.Value{
   203  						"boz": cty.StringVal("world"),
   204  					}),
   205  				}),
   206  				"bloop": cty.SetVal([]cty.Value{
   207  					cty.ObjectVal(map[string]cty.Value{
   208  						"blop": cty.StringVal("blub"),
   209  					}),
   210  				}),
   211  			}),
   212  			cty.ObjectVal(map[string]cty.Value{
   213  				"baz": cty.SetVal([]cty.Value{
   214  					cty.ObjectVal(map[string]cty.Value{
   215  						"boz": cty.StringVal("world"),
   216  					}),
   217  				}),
   218  				"bloop": cty.SetVal([]cty.Value{
   219  					cty.ObjectVal(map[string]cty.Value{
   220  						"blop": cty.StringVal("blub"),
   221  					}),
   222  				}),
   223  			}),
   224  		},
   225  		"prior attributes": {
   226  			&configschema.Block{
   227  				Attributes: map[string]*configschema.Attribute{
   228  					"foo": {
   229  						Type:     cty.String,
   230  						Optional: true,
   231  					},
   232  					"bar": {
   233  						Type:     cty.String,
   234  						Computed: true,
   235  					},
   236  					"baz": {
   237  						Type:     cty.String,
   238  						Optional: true,
   239  						Computed: true,
   240  					},
   241  					"boz": {
   242  						Type:     cty.String,
   243  						Optional: true,
   244  						Computed: true,
   245  					},
   246  					"bloop": {
   247  						NestedType: &configschema.Object{
   248  							Nesting: configschema.NestingSingle,
   249  							Attributes: map[string]*configschema.Attribute{
   250  								"blop": {
   251  									Type:     cty.String,
   252  									Required: true,
   253  								},
   254  							},
   255  						},
   256  						Optional: true,
   257  					},
   258  				},
   259  			},
   260  			cty.ObjectVal(map[string]cty.Value{
   261  				"foo": cty.StringVal("bonjour"),
   262  				"bar": cty.StringVal("petit dejeuner"),
   263  				"baz": cty.StringVal("grande dejeuner"),
   264  				"boz": cty.StringVal("a la monde"),
   265  				"bloop": cty.ObjectVal(map[string]cty.Value{
   266  					"blop": cty.StringVal("glub"),
   267  				}),
   268  			}),
   269  			cty.ObjectVal(map[string]cty.Value{
   270  				"foo": cty.StringVal("hello"),
   271  				"bar": cty.NullVal(cty.String),
   272  				"baz": cty.NullVal(cty.String),
   273  				"boz": cty.StringVal("world"),
   274  				"bloop": cty.ObjectVal(map[string]cty.Value{
   275  					"blop": cty.StringVal("bleep"),
   276  				}),
   277  			}),
   278  			cty.ObjectVal(map[string]cty.Value{
   279  				"foo": cty.StringVal("hello"),
   280  				"bar": cty.StringVal("petit dejeuner"),
   281  				"baz": cty.StringVal("grande dejeuner"),
   282  				"boz": cty.StringVal("world"),
   283  				"bloop": cty.ObjectVal(map[string]cty.Value{
   284  					"blop": cty.StringVal("bleep"),
   285  				}),
   286  			}),
   287  		},
   288  		"prior nested single": {
   289  			&configschema.Block{
   290  				BlockTypes: map[string]*configschema.NestedBlock{
   291  					"foo": {
   292  						Nesting: configschema.NestingSingle,
   293  						Block: configschema.Block{
   294  							Attributes: map[string]*configschema.Attribute{
   295  								"bar": {
   296  									Type:     cty.String,
   297  									Optional: true,
   298  									Computed: true,
   299  								},
   300  								"baz": {
   301  									Type:     cty.String,
   302  									Optional: true,
   303  									Computed: true,
   304  								},
   305  							},
   306  						},
   307  					},
   308  				},
   309  				Attributes: map[string]*configschema.Attribute{
   310  					"bloop": {
   311  						NestedType: &configschema.Object{
   312  							Nesting: configschema.NestingSingle,
   313  							Attributes: map[string]*configschema.Attribute{
   314  								"blop": {
   315  									Type:     cty.String,
   316  									Required: true,
   317  								},
   318  								"bleep": {
   319  									Type:     cty.String,
   320  									Optional: true,
   321  								},
   322  							},
   323  						},
   324  						Optional: true,
   325  					},
   326  				},
   327  			},
   328  			cty.ObjectVal(map[string]cty.Value{
   329  				"foo": cty.ObjectVal(map[string]cty.Value{
   330  					"bar": cty.StringVal("beep"),
   331  					"baz": cty.StringVal("boop"),
   332  				}),
   333  				"bloop": cty.ObjectVal(map[string]cty.Value{
   334  					"blop":  cty.StringVal("glub"),
   335  					"bleep": cty.NullVal(cty.String),
   336  				}),
   337  			}),
   338  			cty.ObjectVal(map[string]cty.Value{
   339  				"foo": cty.ObjectVal(map[string]cty.Value{
   340  					"bar": cty.StringVal("bap"),
   341  					"baz": cty.NullVal(cty.String),
   342  				}),
   343  				"bloop": cty.ObjectVal(map[string]cty.Value{
   344  					"blop":  cty.StringVal("glub"),
   345  					"bleep": cty.StringVal("beep"),
   346  				}),
   347  			}),
   348  			cty.ObjectVal(map[string]cty.Value{
   349  				"foo": cty.ObjectVal(map[string]cty.Value{
   350  					"bar": cty.StringVal("bap"),
   351  					"baz": cty.StringVal("boop"),
   352  				}),
   353  				"bloop": cty.ObjectVal(map[string]cty.Value{
   354  					"blop":  cty.StringVal("glub"),
   355  					"bleep": cty.StringVal("beep"),
   356  				}),
   357  			}),
   358  		},
   359  		"prior nested single to null": {
   360  			&configschema.Block{
   361  				BlockTypes: map[string]*configschema.NestedBlock{
   362  					"foo": {
   363  						Nesting: configschema.NestingSingle,
   364  						Block: configschema.Block{
   365  							Attributes: map[string]*configschema.Attribute{
   366  								"bar": {
   367  									Type:     cty.String,
   368  									Optional: true,
   369  									Computed: true,
   370  								},
   371  								"baz": {
   372  									Type:     cty.String,
   373  									Optional: true,
   374  									Computed: true,
   375  								},
   376  							},
   377  						},
   378  					},
   379  				},
   380  				Attributes: map[string]*configschema.Attribute{
   381  					"bloop": {
   382  						NestedType: &configschema.Object{
   383  							Nesting: configschema.NestingSingle,
   384  							Attributes: map[string]*configschema.Attribute{
   385  								"blop": {
   386  									Type:     cty.String,
   387  									Required: true,
   388  								},
   389  								"bleep": {
   390  									Type:     cty.String,
   391  									Optional: true,
   392  								},
   393  							},
   394  						},
   395  						Optional: true,
   396  					},
   397  				},
   398  			},
   399  			cty.ObjectVal(map[string]cty.Value{
   400  				"foo": cty.ObjectVal(map[string]cty.Value{
   401  					"bar": cty.StringVal("beep"),
   402  					"baz": cty.StringVal("boop"),
   403  				}),
   404  				"bloop": cty.ObjectVal(map[string]cty.Value{
   405  					"blop":  cty.StringVal("glub"),
   406  					"bleep": cty.NullVal(cty.String),
   407  				}),
   408  			}),
   409  			cty.ObjectVal(map[string]cty.Value{
   410  				"foo": cty.NullVal(cty.Object(map[string]cty.Type{
   411  					"bar": cty.String,
   412  					"baz": cty.String,
   413  				})),
   414  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
   415  					"blop":  cty.String,
   416  					"bleep": cty.String,
   417  				})),
   418  			}),
   419  			cty.ObjectVal(map[string]cty.Value{
   420  				"foo": cty.NullVal(cty.Object(map[string]cty.Type{
   421  					"bar": cty.String,
   422  					"baz": cty.String,
   423  				})),
   424  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
   425  					"blop":  cty.String,
   426  					"bleep": cty.String,
   427  				})),
   428  			}),
   429  		},
   430  
   431  		"prior optional computed nested single to null": {
   432  			&configschema.Block{
   433  				Attributes: map[string]*configschema.Attribute{
   434  					"bloop": {
   435  						NestedType: &configschema.Object{
   436  							Nesting: configschema.NestingSingle,
   437  							Attributes: map[string]*configschema.Attribute{
   438  								"blop": {
   439  									Type:     cty.String,
   440  									Required: true,
   441  								},
   442  								"bleep": {
   443  									Type:     cty.String,
   444  									Optional: true,
   445  								},
   446  							},
   447  						},
   448  						Optional: true,
   449  						Computed: true,
   450  					},
   451  				},
   452  			},
   453  			cty.ObjectVal(map[string]cty.Value{
   454  				"bloop": cty.ObjectVal(map[string]cty.Value{
   455  					"blop":  cty.StringVal("glub"),
   456  					"bleep": cty.NullVal(cty.String),
   457  				}),
   458  			}),
   459  			cty.ObjectVal(map[string]cty.Value{
   460  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
   461  					"blop":  cty.String,
   462  					"bleep": cty.String,
   463  				})),
   464  			}),
   465  			cty.ObjectVal(map[string]cty.Value{
   466  				"bloop": cty.NullVal(cty.Object(map[string]cty.Type{
   467  					"blop":  cty.String,
   468  					"bleep": cty.String,
   469  				})),
   470  			}),
   471  		},
   472  
   473  		"prior nested list": {
   474  			&configschema.Block{
   475  				BlockTypes: map[string]*configschema.NestedBlock{
   476  					"foo": {
   477  						Nesting: configschema.NestingList,
   478  						Block: configschema.Block{
   479  							Attributes: map[string]*configschema.Attribute{
   480  								"bar": {
   481  									Type:     cty.String,
   482  									Optional: true,
   483  									Computed: true,
   484  								},
   485  								"baz": {
   486  									Type:     cty.String,
   487  									Optional: true,
   488  									Computed: true,
   489  								},
   490  							},
   491  						},
   492  					},
   493  				},
   494  				Attributes: map[string]*configschema.Attribute{
   495  					"bloop": {
   496  						NestedType: &configschema.Object{
   497  							Nesting: configschema.NestingList,
   498  							Attributes: map[string]*configschema.Attribute{
   499  								"blop": {
   500  									Type:     cty.String,
   501  									Required: true,
   502  								},
   503  							},
   504  						},
   505  						Optional: true,
   506  					},
   507  				},
   508  			},
   509  			cty.ObjectVal(map[string]cty.Value{
   510  				"foo": cty.ListVal([]cty.Value{
   511  					cty.ObjectVal(map[string]cty.Value{
   512  						"bar": cty.StringVal("beep"),
   513  						"baz": cty.StringVal("boop"),
   514  					}),
   515  				}),
   516  				"bloop": cty.ListVal([]cty.Value{
   517  					cty.ObjectVal(map[string]cty.Value{
   518  						"blop": cty.StringVal("bar"),
   519  					}),
   520  					cty.ObjectVal(map[string]cty.Value{
   521  						"blop": cty.StringVal("baz"),
   522  					}),
   523  				}),
   524  			}),
   525  			cty.ObjectVal(map[string]cty.Value{
   526  				"foo": cty.ListVal([]cty.Value{
   527  					cty.ObjectVal(map[string]cty.Value{
   528  						"bar": cty.StringVal("bap"),
   529  						"baz": cty.NullVal(cty.String),
   530  					}),
   531  					cty.ObjectVal(map[string]cty.Value{
   532  						"bar": cty.StringVal("blep"),
   533  						"baz": cty.NullVal(cty.String),
   534  					}),
   535  				}),
   536  				"bloop": cty.ListVal([]cty.Value{
   537  					cty.ObjectVal(map[string]cty.Value{
   538  						"blop": cty.StringVal("bar"),
   539  					}),
   540  					cty.ObjectVal(map[string]cty.Value{
   541  						"blop": cty.StringVal("baz"),
   542  					}),
   543  				}),
   544  			}),
   545  			cty.ObjectVal(map[string]cty.Value{
   546  				"foo": cty.ListVal([]cty.Value{
   547  					cty.ObjectVal(map[string]cty.Value{
   548  						"bar": cty.StringVal("bap"),
   549  						"baz": cty.StringVal("boop"),
   550  					}),
   551  					cty.ObjectVal(map[string]cty.Value{
   552  						"bar": cty.StringVal("blep"),
   553  						"baz": cty.NullVal(cty.String),
   554  					}),
   555  				}),
   556  				"bloop": cty.ListVal([]cty.Value{
   557  					cty.ObjectVal(map[string]cty.Value{
   558  						"blop": cty.StringVal("bar"),
   559  					}),
   560  					cty.ObjectVal(map[string]cty.Value{
   561  						"blop": cty.StringVal("baz"),
   562  					}),
   563  				}),
   564  			}),
   565  		},
   566  		"prior nested list with dynamic": {
   567  			&configschema.Block{
   568  				BlockTypes: map[string]*configschema.NestedBlock{
   569  					"foo": {
   570  						Nesting: configschema.NestingList,
   571  						Block: configschema.Block{
   572  							Attributes: map[string]*configschema.Attribute{
   573  								"bar": {
   574  									Type:     cty.String,
   575  									Optional: true,
   576  									Computed: true,
   577  								},
   578  								"baz": {
   579  									Type:     cty.DynamicPseudoType,
   580  									Optional: true,
   581  									Computed: true,
   582  								},
   583  							},
   584  						},
   585  					},
   586  				},
   587  				Attributes: map[string]*configschema.Attribute{
   588  					"bloop": {
   589  						NestedType: &configschema.Object{
   590  							Nesting: configschema.NestingList,
   591  							Attributes: map[string]*configschema.Attribute{
   592  								"blop": {
   593  									Type:     cty.DynamicPseudoType,
   594  									Required: true,
   595  								},
   596  								"blub": {
   597  									Type:     cty.DynamicPseudoType,
   598  									Optional: true,
   599  								},
   600  							},
   601  						},
   602  						Optional: true,
   603  					},
   604  				},
   605  			},
   606  			cty.ObjectVal(map[string]cty.Value{
   607  				"foo": cty.TupleVal([]cty.Value{
   608  					cty.ObjectVal(map[string]cty.Value{
   609  						"bar": cty.StringVal("beep"),
   610  						"baz": cty.StringVal("boop"),
   611  					}),
   612  				}),
   613  				"bloop": cty.ListVal([]cty.Value{
   614  					cty.ObjectVal(map[string]cty.Value{
   615  						"blop": cty.StringVal("bar"),
   616  						"blub": cty.StringVal("glub"),
   617  					}),
   618  					cty.ObjectVal(map[string]cty.Value{
   619  						"blop": cty.StringVal("baz"),
   620  						"blub": cty.NullVal(cty.String),
   621  					}),
   622  				}),
   623  			}),
   624  			cty.ObjectVal(map[string]cty.Value{
   625  				"foo": cty.TupleVal([]cty.Value{
   626  					cty.ObjectVal(map[string]cty.Value{
   627  						"bar": cty.StringVal("bap"),
   628  						"baz": cty.NullVal(cty.String),
   629  					}),
   630  					cty.ObjectVal(map[string]cty.Value{
   631  						"bar": cty.StringVal("blep"),
   632  						"baz": cty.NullVal(cty.String),
   633  					}),
   634  				}),
   635  				"bloop": cty.ListVal([]cty.Value{
   636  					cty.ObjectVal(map[string]cty.Value{
   637  						"blop": cty.StringVal("bar"),
   638  						"blub": cty.NullVal(cty.String),
   639  					}),
   640  				}),
   641  			}),
   642  			cty.ObjectVal(map[string]cty.Value{
   643  				"foo": cty.TupleVal([]cty.Value{
   644  					cty.ObjectVal(map[string]cty.Value{
   645  						"bar": cty.StringVal("bap"),
   646  						"baz": cty.StringVal("boop"),
   647  					}),
   648  					cty.ObjectVal(map[string]cty.Value{
   649  						"bar": cty.StringVal("blep"),
   650  						"baz": cty.NullVal(cty.String),
   651  					}),
   652  				}),
   653  				"bloop": cty.ListVal([]cty.Value{
   654  					cty.ObjectVal(map[string]cty.Value{
   655  						"blop": cty.StringVal("bar"),
   656  						"blub": cty.NullVal(cty.String),
   657  					}),
   658  				}),
   659  			}),
   660  		},
   661  		"prior nested map": {
   662  			&configschema.Block{
   663  				BlockTypes: map[string]*configschema.NestedBlock{
   664  					"foo": {
   665  						Nesting: configschema.NestingMap,
   666  						Block: configschema.Block{
   667  							Attributes: map[string]*configschema.Attribute{
   668  								"bar": {
   669  									Type:     cty.String,
   670  									Optional: true,
   671  									Computed: true,
   672  								},
   673  								"baz": {
   674  									Type:     cty.String,
   675  									Optional: true,
   676  									Computed: true,
   677  								},
   678  							},
   679  						},
   680  					},
   681  				},
   682  				Attributes: map[string]*configschema.Attribute{
   683  					"bloop": {
   684  						NestedType: &configschema.Object{
   685  							Nesting: configschema.NestingMap,
   686  							Attributes: map[string]*configschema.Attribute{
   687  								"blop": {
   688  									Type:     cty.String,
   689  									Required: true,
   690  								},
   691  							},
   692  						},
   693  						Optional: true,
   694  					},
   695  				},
   696  			},
   697  			cty.ObjectVal(map[string]cty.Value{
   698  				"foo": cty.MapVal(map[string]cty.Value{
   699  					"a": cty.ObjectVal(map[string]cty.Value{
   700  						"bar": cty.StringVal("beep"),
   701  						"baz": cty.StringVal("boop"),
   702  					}),
   703  					"b": cty.ObjectVal(map[string]cty.Value{
   704  						"bar": cty.StringVal("blep"),
   705  						"baz": cty.StringVal("boot"),
   706  					}),
   707  				}),
   708  				"bloop": cty.MapVal(map[string]cty.Value{
   709  					"a": cty.ObjectVal(map[string]cty.Value{
   710  						"blop": cty.StringVal("glub"),
   711  					}),
   712  					"b": cty.ObjectVal(map[string]cty.Value{
   713  						"blop": cty.StringVal("blub"),
   714  					}),
   715  				}),
   716  			}),
   717  			cty.ObjectVal(map[string]cty.Value{
   718  				"foo": cty.MapVal(map[string]cty.Value{
   719  					"a": cty.ObjectVal(map[string]cty.Value{
   720  						"bar": cty.StringVal("bap"),
   721  						"baz": cty.NullVal(cty.String),
   722  					}),
   723  					"c": cty.ObjectVal(map[string]cty.Value{
   724  						"bar": cty.StringVal("bosh"),
   725  						"baz": cty.NullVal(cty.String),
   726  					}),
   727  				}),
   728  				"bloop": cty.MapVal(map[string]cty.Value{
   729  					"a": cty.ObjectVal(map[string]cty.Value{
   730  						"blop": cty.StringVal("glub"),
   731  					}),
   732  					"c": cty.ObjectVal(map[string]cty.Value{
   733  						"blop": cty.StringVal("blub"),
   734  					}),
   735  				}),
   736  			}),
   737  			cty.ObjectVal(map[string]cty.Value{
   738  				"foo": cty.MapVal(map[string]cty.Value{
   739  					"a": cty.ObjectVal(map[string]cty.Value{
   740  						"bar": cty.StringVal("bap"),
   741  						"baz": cty.StringVal("boop"),
   742  					}),
   743  					"c": cty.ObjectVal(map[string]cty.Value{
   744  						"bar": cty.StringVal("bosh"),
   745  						"baz": cty.NullVal(cty.String),
   746  					}),
   747  				}),
   748  				"bloop": cty.MapVal(map[string]cty.Value{
   749  					"a": cty.ObjectVal(map[string]cty.Value{
   750  						"blop": cty.StringVal("glub"),
   751  					}),
   752  					"c": cty.ObjectVal(map[string]cty.Value{
   753  						"blop": cty.StringVal("blub"),
   754  					}),
   755  				}),
   756  			}),
   757  		},
   758  
   759  		"prior optional computed nested map elem to null": {
   760  			&configschema.Block{
   761  				Attributes: map[string]*configschema.Attribute{
   762  					"bloop": {
   763  						NestedType: &configschema.Object{
   764  							Nesting: configschema.NestingMap,
   765  							Attributes: map[string]*configschema.Attribute{
   766  								"blop": {
   767  									Type:     cty.String,
   768  									Optional: true,
   769  								},
   770  								"bleep": {
   771  									Type:     cty.String,
   772  									Optional: true,
   773  									Computed: true,
   774  								},
   775  							},
   776  						},
   777  						Optional: true,
   778  					},
   779  				},
   780  			},
   781  			cty.ObjectVal(map[string]cty.Value{
   782  				"bloop": cty.MapVal(map[string]cty.Value{
   783  					"a": cty.ObjectVal(map[string]cty.Value{
   784  						"blop":  cty.StringVal("glub"),
   785  						"bleep": cty.StringVal("computed"),
   786  					}),
   787  					"b": cty.ObjectVal(map[string]cty.Value{
   788  						"blop":  cty.StringVal("blub"),
   789  						"bleep": cty.StringVal("computed"),
   790  					}),
   791  				}),
   792  			}),
   793  			cty.ObjectVal(map[string]cty.Value{
   794  				"bloop": cty.MapVal(map[string]cty.Value{
   795  					"a": cty.NullVal(cty.Object(map[string]cty.Type{
   796  						"blop":  cty.String,
   797  						"bleep": cty.String,
   798  					})),
   799  					"c": cty.ObjectVal(map[string]cty.Value{
   800  						"blop":  cty.StringVal("blub"),
   801  						"bleep": cty.NullVal(cty.String),
   802  					}),
   803  				}),
   804  			}),
   805  			cty.ObjectVal(map[string]cty.Value{
   806  				"bloop": cty.MapVal(map[string]cty.Value{
   807  					"a": cty.NullVal(cty.Object(map[string]cty.Type{
   808  						"blop":  cty.String,
   809  						"bleep": cty.String,
   810  					})),
   811  					"c": cty.ObjectVal(map[string]cty.Value{
   812  						"blop":  cty.StringVal("blub"),
   813  						"bleep": cty.NullVal(cty.String),
   814  					}),
   815  				}),
   816  			}),
   817  		},
   818  
   819  		"prior optional computed nested map to null": {
   820  			&configschema.Block{
   821  				Attributes: map[string]*configschema.Attribute{
   822  					"bloop": {
   823  						NestedType: &configschema.Object{
   824  							Nesting: configschema.NestingMap,
   825  							Attributes: map[string]*configschema.Attribute{
   826  								"blop": {
   827  									Type:     cty.String,
   828  									Optional: true,
   829  								},
   830  								"bleep": {
   831  									Type:     cty.String,
   832  									Optional: true,
   833  									Computed: true,
   834  								},
   835  							},
   836  						},
   837  						Optional: true,
   838  						Computed: true,
   839  					},
   840  				},
   841  			},
   842  			cty.ObjectVal(map[string]cty.Value{
   843  				"bloop": cty.MapVal(map[string]cty.Value{
   844  					"a": cty.ObjectVal(map[string]cty.Value{
   845  						"blop":  cty.StringVal("glub"),
   846  						"bleep": cty.StringVal("computed"),
   847  					}),
   848  					"b": cty.ObjectVal(map[string]cty.Value{
   849  						"blop":  cty.StringVal("blub"),
   850  						"bleep": cty.StringVal("computed"),
   851  					}),
   852  				}),
   853  			}),
   854  			cty.ObjectVal(map[string]cty.Value{
   855  				"bloop": cty.NullVal(cty.Map(
   856  					cty.Object(map[string]cty.Type{
   857  						"blop":  cty.String,
   858  						"bleep": cty.String,
   859  					}),
   860  				)),
   861  			}),
   862  			cty.ObjectVal(map[string]cty.Value{
   863  				"bloop": cty.NullVal(cty.Map(
   864  					cty.Object(map[string]cty.Type{
   865  						"blop":  cty.String,
   866  						"bleep": cty.String,
   867  					}),
   868  				)),
   869  			}),
   870  		},
   871  
   872  		"prior nested map with dynamic": {
   873  			&configschema.Block{
   874  				BlockTypes: map[string]*configschema.NestedBlock{
   875  					"foo": {
   876  						Nesting: configschema.NestingMap,
   877  						Block: configschema.Block{
   878  							Attributes: map[string]*configschema.Attribute{
   879  								"bar": {
   880  									Type:     cty.String,
   881  									Optional: true,
   882  									Computed: true,
   883  								},
   884  								"baz": {
   885  									Type:     cty.DynamicPseudoType,
   886  									Optional: true,
   887  									Computed: true,
   888  								},
   889  							},
   890  						},
   891  					},
   892  				},
   893  				Attributes: map[string]*configschema.Attribute{
   894  					"bloop": {
   895  						NestedType: &configschema.Object{
   896  							Nesting: configschema.NestingMap,
   897  							Attributes: map[string]*configschema.Attribute{
   898  								"blop": {
   899  									Type:     cty.DynamicPseudoType,
   900  									Required: true,
   901  								},
   902  							},
   903  						},
   904  						Optional: true,
   905  					},
   906  				},
   907  			},
   908  			cty.ObjectVal(map[string]cty.Value{
   909  				"foo": cty.ObjectVal(map[string]cty.Value{
   910  					"a": cty.ObjectVal(map[string]cty.Value{
   911  						"bar": cty.StringVal("beep"),
   912  						"baz": cty.StringVal("boop"),
   913  					}),
   914  					"b": cty.ObjectVal(map[string]cty.Value{
   915  						"bar": cty.StringVal("blep"),
   916  						"baz": cty.ListVal([]cty.Value{cty.StringVal("boot")}),
   917  					}),
   918  				}),
   919  				"bloop": cty.ObjectVal(map[string]cty.Value{
   920  					"a": cty.ObjectVal(map[string]cty.Value{
   921  						"blop": cty.StringVal("glub"),
   922  					}),
   923  					"b": cty.ObjectVal(map[string]cty.Value{
   924  						"blop": cty.NumberIntVal(13),
   925  					}),
   926  				}),
   927  			}),
   928  			cty.ObjectVal(map[string]cty.Value{
   929  				"foo": cty.ObjectVal(map[string]cty.Value{
   930  					"a": cty.ObjectVal(map[string]cty.Value{
   931  						"bar": cty.StringVal("bap"),
   932  						"baz": cty.NullVal(cty.String),
   933  					}),
   934  					"c": cty.ObjectVal(map[string]cty.Value{
   935  						"bar": cty.StringVal("bosh"),
   936  						"baz": cty.NullVal(cty.List(cty.String)),
   937  					}),
   938  				}),
   939  				"bloop": cty.ObjectVal(map[string]cty.Value{
   940  					"a": cty.ObjectVal(map[string]cty.Value{
   941  						"blop": cty.StringVal("blep"),
   942  					}),
   943  					"c": cty.ObjectVal(map[string]cty.Value{
   944  						"blop": cty.NumberIntVal(13),
   945  					}),
   946  				}),
   947  			}),
   948  			cty.ObjectVal(map[string]cty.Value{
   949  				"foo": cty.ObjectVal(map[string]cty.Value{
   950  					"a": cty.ObjectVal(map[string]cty.Value{
   951  						"bar": cty.StringVal("bap"),
   952  						"baz": cty.StringVal("boop"),
   953  					}),
   954  					"c": cty.ObjectVal(map[string]cty.Value{
   955  						"bar": cty.StringVal("bosh"),
   956  						"baz": cty.NullVal(cty.List(cty.String)),
   957  					}),
   958  				}),
   959  				"bloop": cty.ObjectVal(map[string]cty.Value{
   960  					"a": cty.ObjectVal(map[string]cty.Value{
   961  						"blop": cty.StringVal("blep"),
   962  					}),
   963  					"c": cty.ObjectVal(map[string]cty.Value{
   964  						"blop": cty.NumberIntVal(13),
   965  					}),
   966  				}),
   967  			}),
   968  		},
   969  		"prior nested set": {
   970  			&configschema.Block{
   971  				BlockTypes: map[string]*configschema.NestedBlock{
   972  					"foo": {
   973  						Nesting: configschema.NestingSet,
   974  						Block: configschema.Block{
   975  							Attributes: map[string]*configschema.Attribute{
   976  								"bar": {
   977  									// This non-computed attribute will serve
   978  									// as our matching key for propagating
   979  									// "baz" from elements in the prior value.
   980  									Type:     cty.String,
   981  									Optional: true,
   982  								},
   983  								"baz": {
   984  									Type:     cty.String,
   985  									Optional: true,
   986  									Computed: true,
   987  								},
   988  							},
   989  						},
   990  					},
   991  				},
   992  				Attributes: map[string]*configschema.Attribute{
   993  					"bloop": {
   994  						NestedType: &configschema.Object{
   995  							Nesting: configschema.NestingSet,
   996  							Attributes: map[string]*configschema.Attribute{
   997  								"blop": {
   998  									Type:     cty.String,
   999  									Required: true,
  1000  								},
  1001  								"bleep": {
  1002  									Type:     cty.String,
  1003  									Optional: true,
  1004  								},
  1005  							},
  1006  						},
  1007  						Optional: true,
  1008  					},
  1009  				},
  1010  			},
  1011  			cty.ObjectVal(map[string]cty.Value{
  1012  				"foo": cty.SetVal([]cty.Value{
  1013  					cty.ObjectVal(map[string]cty.Value{
  1014  						"bar": cty.StringVal("beep"),
  1015  						"baz": cty.StringVal("boop"),
  1016  					}),
  1017  					cty.ObjectVal(map[string]cty.Value{
  1018  						"bar": cty.StringVal("blep"),
  1019  						"baz": cty.StringVal("boot"),
  1020  					}),
  1021  				}),
  1022  				"bloop": cty.SetVal([]cty.Value{
  1023  					cty.ObjectVal(map[string]cty.Value{
  1024  						"blop":  cty.StringVal("glubglub"),
  1025  						"bleep": cty.NullVal(cty.String),
  1026  					}),
  1027  					cty.ObjectVal(map[string]cty.Value{
  1028  						"blop":  cty.StringVal("glubglub"),
  1029  						"bleep": cty.StringVal("beep"),
  1030  					}),
  1031  				}),
  1032  			}),
  1033  			cty.ObjectVal(map[string]cty.Value{
  1034  				"foo": cty.SetVal([]cty.Value{
  1035  					cty.ObjectVal(map[string]cty.Value{
  1036  						"bar": cty.StringVal("beep"),
  1037  						"baz": cty.NullVal(cty.String),
  1038  					}),
  1039  					cty.ObjectVal(map[string]cty.Value{
  1040  						"bar": cty.StringVal("bosh"),
  1041  						"baz": cty.NullVal(cty.String),
  1042  					}),
  1043  				}),
  1044  				"bloop": cty.SetVal([]cty.Value{
  1045  					cty.ObjectVal(map[string]cty.Value{
  1046  						"blop":  cty.StringVal("glubglub"),
  1047  						"bleep": cty.NullVal(cty.String),
  1048  					}),
  1049  					cty.ObjectVal(map[string]cty.Value{
  1050  						"blop":  cty.StringVal("glub"),
  1051  						"bleep": cty.NullVal(cty.String),
  1052  					}),
  1053  				}),
  1054  			}),
  1055  			cty.ObjectVal(map[string]cty.Value{
  1056  				"foo": cty.SetVal([]cty.Value{
  1057  					cty.ObjectVal(map[string]cty.Value{
  1058  						"bar": cty.StringVal("beep"),
  1059  						"baz": cty.StringVal("boop"),
  1060  					}),
  1061  					cty.ObjectVal(map[string]cty.Value{
  1062  						"bar": cty.StringVal("bosh"),
  1063  						"baz": cty.NullVal(cty.String),
  1064  					}),
  1065  				}),
  1066  				"bloop": cty.SetVal([]cty.Value{
  1067  					cty.ObjectVal(map[string]cty.Value{
  1068  						"blop":  cty.StringVal("glubglub"),
  1069  						"bleep": cty.NullVal(cty.String),
  1070  					}),
  1071  					cty.ObjectVal(map[string]cty.Value{
  1072  						"blop":  cty.StringVal("glub"),
  1073  						"bleep": cty.NullVal(cty.String),
  1074  					}),
  1075  				}),
  1076  			}),
  1077  		},
  1078  
  1079  		"set with partial optional computed change": {
  1080  			&configschema.Block{
  1081  				BlockTypes: map[string]*configschema.NestedBlock{
  1082  					"multi": {
  1083  						Nesting: configschema.NestingSet,
  1084  						Block: configschema.Block{
  1085  							Attributes: map[string]*configschema.Attribute{
  1086  								"opt": {
  1087  									Type:     cty.String,
  1088  									Optional: true,
  1089  								},
  1090  								"cmp": {
  1091  									Type:     cty.String,
  1092  									Optional: true,
  1093  									Computed: true,
  1094  								},
  1095  							},
  1096  						},
  1097  					},
  1098  				},
  1099  			},
  1100  			cty.ObjectVal(map[string]cty.Value{
  1101  				"multi": cty.SetVal([]cty.Value{
  1102  					cty.ObjectVal(map[string]cty.Value{
  1103  						"opt": cty.StringVal("one"),
  1104  						"cmp": cty.StringVal("OK"),
  1105  					}),
  1106  					cty.ObjectVal(map[string]cty.Value{
  1107  						"opt": cty.StringVal("two"),
  1108  						"cmp": cty.StringVal("OK"),
  1109  					}),
  1110  				}),
  1111  			}),
  1112  
  1113  			cty.ObjectVal(map[string]cty.Value{
  1114  				"multi": cty.SetVal([]cty.Value{
  1115  					cty.ObjectVal(map[string]cty.Value{
  1116  						"opt": cty.StringVal("one"),
  1117  						"cmp": cty.NullVal(cty.String),
  1118  					}),
  1119  					cty.ObjectVal(map[string]cty.Value{
  1120  						"opt": cty.StringVal("replaced"),
  1121  						"cmp": cty.NullVal(cty.String),
  1122  					}),
  1123  				}),
  1124  			}),
  1125  			// "one" can be correlated because it is a non-computed value in
  1126  			// the configuration.
  1127  			cty.ObjectVal(map[string]cty.Value{
  1128  				"multi": cty.SetVal([]cty.Value{
  1129  					cty.ObjectVal(map[string]cty.Value{
  1130  						"opt": cty.StringVal("one"),
  1131  						"cmp": cty.StringVal("OK"),
  1132  					}),
  1133  					cty.ObjectVal(map[string]cty.Value{
  1134  						"opt": cty.StringVal("replaced"),
  1135  						"cmp": cty.NullVal(cty.String),
  1136  					}),
  1137  				}),
  1138  			}),
  1139  		},
  1140  
  1141  		"set without partial optional computed change": {
  1142  			&configschema.Block{
  1143  				BlockTypes: map[string]*configschema.NestedBlock{
  1144  					"multi": {
  1145  						Nesting: configschema.NestingSet,
  1146  						Block: configschema.Block{
  1147  							Attributes: map[string]*configschema.Attribute{
  1148  								"opt": {
  1149  									Type:     cty.String,
  1150  									Optional: true,
  1151  									Computed: true,
  1152  								},
  1153  								"req": {
  1154  									Type:     cty.String,
  1155  									Required: true,
  1156  								},
  1157  							},
  1158  						},
  1159  					},
  1160  				},
  1161  			},
  1162  			cty.ObjectVal(map[string]cty.Value{
  1163  				"multi": cty.SetVal([]cty.Value{
  1164  					cty.ObjectVal(map[string]cty.Value{
  1165  						"opt": cty.StringVal("one"),
  1166  						"req": cty.StringVal("one"),
  1167  					}),
  1168  					cty.ObjectVal(map[string]cty.Value{
  1169  						"opt": cty.StringVal("two"),
  1170  						"req": cty.StringVal("two"),
  1171  					}),
  1172  				}),
  1173  			}),
  1174  			cty.ObjectVal(map[string]cty.Value{
  1175  				"multi": cty.SetVal([]cty.Value{
  1176  					cty.ObjectVal(map[string]cty.Value{
  1177  						"opt": cty.NullVal(cty.String),
  1178  						"req": cty.StringVal("one"),
  1179  					}),
  1180  					cty.ObjectVal(map[string]cty.Value{
  1181  						"opt": cty.NullVal(cty.String),
  1182  						"req": cty.StringVal("two"),
  1183  					}),
  1184  				}),
  1185  			}),
  1186  			cty.ObjectVal(map[string]cty.Value{
  1187  				"multi": cty.SetVal([]cty.Value{
  1188  					cty.ObjectVal(map[string]cty.Value{
  1189  						"opt": cty.StringVal("one"),
  1190  						"req": cty.StringVal("one"),
  1191  					}),
  1192  					cty.ObjectVal(map[string]cty.Value{
  1193  						"opt": cty.StringVal("two"),
  1194  						"req": cty.StringVal("two"),
  1195  					}),
  1196  				}),
  1197  			}),
  1198  		},
  1199  
  1200  		"sets differing only by unknown": {
  1201  			&configschema.Block{
  1202  				BlockTypes: map[string]*configschema.NestedBlock{
  1203  					"multi": {
  1204  						Nesting: configschema.NestingSet,
  1205  						Block: configschema.Block{
  1206  							Attributes: map[string]*configschema.Attribute{
  1207  								"optional": {
  1208  									Type:     cty.String,
  1209  									Optional: true,
  1210  									Computed: true,
  1211  								},
  1212  							},
  1213  						},
  1214  					},
  1215  				},
  1216  				Attributes: map[string]*configschema.Attribute{
  1217  					"bloop": {
  1218  						NestedType: &configschema.Object{
  1219  							Nesting: configschema.NestingSet,
  1220  							Attributes: map[string]*configschema.Attribute{
  1221  								"blop": {
  1222  									Type:     cty.String,
  1223  									Required: true,
  1224  								},
  1225  							},
  1226  						},
  1227  						Optional: true,
  1228  					},
  1229  				},
  1230  			},
  1231  			cty.NullVal(cty.DynamicPseudoType),
  1232  			cty.ObjectVal(map[string]cty.Value{
  1233  				"multi": cty.SetVal([]cty.Value{
  1234  					cty.ObjectVal(map[string]cty.Value{
  1235  						"optional": cty.UnknownVal(cty.String),
  1236  					}),
  1237  					cty.ObjectVal(map[string]cty.Value{
  1238  						"optional": cty.UnknownVal(cty.String),
  1239  					}),
  1240  				}),
  1241  				"bloop": cty.SetVal([]cty.Value{
  1242  					cty.ObjectVal(map[string]cty.Value{
  1243  						"blop": cty.UnknownVal(cty.String),
  1244  					}),
  1245  					cty.ObjectVal(map[string]cty.Value{
  1246  						"blop": cty.UnknownVal(cty.String),
  1247  					}),
  1248  				}),
  1249  			}),
  1250  			cty.ObjectVal(map[string]cty.Value{
  1251  				"multi": cty.SetVal([]cty.Value{
  1252  					// These remain distinct because unknown values never
  1253  					// compare equal. They may be consolidated together once
  1254  					// the values become known, though.
  1255  					cty.ObjectVal(map[string]cty.Value{
  1256  						"optional": cty.UnknownVal(cty.String),
  1257  					}),
  1258  					cty.ObjectVal(map[string]cty.Value{
  1259  						"optional": cty.UnknownVal(cty.String),
  1260  					}),
  1261  				}),
  1262  				"bloop": cty.SetVal([]cty.Value{
  1263  					cty.ObjectVal(map[string]cty.Value{
  1264  						"blop": cty.UnknownVal(cty.String),
  1265  					}),
  1266  					cty.ObjectVal(map[string]cty.Value{
  1267  						"blop": cty.UnknownVal(cty.String),
  1268  					}),
  1269  				}),
  1270  			}),
  1271  		},
  1272  		"nested list in set": {
  1273  			&configschema.Block{
  1274  				BlockTypes: map[string]*configschema.NestedBlock{
  1275  					"foo": {
  1276  						Nesting: configschema.NestingSet,
  1277  						Block: configschema.Block{
  1278  							BlockTypes: map[string]*configschema.NestedBlock{
  1279  								"bar": {
  1280  									Nesting: configschema.NestingList,
  1281  									Block: configschema.Block{
  1282  										Attributes: map[string]*configschema.Attribute{
  1283  											"baz": {
  1284  												Type: cty.String,
  1285  											},
  1286  											"qux": {
  1287  												Type:     cty.String,
  1288  												Computed: true,
  1289  												Optional: true,
  1290  											},
  1291  										},
  1292  									},
  1293  								},
  1294  							},
  1295  						},
  1296  					},
  1297  				},
  1298  			},
  1299  			cty.ObjectVal(map[string]cty.Value{
  1300  				"foo": cty.SetVal([]cty.Value{
  1301  					cty.ObjectVal(map[string]cty.Value{
  1302  						"bar": cty.ListVal([]cty.Value{
  1303  							cty.ObjectVal(map[string]cty.Value{
  1304  								"baz": cty.StringVal("beep"),
  1305  								"qux": cty.StringVal("boop"),
  1306  							}),
  1307  						}),
  1308  					}),
  1309  				}),
  1310  			}),
  1311  			cty.ObjectVal(map[string]cty.Value{
  1312  				"foo": cty.SetVal([]cty.Value{
  1313  					cty.ObjectVal(map[string]cty.Value{
  1314  						"bar": cty.ListVal([]cty.Value{
  1315  							cty.ObjectVal(map[string]cty.Value{
  1316  								"baz": cty.StringVal("beep"),
  1317  								"qux": cty.NullVal(cty.String),
  1318  							}),
  1319  						}),
  1320  					}),
  1321  				}),
  1322  			}),
  1323  			cty.ObjectVal(map[string]cty.Value{
  1324  				"foo": cty.SetVal([]cty.Value{
  1325  					cty.ObjectVal(map[string]cty.Value{
  1326  						"bar": cty.ListVal([]cty.Value{
  1327  							cty.ObjectVal(map[string]cty.Value{
  1328  								"baz": cty.StringVal("beep"),
  1329  								"qux": cty.StringVal("boop"),
  1330  							}),
  1331  						}),
  1332  					}),
  1333  				}),
  1334  			}),
  1335  		},
  1336  		"empty nested list in set": {
  1337  			&configschema.Block{
  1338  				BlockTypes: map[string]*configschema.NestedBlock{
  1339  					"foo": {
  1340  						Nesting: configschema.NestingSet,
  1341  						Block: configschema.Block{
  1342  							BlockTypes: map[string]*configschema.NestedBlock{
  1343  								"bar": {
  1344  									Nesting: configschema.NestingList,
  1345  									Block:   configschema.Block{},
  1346  								},
  1347  							},
  1348  						},
  1349  					},
  1350  				},
  1351  			},
  1352  			cty.ObjectVal(map[string]cty.Value{
  1353  				"foo": cty.SetVal([]cty.Value{
  1354  					cty.ObjectVal(map[string]cty.Value{
  1355  						"bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()),
  1356  					}),
  1357  				}),
  1358  			}),
  1359  			cty.ObjectVal(map[string]cty.Value{
  1360  				"foo": cty.SetVal([]cty.Value{
  1361  					cty.ObjectVal(map[string]cty.Value{
  1362  						"bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()),
  1363  					}),
  1364  				}),
  1365  			}),
  1366  			cty.ObjectVal(map[string]cty.Value{
  1367  				"foo": cty.SetVal([]cty.Value{
  1368  					cty.ObjectVal(map[string]cty.Value{
  1369  						"bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()),
  1370  					}),
  1371  				}),
  1372  			}),
  1373  		},
  1374  		"nested list with dynamic in set": {
  1375  			&configschema.Block{
  1376  				BlockTypes: map[string]*configschema.NestedBlock{
  1377  					"foo": {
  1378  						Nesting: configschema.NestingSet,
  1379  						Block: configschema.Block{
  1380  							BlockTypes: map[string]*configschema.NestedBlock{
  1381  								"bar": {
  1382  									Nesting: configschema.NestingList,
  1383  									Block: configschema.Block{
  1384  										Attributes: map[string]*configschema.Attribute{
  1385  											"baz": {
  1386  												Type: cty.DynamicPseudoType,
  1387  											},
  1388  										},
  1389  									},
  1390  								},
  1391  							},
  1392  						},
  1393  					},
  1394  				},
  1395  			},
  1396  			cty.ObjectVal(map[string]cty.Value{
  1397  				"foo": cty.SetVal([]cty.Value{
  1398  					cty.ObjectVal(map[string]cty.Value{
  1399  						"bar": cty.TupleVal([]cty.Value{
  1400  							cty.ObjectVal(map[string]cty.Value{
  1401  								"baz": cty.StringVal("true"),
  1402  							}),
  1403  							cty.ObjectVal(map[string]cty.Value{
  1404  								"baz": cty.ListVal([]cty.Value{cty.StringVal("true")}),
  1405  							}),
  1406  						}),
  1407  					}),
  1408  				}),
  1409  			}),
  1410  			cty.ObjectVal(map[string]cty.Value{
  1411  				"foo": cty.SetVal([]cty.Value{
  1412  					cty.ObjectVal(map[string]cty.Value{
  1413  						"bar": cty.TupleVal([]cty.Value{
  1414  							cty.ObjectVal(map[string]cty.Value{
  1415  								"baz": cty.StringVal("true"),
  1416  							}),
  1417  							cty.ObjectVal(map[string]cty.Value{
  1418  								"baz": cty.ListVal([]cty.Value{cty.StringVal("true")}),
  1419  							}),
  1420  						}),
  1421  					}),
  1422  				}),
  1423  			}),
  1424  			cty.ObjectVal(map[string]cty.Value{
  1425  				"foo": cty.SetVal([]cty.Value{
  1426  					cty.ObjectVal(map[string]cty.Value{
  1427  						"bar": cty.TupleVal([]cty.Value{
  1428  							cty.ObjectVal(map[string]cty.Value{
  1429  								"baz": cty.StringVal("true"),
  1430  							}),
  1431  							cty.ObjectVal(map[string]cty.Value{
  1432  								"baz": cty.ListVal([]cty.Value{cty.StringVal("true")}),
  1433  							}),
  1434  						}),
  1435  					}),
  1436  				}),
  1437  			}),
  1438  		},
  1439  		"nested map with dynamic in set": {
  1440  			&configschema.Block{
  1441  				BlockTypes: map[string]*configschema.NestedBlock{
  1442  					"foo": {
  1443  						Nesting: configschema.NestingSet,
  1444  						Block: configschema.Block{
  1445  							BlockTypes: map[string]*configschema.NestedBlock{
  1446  								"bar": {
  1447  									Nesting: configschema.NestingMap,
  1448  									Block: configschema.Block{
  1449  										Attributes: map[string]*configschema.Attribute{
  1450  											"baz": {
  1451  												Type:     cty.DynamicPseudoType,
  1452  												Optional: true,
  1453  											},
  1454  										},
  1455  									},
  1456  								},
  1457  							},
  1458  						},
  1459  					},
  1460  				},
  1461  			},
  1462  			cty.ObjectVal(map[string]cty.Value{
  1463  				"foo": cty.SetVal([]cty.Value{
  1464  					cty.ObjectVal(map[string]cty.Value{
  1465  						"bar": cty.ObjectVal(map[string]cty.Value{
  1466  							"bing": cty.ObjectVal(map[string]cty.Value{
  1467  								"baz": cty.StringVal("true"),
  1468  							}),
  1469  							"bang": cty.ObjectVal(map[string]cty.Value{
  1470  								"baz": cty.ListVal([]cty.Value{cty.StringVal("true")}),
  1471  							}),
  1472  						}),
  1473  					}),
  1474  				}),
  1475  			}),
  1476  			cty.ObjectVal(map[string]cty.Value{
  1477  				"foo": cty.SetVal([]cty.Value{
  1478  					cty.ObjectVal(map[string]cty.Value{
  1479  						"bar": cty.ObjectVal(map[string]cty.Value{
  1480  							"bing": cty.ObjectVal(map[string]cty.Value{
  1481  								"baz": cty.ListVal([]cty.Value{cty.StringVal("true")}),
  1482  							}),
  1483  						}),
  1484  					}),
  1485  				}),
  1486  			}),
  1487  			cty.ObjectVal(map[string]cty.Value{
  1488  				"foo": cty.SetVal([]cty.Value{
  1489  					cty.ObjectVal(map[string]cty.Value{
  1490  						"bar": cty.ObjectVal(map[string]cty.Value{
  1491  							"bing": cty.ObjectVal(map[string]cty.Value{
  1492  								"baz": cty.ListVal([]cty.Value{cty.StringVal("true")}),
  1493  							}),
  1494  						}),
  1495  					}),
  1496  				}),
  1497  			}),
  1498  		},
  1499  		"empty nested map in set": {
  1500  			&configschema.Block{
  1501  				BlockTypes: map[string]*configschema.NestedBlock{
  1502  					"foo": {
  1503  						Nesting: configschema.NestingSet,
  1504  						Block: configschema.Block{
  1505  							BlockTypes: map[string]*configschema.NestedBlock{
  1506  								"bar": {
  1507  									Nesting: configschema.NestingMap,
  1508  									Block: configschema.Block{
  1509  										Attributes: map[string]*configschema.Attribute{
  1510  											"baz": {
  1511  												Type:     cty.String,
  1512  												Optional: true,
  1513  											},
  1514  										},
  1515  									},
  1516  								},
  1517  							},
  1518  						},
  1519  					},
  1520  				},
  1521  			},
  1522  			cty.ObjectVal(map[string]cty.Value{
  1523  				"foo": cty.SetVal([]cty.Value{
  1524  					cty.ObjectVal(map[string]cty.Value{
  1525  						"bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{
  1526  							"baz": cty.String,
  1527  						})),
  1528  					}),
  1529  				}),
  1530  			}),
  1531  			cty.ObjectVal(map[string]cty.Value{
  1532  				"foo": cty.SetVal([]cty.Value{
  1533  					cty.ObjectVal(map[string]cty.Value{
  1534  						"bar": cty.MapVal(map[string]cty.Value{
  1535  							"bing": cty.ObjectVal(map[string]cty.Value{
  1536  								"baz": cty.StringVal("true"),
  1537  							}),
  1538  						}),
  1539  					}),
  1540  				}),
  1541  			}),
  1542  			cty.ObjectVal(map[string]cty.Value{
  1543  				"foo": cty.SetVal([]cty.Value{
  1544  					cty.ObjectVal(map[string]cty.Value{
  1545  						"bar": cty.MapVal(map[string]cty.Value{
  1546  							"bing": cty.ObjectVal(map[string]cty.Value{
  1547  								"baz": cty.StringVal("true"),
  1548  							}),
  1549  						}),
  1550  					}),
  1551  				}),
  1552  			}),
  1553  		},
  1554  		// This example has a mixture of optional, computed and required in a deeply-nested NestedType attribute
  1555  		"deeply NestedType": {
  1556  			&configschema.Block{
  1557  				Attributes: map[string]*configschema.Attribute{
  1558  					"foo": {
  1559  						NestedType: &configschema.Object{
  1560  							Nesting: configschema.NestingSingle,
  1561  							Attributes: map[string]*configschema.Attribute{
  1562  								"bar": {
  1563  									NestedType: &configschema.Object{
  1564  										Nesting:    configschema.NestingSingle,
  1565  										Attributes: testAttributes,
  1566  									},
  1567  									Required: true,
  1568  								},
  1569  								"baz": {
  1570  									NestedType: &configschema.Object{
  1571  										Nesting:    configschema.NestingSingle,
  1572  										Attributes: testAttributes,
  1573  									},
  1574  									Optional: true,
  1575  								},
  1576  							},
  1577  						},
  1578  						Optional: true,
  1579  					},
  1580  				},
  1581  			},
  1582  			// prior
  1583  			cty.ObjectVal(map[string]cty.Value{
  1584  				"foo": cty.ObjectVal(map[string]cty.Value{
  1585  					"bar": cty.NullVal(cty.DynamicPseudoType),
  1586  					"baz": cty.ObjectVal(map[string]cty.Value{
  1587  						"optional":          cty.NullVal(cty.String),
  1588  						"computed":          cty.StringVal("hello"),
  1589  						"optional_computed": cty.StringVal("prior"),
  1590  						"required":          cty.StringVal("present"),
  1591  					}),
  1592  				}),
  1593  			}),
  1594  			// config
  1595  			cty.ObjectVal(map[string]cty.Value{
  1596  				"foo": cty.ObjectVal(map[string]cty.Value{
  1597  					"bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown from the config
  1598  						"optional":          cty.String,
  1599  						"computed":          cty.String,
  1600  						"optional_computed": cty.String,
  1601  						"required":          cty.String,
  1602  					})),
  1603  					"baz": cty.ObjectVal(map[string]cty.Value{
  1604  						"optional":          cty.NullVal(cty.String),
  1605  						"computed":          cty.NullVal(cty.String),
  1606  						"optional_computed": cty.StringVal("hello"),
  1607  						"required":          cty.StringVal("present"),
  1608  					}),
  1609  				}),
  1610  			}),
  1611  			// want
  1612  			cty.ObjectVal(map[string]cty.Value{
  1613  				"foo": cty.ObjectVal(map[string]cty.Value{
  1614  					"bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown preserved from the config
  1615  						"optional":          cty.String,
  1616  						"computed":          cty.String,
  1617  						"optional_computed": cty.String,
  1618  						"required":          cty.String,
  1619  					})),
  1620  					"baz": cty.ObjectVal(map[string]cty.Value{
  1621  						"optional":          cty.NullVal(cty.String),  // config is null
  1622  						"computed":          cty.StringVal("hello"),   // computed values come from prior
  1623  						"optional_computed": cty.StringVal("hello"),   // config takes precedent over prior in opt+computed
  1624  						"required":          cty.StringVal("present"), // value from config
  1625  					}),
  1626  				}),
  1627  			}),
  1628  		},
  1629  		"deeply nested set": {
  1630  			&configschema.Block{
  1631  				Attributes: map[string]*configschema.Attribute{
  1632  					"foo": {
  1633  						NestedType: &configschema.Object{
  1634  							Nesting: configschema.NestingSet,
  1635  							Attributes: map[string]*configschema.Attribute{
  1636  								"bar": {
  1637  									NestedType: &configschema.Object{
  1638  										Nesting:    configschema.NestingSet,
  1639  										Attributes: testAttributes,
  1640  									},
  1641  									Required: true,
  1642  								},
  1643  							},
  1644  						},
  1645  						Optional: true,
  1646  					},
  1647  				},
  1648  			},
  1649  			// prior values
  1650  			cty.ObjectVal(map[string]cty.Value{
  1651  				"foo": cty.SetVal([]cty.Value{
  1652  					cty.ObjectVal(map[string]cty.Value{
  1653  						"bar": cty.SetVal([]cty.Value{
  1654  							cty.ObjectVal(map[string]cty.Value{
  1655  								"optional":          cty.StringVal("prior"),
  1656  								"computed":          cty.StringVal("prior"),
  1657  								"optional_computed": cty.StringVal("prior"),
  1658  								"required":          cty.StringVal("prior"),
  1659  							}),
  1660  						}),
  1661  					}),
  1662  					cty.ObjectVal(map[string]cty.Value{
  1663  						"bar": cty.SetVal([]cty.Value{
  1664  							cty.ObjectVal(map[string]cty.Value{
  1665  								"optional":          cty.StringVal("other_prior"),
  1666  								"computed":          cty.StringVal("other_prior"),
  1667  								"optional_computed": cty.StringVal("other_prior"),
  1668  								"required":          cty.StringVal("other_prior"),
  1669  							}),
  1670  						}),
  1671  					}),
  1672  				}),
  1673  			}),
  1674  			// config differs from prior
  1675  			cty.ObjectVal(map[string]cty.Value{
  1676  				"foo": cty.SetVal([]cty.Value{
  1677  					cty.ObjectVal(map[string]cty.Value{
  1678  						"bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  1679  							"optional":          cty.StringVal("configured"),
  1680  							"computed":          cty.NullVal(cty.String), // computed attrs are null in config
  1681  							"optional_computed": cty.StringVal("configured"),
  1682  							"required":          cty.StringVal("configured"),
  1683  						})}),
  1684  					}),
  1685  					cty.ObjectVal(map[string]cty.Value{
  1686  						"bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  1687  							"optional":          cty.NullVal(cty.String), // explicit null in config
  1688  							"computed":          cty.NullVal(cty.String), // computed attrs are null in config
  1689  							"optional_computed": cty.StringVal("other_configured"),
  1690  							"required":          cty.StringVal("other_configured"),
  1691  						})}),
  1692  					}),
  1693  				}),
  1694  			}),
  1695  			// want:
  1696  			cty.ObjectVal(map[string]cty.Value{
  1697  				"foo": cty.SetVal([]cty.Value{
  1698  					cty.ObjectVal(map[string]cty.Value{
  1699  						"bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  1700  							"optional":          cty.StringVal("configured"),
  1701  							"computed":          cty.NullVal(cty.String),
  1702  							"optional_computed": cty.StringVal("configured"),
  1703  							"required":          cty.StringVal("configured"),
  1704  						})}),
  1705  					}),
  1706  					cty.ObjectVal(map[string]cty.Value{
  1707  						"bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  1708  							"optional":          cty.NullVal(cty.String), // explicit null in config is preserved
  1709  							"computed":          cty.NullVal(cty.String),
  1710  							"optional_computed": cty.StringVal("other_configured"),
  1711  							"required":          cty.StringVal("other_configured"),
  1712  						})}),
  1713  					}),
  1714  				}),
  1715  			}),
  1716  		},
  1717  		"expected null NestedTypes": {
  1718  			&configschema.Block{
  1719  				Attributes: map[string]*configschema.Attribute{
  1720  					"single": {
  1721  						NestedType: &configschema.Object{
  1722  							Nesting: configschema.NestingSingle,
  1723  							Attributes: map[string]*configschema.Attribute{
  1724  								"bar": {Type: cty.String},
  1725  							},
  1726  						},
  1727  						Optional: true,
  1728  					},
  1729  					"list": {
  1730  						NestedType: &configschema.Object{
  1731  							Nesting: configschema.NestingList,
  1732  							Attributes: map[string]*configschema.Attribute{
  1733  								"bar": {Type: cty.String},
  1734  							},
  1735  						},
  1736  						Optional: true,
  1737  					},
  1738  					"set": {
  1739  						NestedType: &configschema.Object{
  1740  							Nesting: configschema.NestingSet,
  1741  							Attributes: map[string]*configschema.Attribute{
  1742  								"bar": {Type: cty.String},
  1743  							},
  1744  						},
  1745  						Optional: true,
  1746  					},
  1747  					"map": {
  1748  						NestedType: &configschema.Object{
  1749  							Nesting: configschema.NestingMap,
  1750  							Attributes: map[string]*configschema.Attribute{
  1751  								"bar": {Type: cty.String},
  1752  							},
  1753  						},
  1754  						Optional: true,
  1755  					},
  1756  					"nested_map": {
  1757  						NestedType: &configschema.Object{
  1758  							Nesting: configschema.NestingMap,
  1759  							Attributes: map[string]*configschema.Attribute{
  1760  								"inner": {
  1761  									NestedType: &configschema.Object{
  1762  										Nesting:    configschema.NestingSingle,
  1763  										Attributes: testAttributes,
  1764  									},
  1765  								},
  1766  							},
  1767  						},
  1768  						Optional: true,
  1769  					},
  1770  				},
  1771  			},
  1772  			cty.ObjectVal(map[string]cty.Value{
  1773  				"single": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}),
  1774  				"list":   cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}),
  1775  				"map": cty.MapVal(map[string]cty.Value{
  1776  					"map_entry": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}),
  1777  				}),
  1778  				"set": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}),
  1779  				"nested_map": cty.MapVal(map[string]cty.Value{
  1780  					"a": cty.ObjectVal(map[string]cty.Value{
  1781  						"inner": cty.ObjectVal(map[string]cty.Value{
  1782  							"optional":          cty.StringVal("foo"),
  1783  							"computed":          cty.StringVal("foo"),
  1784  							"optional_computed": cty.StringVal("foo"),
  1785  							"required":          cty.StringVal("foo"),
  1786  						}),
  1787  					}),
  1788  				}),
  1789  			}),
  1790  			cty.ObjectVal(map[string]cty.Value{
  1791  				"single": cty.NullVal(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1792  				"list":   cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))),
  1793  				"map":    cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))),
  1794  				"set":    cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))),
  1795  				"nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
  1796  					"inner": cty.Object(map[string]cty.Type{
  1797  						"optional":          cty.String,
  1798  						"computed":          cty.String,
  1799  						"optional_computed": cty.String,
  1800  						"required":          cty.String,
  1801  					}),
  1802  				}))),
  1803  			}),
  1804  			cty.ObjectVal(map[string]cty.Value{
  1805  				"single": cty.NullVal(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1806  				"list":   cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))),
  1807  				"map":    cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))),
  1808  				"set":    cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))),
  1809  				"nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
  1810  					"inner": cty.Object(map[string]cty.Type{
  1811  						"optional":          cty.String,
  1812  						"computed":          cty.String,
  1813  						"optional_computed": cty.String,
  1814  						"required":          cty.String,
  1815  					}),
  1816  				}))),
  1817  			}),
  1818  		},
  1819  		"expected empty NestedTypes": {
  1820  			&configschema.Block{
  1821  				Attributes: map[string]*configschema.Attribute{
  1822  					"set": {
  1823  						NestedType: &configschema.Object{
  1824  							Nesting: configschema.NestingSet,
  1825  							Attributes: map[string]*configschema.Attribute{
  1826  								"bar": {Type: cty.String},
  1827  							},
  1828  						},
  1829  						Optional: true,
  1830  					},
  1831  					"map": {
  1832  						NestedType: &configschema.Object{
  1833  							Nesting: configschema.NestingMap,
  1834  							Attributes: map[string]*configschema.Attribute{
  1835  								"bar": {Type: cty.String},
  1836  							},
  1837  						},
  1838  						Optional: true,
  1839  					},
  1840  				},
  1841  			},
  1842  			cty.ObjectVal(map[string]cty.Value{
  1843  				"map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1844  				"set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1845  			}),
  1846  			cty.ObjectVal(map[string]cty.Value{
  1847  				"map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1848  				"set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1849  			}),
  1850  			cty.ObjectVal(map[string]cty.Value{
  1851  				"map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1852  				"set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})),
  1853  			}),
  1854  		},
  1855  		"optional types set replacement": {
  1856  			&configschema.Block{
  1857  				Attributes: map[string]*configschema.Attribute{
  1858  					"set": {
  1859  						NestedType: &configschema.Object{
  1860  							Nesting: configschema.NestingSet,
  1861  							Attributes: map[string]*configschema.Attribute{
  1862  								"bar": {
  1863  									Type:     cty.String,
  1864  									Required: true,
  1865  								},
  1866  							},
  1867  						},
  1868  						Optional: true,
  1869  					},
  1870  				},
  1871  			},
  1872  			cty.ObjectVal(map[string]cty.Value{
  1873  				"set": cty.SetVal([]cty.Value{
  1874  					cty.ObjectVal(map[string]cty.Value{
  1875  						"bar": cty.StringVal("old"),
  1876  					}),
  1877  				}),
  1878  			}),
  1879  			cty.ObjectVal(map[string]cty.Value{
  1880  				"set": cty.SetVal([]cty.Value{
  1881  					cty.ObjectVal(map[string]cty.Value{
  1882  						"bar": cty.StringVal("new"),
  1883  					}),
  1884  				}),
  1885  			}),
  1886  			cty.ObjectVal(map[string]cty.Value{
  1887  				"set": cty.SetVal([]cty.Value{
  1888  					cty.ObjectVal(map[string]cty.Value{
  1889  						"bar": cty.StringVal("new"),
  1890  					}),
  1891  				}),
  1892  			}),
  1893  		},
  1894  		"prior null nested objects": {
  1895  			&configschema.Block{
  1896  				Attributes: map[string]*configschema.Attribute{
  1897  					"single": {
  1898  						NestedType: &configschema.Object{
  1899  							Nesting: configschema.NestingSingle,
  1900  							Attributes: map[string]*configschema.Attribute{
  1901  								"list": {
  1902  									NestedType: &configschema.Object{
  1903  										Nesting: configschema.NestingList,
  1904  										Attributes: map[string]*configschema.Attribute{
  1905  											"foo": {
  1906  												Type: cty.String,
  1907  											},
  1908  										},
  1909  									},
  1910  									Optional: true,
  1911  								},
  1912  							},
  1913  						},
  1914  						Optional: true,
  1915  					},
  1916  					"map": {
  1917  						NestedType: &configschema.Object{
  1918  							Nesting: configschema.NestingMap,
  1919  							Attributes: map[string]*configschema.Attribute{
  1920  								"map": {
  1921  									NestedType: &configschema.Object{
  1922  										Nesting: configschema.NestingList,
  1923  										Attributes: map[string]*configschema.Attribute{
  1924  											"foo": {
  1925  												Type: cty.String,
  1926  											},
  1927  										},
  1928  									},
  1929  									Optional: true,
  1930  								},
  1931  							},
  1932  						},
  1933  						Optional: true,
  1934  					},
  1935  				},
  1936  			},
  1937  			cty.NullVal(cty.Object(map[string]cty.Type{
  1938  				"single": cty.Object(map[string]cty.Type{
  1939  					"list": cty.List(cty.Object(map[string]cty.Type{
  1940  						"foo": cty.String,
  1941  					})),
  1942  				}),
  1943  				"map": cty.Map(cty.Object(map[string]cty.Type{
  1944  					"list": cty.List(cty.Object(map[string]cty.Type{
  1945  						"foo": cty.String,
  1946  					})),
  1947  				})),
  1948  			})),
  1949  			cty.ObjectVal(map[string]cty.Value{
  1950  				"single": cty.ObjectVal(map[string]cty.Value{
  1951  					"list": cty.ListVal([]cty.Value{
  1952  						cty.ObjectVal(map[string]cty.Value{
  1953  							"foo": cty.StringVal("a"),
  1954  						}),
  1955  						cty.ObjectVal(map[string]cty.Value{
  1956  							"foo": cty.StringVal("b"),
  1957  						}),
  1958  					}),
  1959  				}),
  1960  				"map": cty.MapVal(map[string]cty.Value{
  1961  					"one": cty.ObjectVal(map[string]cty.Value{
  1962  						"list": cty.ListVal([]cty.Value{
  1963  							cty.ObjectVal(map[string]cty.Value{
  1964  								"foo": cty.StringVal("a"),
  1965  							}),
  1966  							cty.ObjectVal(map[string]cty.Value{
  1967  								"foo": cty.StringVal("b"),
  1968  							}),
  1969  						}),
  1970  					}),
  1971  				}),
  1972  			}),
  1973  			cty.ObjectVal(map[string]cty.Value{
  1974  				"single": cty.ObjectVal(map[string]cty.Value{
  1975  					"list": cty.ListVal([]cty.Value{
  1976  						cty.ObjectVal(map[string]cty.Value{
  1977  							"foo": cty.StringVal("a"),
  1978  						}),
  1979  						cty.ObjectVal(map[string]cty.Value{
  1980  							"foo": cty.StringVal("b"),
  1981  						}),
  1982  					}),
  1983  				}),
  1984  				"map": cty.MapVal(map[string]cty.Value{
  1985  					"one": cty.ObjectVal(map[string]cty.Value{
  1986  						"list": cty.ListVal([]cty.Value{
  1987  							cty.ObjectVal(map[string]cty.Value{
  1988  								"foo": cty.StringVal("a"),
  1989  							}),
  1990  							cty.ObjectVal(map[string]cty.Value{
  1991  								"foo": cty.StringVal("b"),
  1992  							}),
  1993  						}),
  1994  					}),
  1995  				}),
  1996  			}),
  1997  		},
  1998  
  1999  		// Data sources are planned with an unknown value.
  2000  		// Note that this plan fails AssertPlanValid, because for managed
  2001  		// resources an instance would never be completely unknown.
  2002  		"unknown prior nested objects": {
  2003  			&configschema.Block{
  2004  				Attributes: map[string]*configschema.Attribute{
  2005  					"list": {
  2006  						NestedType: &configschema.Object{
  2007  							Nesting: configschema.NestingList,
  2008  							Attributes: map[string]*configschema.Attribute{
  2009  								"list": {
  2010  									NestedType: &configschema.Object{
  2011  										Nesting: configschema.NestingList,
  2012  										Attributes: map[string]*configschema.Attribute{
  2013  											"foo": {
  2014  												Type: cty.String,
  2015  											},
  2016  										},
  2017  									},
  2018  									Computed: true,
  2019  								},
  2020  							},
  2021  						},
  2022  						Computed: true,
  2023  					},
  2024  				},
  2025  			},
  2026  			cty.UnknownVal(cty.Object(map[string]cty.Type{
  2027  				"list": cty.List(cty.Object(map[string]cty.Type{
  2028  					"list": cty.List(cty.Object(map[string]cty.Type{
  2029  						"foo": cty.String,
  2030  					})),
  2031  				})),
  2032  			})),
  2033  			cty.NullVal(cty.Object(map[string]cty.Type{
  2034  				"list": cty.List(cty.Object(map[string]cty.Type{
  2035  					"list": cty.List(cty.Object(map[string]cty.Type{
  2036  						"foo": cty.String,
  2037  					})),
  2038  				})),
  2039  			})),
  2040  			cty.UnknownVal(cty.Object(map[string]cty.Type{
  2041  				"list": cty.List(cty.Object(map[string]cty.Type{
  2042  					"list": cty.List(cty.Object(map[string]cty.Type{
  2043  						"foo": cty.String,
  2044  					})),
  2045  				})),
  2046  			})),
  2047  		},
  2048  
  2049  		// A nested object with computed attributes, which is contained in an
  2050  		// optional+computed container. The nested computed values should be
  2051  		// represented in the proposed new object.
  2052  		"config within optional+computed": {
  2053  			&configschema.Block{
  2054  				Attributes: map[string]*configschema.Attribute{
  2055  					"list_obj": {
  2056  						Optional: true,
  2057  						Computed: true,
  2058  						NestedType: &configschema.Object{
  2059  							Nesting: configschema.NestingList,
  2060  							Attributes: map[string]*configschema.Attribute{
  2061  								"obj": {
  2062  									Optional: true,
  2063  									NestedType: &configschema.Object{
  2064  										Nesting: configschema.NestingSingle,
  2065  										Attributes: map[string]*configschema.Attribute{
  2066  											"optional": {Type: cty.String, Optional: true},
  2067  											"computed": {Type: cty.String, Computed: true},
  2068  										},
  2069  									},
  2070  								},
  2071  							},
  2072  						},
  2073  					},
  2074  				},
  2075  			},
  2076  			cty.ObjectVal(map[string]cty.Value{
  2077  				"list_obj": cty.ListVal([]cty.Value{
  2078  					cty.ObjectVal(map[string]cty.Value{
  2079  						"obj": cty.ObjectVal(map[string]cty.Value{
  2080  							"optional": cty.StringVal("prior"),
  2081  							"computed": cty.StringVal("prior computed"),
  2082  						}),
  2083  					}),
  2084  				}),
  2085  			}),
  2086  			cty.ObjectVal(map[string]cty.Value{
  2087  				"list_obj": cty.ListVal([]cty.Value{
  2088  					cty.ObjectVal(map[string]cty.Value{
  2089  						"obj": cty.ObjectVal(map[string]cty.Value{
  2090  							"optional": cty.StringVal("prior"),
  2091  							"computed": cty.NullVal(cty.String),
  2092  						}),
  2093  					}),
  2094  				}),
  2095  			}),
  2096  			cty.ObjectVal(map[string]cty.Value{
  2097  				"list_obj": cty.ListVal([]cty.Value{
  2098  					cty.ObjectVal(map[string]cty.Value{
  2099  						"obj": cty.ObjectVal(map[string]cty.Value{
  2100  							"optional": cty.StringVal("prior"),
  2101  							"computed": cty.StringVal("prior computed"),
  2102  						}),
  2103  					}),
  2104  				}),
  2105  			}),
  2106  		},
  2107  
  2108  		// A nested object with computed attributes, which is contained in an
  2109  		// optional+computed container. The prior nested object contains values
  2110  		// which could not be computed, therefor the proposed new value must be
  2111  		// the null value from the configuration.
  2112  		"computed within optional+computed": {
  2113  			&configschema.Block{
  2114  				Attributes: map[string]*configschema.Attribute{
  2115  					"list_obj": {
  2116  						Optional: true,
  2117  						Computed: true,
  2118  						NestedType: &configschema.Object{
  2119  							Nesting: configschema.NestingList,
  2120  							Attributes: map[string]*configschema.Attribute{
  2121  								"obj": {
  2122  									Optional: true,
  2123  									NestedType: &configschema.Object{
  2124  										Nesting: configschema.NestingSingle,
  2125  										Attributes: map[string]*configschema.Attribute{
  2126  											"optional": {Type: cty.String, Optional: true},
  2127  											"computed": {Type: cty.String, Computed: true},
  2128  										},
  2129  									},
  2130  								},
  2131  							},
  2132  						},
  2133  					},
  2134  				},
  2135  			},
  2136  			cty.ObjectVal(map[string]cty.Value{
  2137  				"list_obj": cty.ListVal([]cty.Value{
  2138  					cty.ObjectVal(map[string]cty.Value{
  2139  						"obj": cty.ObjectVal(map[string]cty.Value{
  2140  							"optional": cty.StringVal("prior"),
  2141  							"computed": cty.StringVal("prior computed"),
  2142  						}),
  2143  					}),
  2144  				}),
  2145  			}),
  2146  			cty.ObjectVal(map[string]cty.Value{
  2147  				"list_obj": cty.NullVal(cty.List(
  2148  					cty.Object(map[string]cty.Type{
  2149  						"obj": cty.Object(map[string]cty.Type{
  2150  							"optional": cty.String,
  2151  							"computed": cty.String,
  2152  						}),
  2153  					}),
  2154  				)),
  2155  			}),
  2156  			cty.ObjectVal(map[string]cty.Value{
  2157  				"list_obj": cty.NullVal(cty.List(
  2158  					cty.Object(map[string]cty.Type{
  2159  						"obj": cty.Object(map[string]cty.Type{
  2160  							"optional": cty.String,
  2161  							"computed": cty.String,
  2162  						}),
  2163  					}),
  2164  				)),
  2165  			}),
  2166  		},
  2167  
  2168  		// A nested object with computed attributes, which is contained in an
  2169  		// optional+computed set. The nested computed values should be
  2170  		// represented in the proposed new object, and correlated with state
  2171  		// via the non-computed attributes.
  2172  		"config add within optional+computed set": {
  2173  			&configschema.Block{
  2174  				Attributes: map[string]*configschema.Attribute{
  2175  					"set_obj": {
  2176  						Optional: true,
  2177  						Computed: true,
  2178  						NestedType: &configschema.Object{
  2179  							Nesting: configschema.NestingSet,
  2180  							Attributes: map[string]*configschema.Attribute{
  2181  								"obj": {
  2182  									Optional: true,
  2183  									NestedType: &configschema.Object{
  2184  										Nesting: configschema.NestingSingle,
  2185  										Attributes: map[string]*configschema.Attribute{
  2186  											"optional": {Type: cty.String, Optional: true},
  2187  											"computed": {Type: cty.String, Computed: true},
  2188  										},
  2189  									},
  2190  								},
  2191  							},
  2192  						},
  2193  					},
  2194  				},
  2195  			},
  2196  			cty.ObjectVal(map[string]cty.Value{
  2197  				"set_obj": cty.SetVal([]cty.Value{
  2198  					cty.ObjectVal(map[string]cty.Value{
  2199  						"obj": cty.ObjectVal(map[string]cty.Value{
  2200  							"optional": cty.StringVal("first"),
  2201  							"computed": cty.StringVal("first computed"),
  2202  						}),
  2203  					}),
  2204  					cty.ObjectVal(map[string]cty.Value{
  2205  						"obj": cty.ObjectVal(map[string]cty.Value{
  2206  							"optional": cty.StringVal("second"),
  2207  							"computed": cty.StringVal("second computed"),
  2208  						}),
  2209  					}),
  2210  				}),
  2211  			}),
  2212  			cty.ObjectVal(map[string]cty.Value{
  2213  				"set_obj": cty.SetVal([]cty.Value{
  2214  					cty.ObjectVal(map[string]cty.Value{
  2215  						"obj": cty.ObjectVal(map[string]cty.Value{
  2216  							"optional": cty.StringVal("first"),
  2217  							"computed": cty.NullVal(cty.String),
  2218  						}),
  2219  					}),
  2220  					cty.ObjectVal(map[string]cty.Value{
  2221  						"obj": cty.ObjectVal(map[string]cty.Value{
  2222  							"optional": cty.StringVal("second"),
  2223  							"computed": cty.NullVal(cty.String),
  2224  						}),
  2225  					}),
  2226  					cty.ObjectVal(map[string]cty.Value{
  2227  						"obj": cty.ObjectVal(map[string]cty.Value{
  2228  							"optional": cty.StringVal("third"),
  2229  							"computed": cty.NullVal(cty.String),
  2230  						}),
  2231  					}),
  2232  				}),
  2233  			}),
  2234  			cty.ObjectVal(map[string]cty.Value{
  2235  				"set_obj": cty.SetVal([]cty.Value{
  2236  					cty.ObjectVal(map[string]cty.Value{
  2237  						"obj": cty.ObjectVal(map[string]cty.Value{
  2238  							"optional": cty.StringVal("first"),
  2239  							"computed": cty.StringVal("first computed"),
  2240  						}),
  2241  					}),
  2242  					cty.ObjectVal(map[string]cty.Value{
  2243  						"obj": cty.ObjectVal(map[string]cty.Value{
  2244  							"optional": cty.StringVal("second"),
  2245  							"computed": cty.StringVal("second computed"),
  2246  						}),
  2247  					}),
  2248  					cty.ObjectVal(map[string]cty.Value{
  2249  						"obj": cty.ObjectVal(map[string]cty.Value{
  2250  							"optional": cty.StringVal("third"),
  2251  							"computed": cty.NullVal(cty.String),
  2252  						}),
  2253  					}),
  2254  				}),
  2255  			}),
  2256  		},
  2257  
  2258  		// A nested object with computed attributes, which is contained in a
  2259  		// set. The nested computed values should be represented in the
  2260  		// proposed new object, and correlated with state via the non-computed
  2261  		// attributes.
  2262  		"config add within set block": {
  2263  			&configschema.Block{
  2264  				BlockTypes: map[string]*configschema.NestedBlock{
  2265  					"set_obj": {
  2266  						Nesting: configschema.NestingSet,
  2267  						Block: configschema.Block{
  2268  							Attributes: map[string]*configschema.Attribute{
  2269  								"obj": {
  2270  									Optional: true,
  2271  									NestedType: &configschema.Object{
  2272  										Nesting: configschema.NestingSingle,
  2273  										Attributes: map[string]*configschema.Attribute{
  2274  											"optional": {Type: cty.String, Optional: true},
  2275  											"computed": {Type: cty.String, Optional: true, Computed: true},
  2276  										},
  2277  									},
  2278  								},
  2279  							},
  2280  						},
  2281  					},
  2282  				},
  2283  			},
  2284  			cty.ObjectVal(map[string]cty.Value{
  2285  				"set_obj": cty.SetVal([]cty.Value{
  2286  					cty.ObjectVal(map[string]cty.Value{
  2287  						"obj": cty.ObjectVal(map[string]cty.Value{
  2288  							"optional": cty.StringVal("first"),
  2289  							"computed": cty.StringVal("first computed"),
  2290  						}),
  2291  					}),
  2292  					cty.ObjectVal(map[string]cty.Value{
  2293  						"obj": cty.ObjectVal(map[string]cty.Value{
  2294  							"optional": cty.StringVal("second"),
  2295  							"computed": cty.StringVal("second from config"),
  2296  						}),
  2297  					}),
  2298  				}),
  2299  			}),
  2300  			cty.ObjectVal(map[string]cty.Value{
  2301  				"set_obj": cty.SetVal([]cty.Value{
  2302  					cty.ObjectVal(map[string]cty.Value{
  2303  						"obj": cty.ObjectVal(map[string]cty.Value{
  2304  							"optional": cty.StringVal("first"),
  2305  							"computed": cty.NullVal(cty.String),
  2306  						}),
  2307  					}),
  2308  					cty.ObjectVal(map[string]cty.Value{
  2309  						"obj": cty.ObjectVal(map[string]cty.Value{
  2310  							"optional": cty.StringVal("second"),
  2311  							"computed": cty.StringVal("second from config"),
  2312  						}),
  2313  					}),
  2314  					// new "third" value added
  2315  					cty.ObjectVal(map[string]cty.Value{
  2316  						"obj": cty.ObjectVal(map[string]cty.Value{
  2317  							"optional": cty.StringVal("third"),
  2318  							"computed": cty.NullVal(cty.String),
  2319  						}),
  2320  					}),
  2321  				}),
  2322  			}),
  2323  			cty.ObjectVal(map[string]cty.Value{
  2324  				"set_obj": cty.SetVal([]cty.Value{
  2325  					cty.ObjectVal(map[string]cty.Value{
  2326  						"obj": cty.ObjectVal(map[string]cty.Value{
  2327  							"optional": cty.StringVal("first"),
  2328  							"computed": cty.StringVal("first computed"),
  2329  						}),
  2330  					}),
  2331  					cty.ObjectVal(map[string]cty.Value{
  2332  						"obj": cty.ObjectVal(map[string]cty.Value{
  2333  							"optional": cty.StringVal("second"),
  2334  							"computed": cty.StringVal("second from config"),
  2335  						}),
  2336  					}),
  2337  					cty.ObjectVal(map[string]cty.Value{
  2338  						"obj": cty.ObjectVal(map[string]cty.Value{
  2339  							"optional": cty.StringVal("third"),
  2340  							"computed": cty.NullVal(cty.String),
  2341  						}),
  2342  					}),
  2343  				}),
  2344  			}),
  2345  		},
  2346  
  2347  		// A nested object with computed attributes, which is contained in a
  2348  		// set. The nested computed values should be represented in the
  2349  		// proposed new object, and correlated with state via the non-computed
  2350  		// attributes.
  2351  		"config change within set block": {
  2352  			&configschema.Block{
  2353  				BlockTypes: map[string]*configschema.NestedBlock{
  2354  					"set_obj": {
  2355  						Nesting: configschema.NestingSet,
  2356  						Block: configschema.Block{
  2357  							Attributes: map[string]*configschema.Attribute{
  2358  								"obj": {
  2359  									Optional: true,
  2360  									NestedType: &configschema.Object{
  2361  										Nesting: configschema.NestingSingle,
  2362  										Attributes: map[string]*configschema.Attribute{
  2363  											"optional": {Type: cty.String, Optional: true},
  2364  											"computed": {Type: cty.String, Optional: true, Computed: true},
  2365  										},
  2366  									},
  2367  								},
  2368  							},
  2369  						},
  2370  					},
  2371  				},
  2372  			},
  2373  			cty.ObjectVal(map[string]cty.Value{
  2374  				"set_obj": cty.SetVal([]cty.Value{
  2375  					cty.ObjectVal(map[string]cty.Value{
  2376  						"obj": cty.ObjectVal(map[string]cty.Value{
  2377  							"optional": cty.StringVal("first"),
  2378  							"computed": cty.StringVal("first computed"),
  2379  						}),
  2380  					}),
  2381  					cty.ObjectVal(map[string]cty.Value{
  2382  						"obj": cty.ObjectVal(map[string]cty.Value{
  2383  							"optional": cty.StringVal("second"),
  2384  							"computed": cty.StringVal("second computed"),
  2385  						}),
  2386  					}),
  2387  				}),
  2388  			}),
  2389  			cty.ObjectVal(map[string]cty.Value{
  2390  				"set_obj": cty.SetVal([]cty.Value{
  2391  					cty.ObjectVal(map[string]cty.Value{
  2392  						"obj": cty.ObjectVal(map[string]cty.Value{
  2393  							"optional": cty.StringVal("first"),
  2394  							"computed": cty.NullVal(cty.String),
  2395  						}),
  2396  					}),
  2397  					cty.ObjectVal(map[string]cty.Value{
  2398  						"obj": cty.ObjectVal(map[string]cty.Value{
  2399  							"optional": cty.StringVal("changed"),
  2400  							"computed": cty.NullVal(cty.String),
  2401  						}),
  2402  					}),
  2403  				}),
  2404  			}),
  2405  			cty.ObjectVal(map[string]cty.Value{
  2406  				"set_obj": cty.SetVal([]cty.Value{
  2407  					cty.ObjectVal(map[string]cty.Value{
  2408  						"obj": cty.ObjectVal(map[string]cty.Value{
  2409  							"optional": cty.StringVal("first"),
  2410  							"computed": cty.StringVal("first computed"),
  2411  						}),
  2412  					}),
  2413  					cty.ObjectVal(map[string]cty.Value{
  2414  						"obj": cty.ObjectVal(map[string]cty.Value{
  2415  							"optional": cty.StringVal("changed"),
  2416  							"computed": cty.NullVal(cty.String),
  2417  						}),
  2418  					}),
  2419  				}),
  2420  			}),
  2421  		},
  2422  
  2423  		"set attr with partial optional computed change": {
  2424  			&configschema.Block{
  2425  				Attributes: map[string]*configschema.Attribute{
  2426  					"multi": {
  2427  						Optional: true,
  2428  						NestedType: &configschema.Object{
  2429  							Nesting: configschema.NestingSet,
  2430  							Attributes: map[string]*configschema.Attribute{
  2431  								"opt": {
  2432  									Type:     cty.String,
  2433  									Optional: true,
  2434  								},
  2435  								"oc": {
  2436  									Type:     cty.String,
  2437  									Optional: true,
  2438  									Computed: true,
  2439  								},
  2440  							},
  2441  						},
  2442  					},
  2443  				},
  2444  			},
  2445  			cty.ObjectVal(map[string]cty.Value{
  2446  				"multi": cty.SetVal([]cty.Value{
  2447  					cty.ObjectVal(map[string]cty.Value{
  2448  						"opt": cty.StringVal("one"),
  2449  						"oc":  cty.StringVal("OK"),
  2450  					}),
  2451  					cty.ObjectVal(map[string]cty.Value{
  2452  						"opt": cty.StringVal("two"),
  2453  						"oc":  cty.StringVal("OK"),
  2454  					}),
  2455  				}),
  2456  			}),
  2457  			cty.ObjectVal(map[string]cty.Value{
  2458  				"multi": cty.SetVal([]cty.Value{
  2459  					cty.ObjectVal(map[string]cty.Value{
  2460  						"opt": cty.StringVal("one"),
  2461  						"oc":  cty.NullVal(cty.String),
  2462  					}),
  2463  					cty.ObjectVal(map[string]cty.Value{
  2464  						"opt": cty.StringVal("replaced"),
  2465  						"oc":  cty.NullVal(cty.String),
  2466  					}),
  2467  				}),
  2468  			}),
  2469  			cty.ObjectVal(map[string]cty.Value{
  2470  				"multi": cty.SetVal([]cty.Value{
  2471  					cty.ObjectVal(map[string]cty.Value{
  2472  						"opt": cty.StringVal("one"),
  2473  						"oc":  cty.StringVal("OK"),
  2474  					}),
  2475  					cty.ObjectVal(map[string]cty.Value{
  2476  						"opt": cty.StringVal("replaced"),
  2477  						"oc":  cty.NullVal(cty.String),
  2478  					}),
  2479  				}),
  2480  			}),
  2481  		},
  2482  
  2483  		"set attr without optional computed change": {
  2484  			&configschema.Block{
  2485  				Attributes: map[string]*configschema.Attribute{
  2486  					"multi": {
  2487  						Optional: true,
  2488  						NestedType: &configschema.Object{
  2489  							Nesting: configschema.NestingSet,
  2490  							Attributes: map[string]*configschema.Attribute{
  2491  								"opt": {
  2492  									Type:     cty.String,
  2493  									Optional: true,
  2494  								},
  2495  								"oc": {
  2496  									Type:     cty.String,
  2497  									Optional: true,
  2498  									Computed: true,
  2499  								},
  2500  							},
  2501  						},
  2502  					},
  2503  				},
  2504  			},
  2505  			cty.ObjectVal(map[string]cty.Value{
  2506  				"multi": cty.SetVal([]cty.Value{
  2507  					cty.ObjectVal(map[string]cty.Value{
  2508  						"opt": cty.StringVal("one"),
  2509  						"oc":  cty.StringVal("OK"),
  2510  					}),
  2511  					cty.ObjectVal(map[string]cty.Value{
  2512  						"opt": cty.StringVal("two"),
  2513  						"oc":  cty.StringVal("OK"),
  2514  					}),
  2515  				}),
  2516  			}),
  2517  			cty.ObjectVal(map[string]cty.Value{
  2518  				"multi": cty.SetVal([]cty.Value{
  2519  					cty.ObjectVal(map[string]cty.Value{
  2520  						"opt": cty.StringVal("one"),
  2521  						"oc":  cty.NullVal(cty.String),
  2522  					}),
  2523  					cty.ObjectVal(map[string]cty.Value{
  2524  						"opt": cty.StringVal("two"),
  2525  						"oc":  cty.NullVal(cty.String),
  2526  					}),
  2527  				}),
  2528  			}),
  2529  			cty.ObjectVal(map[string]cty.Value{
  2530  				"multi": cty.SetVal([]cty.Value{
  2531  					cty.ObjectVal(map[string]cty.Value{
  2532  						"opt": cty.StringVal("one"),
  2533  						"oc":  cty.StringVal("OK"),
  2534  					}),
  2535  					cty.ObjectVal(map[string]cty.Value{
  2536  						"opt": cty.StringVal("two"),
  2537  						"oc":  cty.StringVal("OK"),
  2538  					}),
  2539  				}),
  2540  			}),
  2541  		},
  2542  
  2543  		"set attr with all optional computed": {
  2544  			&configschema.Block{
  2545  				Attributes: map[string]*configschema.Attribute{
  2546  					"multi": {
  2547  						Optional: true,
  2548  						NestedType: &configschema.Object{
  2549  							Nesting: configschema.NestingSet,
  2550  							Attributes: map[string]*configschema.Attribute{
  2551  								"opt": {
  2552  									Type:     cty.String,
  2553  									Optional: true,
  2554  									Computed: true,
  2555  								},
  2556  								"oc": {
  2557  									Type:     cty.String,
  2558  									Optional: true,
  2559  									Computed: true,
  2560  								},
  2561  							},
  2562  						},
  2563  					},
  2564  				},
  2565  			},
  2566  			cty.ObjectVal(map[string]cty.Value{
  2567  				"multi": cty.SetVal([]cty.Value{
  2568  					cty.ObjectVal(map[string]cty.Value{
  2569  						"opt": cty.StringVal("one"),
  2570  						"oc":  cty.StringVal("OK"),
  2571  					}),
  2572  					cty.ObjectVal(map[string]cty.Value{
  2573  						"opt": cty.StringVal("two"),
  2574  						"oc":  cty.StringVal("OK"),
  2575  					}),
  2576  				}),
  2577  			}),
  2578  			// Each of these values can be correlated by the existence of the
  2579  			// optional config attribute. Because "one" and "two" are set in
  2580  			// the config, they must exist in the state regardless of
  2581  			// optional&computed.
  2582  			cty.ObjectVal(map[string]cty.Value{
  2583  				"multi": cty.SetVal([]cty.Value{
  2584  					cty.ObjectVal(map[string]cty.Value{
  2585  						"opt": cty.StringVal("one"),
  2586  						"oc":  cty.NullVal(cty.String),
  2587  					}),
  2588  					cty.ObjectVal(map[string]cty.Value{
  2589  						"opt": cty.StringVal("two"),
  2590  						"oc":  cty.NullVal(cty.String),
  2591  					}),
  2592  				}),
  2593  			}),
  2594  			cty.ObjectVal(map[string]cty.Value{
  2595  				"multi": cty.SetVal([]cty.Value{
  2596  					cty.ObjectVal(map[string]cty.Value{
  2597  						"opt": cty.StringVal("one"),
  2598  						"oc":  cty.StringVal("OK"),
  2599  					}),
  2600  					cty.ObjectVal(map[string]cty.Value{
  2601  						"opt": cty.StringVal("two"),
  2602  						"oc":  cty.StringVal("OK"),
  2603  					}),
  2604  				}),
  2605  			}),
  2606  		},
  2607  
  2608  		"set block with all optional computed and nested object types": {
  2609  			&configschema.Block{
  2610  				BlockTypes: map[string]*configschema.NestedBlock{
  2611  					"multi": {
  2612  						Nesting: configschema.NestingSet,
  2613  						Block: configschema.Block{
  2614  							Attributes: map[string]*configschema.Attribute{
  2615  								"opt": {
  2616  									Type:     cty.String,
  2617  									Optional: true,
  2618  									Computed: true,
  2619  								},
  2620  								"oc": {
  2621  									Type:     cty.String,
  2622  									Optional: true,
  2623  									Computed: true,
  2624  								},
  2625  								"attr": {
  2626  									Optional: true,
  2627  									NestedType: &configschema.Object{
  2628  										Nesting: configschema.NestingSet,
  2629  										Attributes: map[string]*configschema.Attribute{
  2630  											"opt": {
  2631  												Type:     cty.String,
  2632  												Optional: true,
  2633  												Computed: true,
  2634  											},
  2635  											"oc": {
  2636  												Type:     cty.String,
  2637  												Optional: true,
  2638  												Computed: true,
  2639  											},
  2640  										},
  2641  									},
  2642  								},
  2643  							},
  2644  						},
  2645  					},
  2646  				},
  2647  			},
  2648  			cty.ObjectVal(map[string]cty.Value{
  2649  				"multi": cty.SetVal([]cty.Value{
  2650  					cty.ObjectVal(map[string]cty.Value{
  2651  						"opt": cty.StringVal("one"),
  2652  						"oc":  cty.StringVal("OK"),
  2653  						"attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  2654  							"opt": cty.StringVal("one"),
  2655  							"oc":  cty.StringVal("OK"),
  2656  						})}),
  2657  					}),
  2658  					cty.ObjectVal(map[string]cty.Value{
  2659  						"opt": cty.StringVal("two"),
  2660  						"oc":  cty.StringVal("OK"),
  2661  						"attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  2662  							"opt": cty.StringVal("two"),
  2663  							"oc":  cty.StringVal("OK"),
  2664  						})}),
  2665  					}),
  2666  				}),
  2667  			}),
  2668  			cty.ObjectVal(map[string]cty.Value{
  2669  				"multi": cty.SetVal([]cty.Value{
  2670  					cty.ObjectVal(map[string]cty.Value{
  2671  						"opt": cty.StringVal("one"),
  2672  						"oc":  cty.NullVal(cty.String),
  2673  						"attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  2674  							"opt": cty.StringVal("one"),
  2675  							"oc":  cty.StringVal("OK"),
  2676  						})}),
  2677  					}),
  2678  					cty.ObjectVal(map[string]cty.Value{
  2679  						"opt": cty.StringVal("two"),
  2680  						"oc":  cty.StringVal("OK"),
  2681  						"attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  2682  							"opt": cty.StringVal("two"),
  2683  							"oc":  cty.NullVal(cty.String),
  2684  						})}),
  2685  					}),
  2686  					cty.ObjectVal(map[string]cty.Value{
  2687  						"opt": cty.StringVal("three"),
  2688  						"oc":  cty.NullVal(cty.String),
  2689  						"attr": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  2690  							"opt": cty.String,
  2691  							"oc":  cty.String,
  2692  						}))),
  2693  					}),
  2694  				}),
  2695  			}),
  2696  			cty.ObjectVal(map[string]cty.Value{
  2697  				"multi": cty.SetVal([]cty.Value{
  2698  					// We can correlate this with prior from the outer object
  2699  					// attributes, and the equal nested set.
  2700  					cty.ObjectVal(map[string]cty.Value{
  2701  						"opt": cty.StringVal("one"),
  2702  						"oc":  cty.StringVal("OK"),
  2703  						"attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  2704  							"opt": cty.StringVal("one"),
  2705  							"oc":  cty.StringVal("OK"),
  2706  						})}),
  2707  					}),
  2708  					// This value is overridden by config, because we can't
  2709  					// correlate optional+computed config values within nested
  2710  					// sets.
  2711  					cty.ObjectVal(map[string]cty.Value{
  2712  						"opt": cty.StringVal("two"),
  2713  						"oc":  cty.StringVal("OK"),
  2714  						"attr": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{
  2715  							"opt": cty.StringVal("two"),
  2716  							"oc":  cty.NullVal(cty.String),
  2717  						})}),
  2718  					}),
  2719  					// This value was taken only from config
  2720  					cty.ObjectVal(map[string]cty.Value{
  2721  						"opt": cty.StringVal("three"),
  2722  						"oc":  cty.NullVal(cty.String),
  2723  						"attr": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  2724  							"opt": cty.String,
  2725  							"oc":  cty.String,
  2726  						}))),
  2727  					}),
  2728  				}),
  2729  			}),
  2730  		},
  2731  	}
  2732  
  2733  	for name, test := range tests {
  2734  		t.Run(name, func(t *testing.T) {
  2735  			got := ProposedNew(test.Schema, test.Prior, test.Config)
  2736  			if !got.RawEquals(test.Want) {
  2737  				t.Errorf("wrong result\ngot:  %swant: %s", dump.Value(got), dump.Value(test.Want))
  2738  			}
  2739  		})
  2740  	}
  2741  }
  2742  
  2743  var testAttributes = map[string]*configschema.Attribute{
  2744  	"optional": {
  2745  		Type:     cty.String,
  2746  		Optional: true,
  2747  	},
  2748  	"computed": {
  2749  		Type:     cty.String,
  2750  		Computed: true,
  2751  	},
  2752  	"optional_computed": {
  2753  		Type:     cty.String,
  2754  		Computed: true,
  2755  		Optional: true,
  2756  	},
  2757  	"required": {
  2758  		Type:     cty.String,
  2759  		Required: true,
  2760  	},
  2761  }