github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/plans/objchange/objchange_test.go (about)

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