github.com/opentofu/opentofu@v1.7.1/internal/plans/objchange/objchange_test.go (about)

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