github.com/opentofu/opentofu@v1.7.1/internal/legacy/helper/schema/shims_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 schema
     7  
     8  import (
     9  	"bytes"
    10  	"errors"
    11  	"fmt"
    12  	"reflect"
    13  	"strconv"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/google/go-cmp/cmp"
    18  	"github.com/google/go-cmp/cmp/cmpopts"
    19  	"github.com/opentofu/opentofu/internal/configs/configschema"
    20  	"github.com/opentofu/opentofu/internal/configs/hcl2shim"
    21  	"github.com/opentofu/opentofu/internal/legacy/helper/hashcode"
    22  	"github.com/opentofu/opentofu/internal/legacy/tofu"
    23  	"github.com/opentofu/opentofu/internal/providers"
    24  	"github.com/opentofu/opentofu/internal/tfdiags"
    25  	"github.com/zclconf/go-cty/cty"
    26  )
    27  
    28  var (
    29  	typeComparer  = cmp.Comparer(cty.Type.Equals)
    30  	valueComparer = cmp.Comparer(cty.Value.RawEquals)
    31  	equateEmpty   = cmpopts.EquateEmpty()
    32  )
    33  
    34  func testApplyDiff(t *testing.T,
    35  	resource *Resource,
    36  	state, expected *tofu.InstanceState,
    37  	diff *tofu.InstanceDiff) {
    38  
    39  	testSchema := providers.Schema{
    40  		Version: int64(resource.SchemaVersion),
    41  		Block:   resourceSchemaToBlock(resource.Schema),
    42  	}
    43  
    44  	stateVal, err := StateValueFromInstanceState(state, testSchema.Block.ImpliedType())
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  
    49  	newState, err := ApplyDiff(stateVal, diff, testSchema.Block)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  
    54  	// verify that "id" is correct
    55  	id := newState.AsValueMap()["id"]
    56  
    57  	switch {
    58  	case diff.Destroy || diff.DestroyDeposed || diff.DestroyTainted:
    59  		// there should be no id
    60  		if !id.IsNull() {
    61  			t.Fatalf("destroyed instance should have no id: %#v", id)
    62  		}
    63  	default:
    64  		// the "id" field always exists and is computed, so it must have a
    65  		// valid value or be unknown.
    66  		if id.IsNull() {
    67  			t.Fatal("new instance state cannot have a null id")
    68  		}
    69  
    70  		if id.IsKnown() && id.AsString() == "" {
    71  			t.Fatal("new instance id cannot be an empty string")
    72  		}
    73  	}
    74  
    75  	// Resource.Meta will be hanlded separately, so it's OK that we lose the
    76  	// timeout values here.
    77  	expectedState, err := StateValueFromInstanceState(expected, testSchema.Block.ImpliedType())
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	if !cmp.Equal(expectedState, newState, equateEmpty, typeComparer, valueComparer) {
    83  		t.Fatalf(cmp.Diff(expectedState, newState, equateEmpty, typeComparer, valueComparer))
    84  	}
    85  }
    86  
    87  func TestShimResourcePlan_destroyCreate(t *testing.T) {
    88  	r := &Resource{
    89  		SchemaVersion: 2,
    90  		Schema: map[string]*Schema{
    91  			"foo": &Schema{
    92  				Type:     TypeInt,
    93  				Optional: true,
    94  				ForceNew: true,
    95  			},
    96  		},
    97  	}
    98  
    99  	d := &tofu.InstanceDiff{
   100  		Attributes: map[string]*tofu.ResourceAttrDiff{
   101  			"foo": &tofu.ResourceAttrDiff{
   102  				RequiresNew: true,
   103  				Old:         "3",
   104  				New:         "42",
   105  			},
   106  		},
   107  	}
   108  
   109  	state := &tofu.InstanceState{
   110  		Attributes: map[string]string{"foo": "3"},
   111  	}
   112  
   113  	expected := &tofu.InstanceState{
   114  		ID: hcl2shim.UnknownVariableValue,
   115  		Attributes: map[string]string{
   116  			"id":  hcl2shim.UnknownVariableValue,
   117  			"foo": "42",
   118  		},
   119  		Meta: map[string]interface{}{
   120  			"schema_version": "2",
   121  		},
   122  	}
   123  
   124  	testApplyDiff(t, r, state, expected, d)
   125  }
   126  
   127  func TestShimResourceApply_create(t *testing.T) {
   128  	r := &Resource{
   129  		SchemaVersion: 2,
   130  		Schema: map[string]*Schema{
   131  			"foo": &Schema{
   132  				Type:     TypeInt,
   133  				Optional: true,
   134  			},
   135  		},
   136  	}
   137  
   138  	called := false
   139  	r.Create = func(d *ResourceData, m interface{}) error {
   140  		called = true
   141  		d.SetId("foo")
   142  		return nil
   143  	}
   144  
   145  	var s *tofu.InstanceState = nil
   146  
   147  	d := &tofu.InstanceDiff{
   148  		Attributes: map[string]*tofu.ResourceAttrDiff{
   149  			"foo": &tofu.ResourceAttrDiff{
   150  				New: "42",
   151  			},
   152  		},
   153  	}
   154  
   155  	actual, err := r.Apply(s, d, nil)
   156  	if err != nil {
   157  		t.Fatalf("err: %s", err)
   158  	}
   159  
   160  	if !called {
   161  		t.Fatal("not called")
   162  	}
   163  
   164  	expected := &tofu.InstanceState{
   165  		ID: "foo",
   166  		Attributes: map[string]string{
   167  			"id":  "foo",
   168  			"foo": "42",
   169  		},
   170  		Meta: map[string]interface{}{
   171  			"schema_version": "2",
   172  		},
   173  	}
   174  
   175  	if !reflect.DeepEqual(actual, expected) {
   176  		t.Fatalf("bad: %#v", actual)
   177  	}
   178  
   179  	// Shim
   180  	// now that we have our diff and desired state, see if we can reproduce
   181  	// that with the shim
   182  	// we're not testing Resource.Create, so we need to start with the "created" state
   183  	createdState := &tofu.InstanceState{
   184  		ID:         "foo",
   185  		Attributes: map[string]string{"id": "foo"},
   186  	}
   187  
   188  	testApplyDiff(t, r, createdState, expected, d)
   189  }
   190  
   191  func TestShimResourceApply_Timeout_state(t *testing.T) {
   192  	r := &Resource{
   193  		SchemaVersion: 2,
   194  		Schema: map[string]*Schema{
   195  			"foo": &Schema{
   196  				Type:     TypeInt,
   197  				Optional: true,
   198  			},
   199  		},
   200  		Timeouts: &ResourceTimeout{
   201  			Create: DefaultTimeout(40 * time.Minute),
   202  			Update: DefaultTimeout(80 * time.Minute),
   203  			Delete: DefaultTimeout(40 * time.Minute),
   204  		},
   205  	}
   206  
   207  	called := false
   208  	r.Create = func(d *ResourceData, m interface{}) error {
   209  		called = true
   210  		d.SetId("foo")
   211  		return nil
   212  	}
   213  
   214  	var s *tofu.InstanceState = nil
   215  
   216  	d := &tofu.InstanceDiff{
   217  		Attributes: map[string]*tofu.ResourceAttrDiff{
   218  			"foo": &tofu.ResourceAttrDiff{
   219  				New: "42",
   220  			},
   221  		},
   222  	}
   223  
   224  	diffTimeout := &ResourceTimeout{
   225  		Create: DefaultTimeout(40 * time.Minute),
   226  		Update: DefaultTimeout(80 * time.Minute),
   227  		Delete: DefaultTimeout(40 * time.Minute),
   228  	}
   229  
   230  	if err := diffTimeout.DiffEncode(d); err != nil {
   231  		t.Fatalf("Error encoding timeout to diff: %s", err)
   232  	}
   233  
   234  	actual, err := r.Apply(s, d, nil)
   235  	if err != nil {
   236  		t.Fatalf("err: %s", err)
   237  	}
   238  
   239  	if !called {
   240  		t.Fatal("not called")
   241  	}
   242  
   243  	expected := &tofu.InstanceState{
   244  		ID: "foo",
   245  		Attributes: map[string]string{
   246  			"id":  "foo",
   247  			"foo": "42",
   248  		},
   249  		Meta: map[string]interface{}{
   250  			"schema_version": "2",
   251  			TimeoutKey:       expectedForValues(40, 0, 80, 40, 0),
   252  		},
   253  	}
   254  
   255  	if !reflect.DeepEqual(actual, expected) {
   256  		t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta)
   257  	}
   258  
   259  	// Shim
   260  	// we're not testing Resource.Create, so we need to start with the "created" state
   261  	createdState := &tofu.InstanceState{
   262  		ID:         "foo",
   263  		Attributes: map[string]string{"id": "foo"},
   264  	}
   265  
   266  	testApplyDiff(t, r, createdState, expected, d)
   267  }
   268  
   269  func TestShimResourceDiff_Timeout_diff(t *testing.T) {
   270  	r := &Resource{
   271  		Schema: map[string]*Schema{
   272  			"foo": &Schema{
   273  				Type:     TypeInt,
   274  				Optional: true,
   275  			},
   276  		},
   277  		Timeouts: &ResourceTimeout{
   278  			Create: DefaultTimeout(40 * time.Minute),
   279  			Update: DefaultTimeout(80 * time.Minute),
   280  			Delete: DefaultTimeout(40 * time.Minute),
   281  		},
   282  	}
   283  
   284  	r.Create = func(d *ResourceData, m interface{}) error {
   285  		d.SetId("foo")
   286  		return nil
   287  	}
   288  
   289  	conf := tofu.NewResourceConfigRaw(map[string]interface{}{
   290  		"foo": 42,
   291  		TimeoutsConfigKey: map[string]interface{}{
   292  			"create": "2h",
   293  		},
   294  	})
   295  	var s *tofu.InstanceState
   296  
   297  	actual, err := r.Diff(s, conf, nil)
   298  	if err != nil {
   299  		t.Fatalf("err: %s", err)
   300  	}
   301  
   302  	expected := &tofu.InstanceDiff{
   303  		Attributes: map[string]*tofu.ResourceAttrDiff{
   304  			"foo": &tofu.ResourceAttrDiff{
   305  				New: "42",
   306  			},
   307  		},
   308  	}
   309  
   310  	diffTimeout := &ResourceTimeout{
   311  		Create: DefaultTimeout(120 * time.Minute),
   312  		Update: DefaultTimeout(80 * time.Minute),
   313  		Delete: DefaultTimeout(40 * time.Minute),
   314  	}
   315  
   316  	if err := diffTimeout.DiffEncode(expected); err != nil {
   317  		t.Fatalf("Error encoding timeout to diff: %s", err)
   318  	}
   319  
   320  	if !reflect.DeepEqual(actual, expected) {
   321  		t.Fatalf("Not equal in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta)
   322  	}
   323  
   324  	// Shim
   325  	// apply this diff, so we have a state to compare
   326  	applied, err := r.Apply(s, actual, nil)
   327  	if err != nil {
   328  		t.Fatal(err)
   329  	}
   330  
   331  	// we're not testing Resource.Create, so we need to start with the "created" state
   332  	createdState := &tofu.InstanceState{
   333  		ID:         "foo",
   334  		Attributes: map[string]string{"id": "foo"},
   335  	}
   336  
   337  	testSchema := providers.Schema{
   338  		Version: int64(r.SchemaVersion),
   339  		Block:   resourceSchemaToBlock(r.Schema),
   340  	}
   341  
   342  	initialVal, err := StateValueFromInstanceState(createdState, testSchema.Block.ImpliedType())
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  
   347  	appliedVal, err := StateValueFromInstanceState(applied, testSchema.Block.ImpliedType())
   348  	if err != nil {
   349  		t.Fatal(err)
   350  	}
   351  
   352  	d, err := DiffFromValues(initialVal, appliedVal, r)
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  	if eq, _ := d.Same(expected); !eq {
   357  		t.Fatal(cmp.Diff(d, expected))
   358  	}
   359  }
   360  
   361  func TestShimResourceApply_destroy(t *testing.T) {
   362  	r := &Resource{
   363  		Schema: map[string]*Schema{
   364  			"foo": &Schema{
   365  				Type:     TypeInt,
   366  				Optional: true,
   367  			},
   368  		},
   369  	}
   370  
   371  	called := false
   372  	r.Delete = func(d *ResourceData, m interface{}) error {
   373  		called = true
   374  		return nil
   375  	}
   376  
   377  	s := &tofu.InstanceState{
   378  		ID: "bar",
   379  	}
   380  
   381  	d := &tofu.InstanceDiff{
   382  		Destroy: true,
   383  	}
   384  
   385  	actual, err := r.Apply(s, d, nil)
   386  	if err != nil {
   387  		t.Fatalf("err: %s", err)
   388  	}
   389  
   390  	if !called {
   391  		t.Fatal("delete not called")
   392  	}
   393  
   394  	if actual != nil {
   395  		t.Fatalf("bad: %#v", actual)
   396  	}
   397  
   398  	// Shim
   399  	// now that we have our diff and desired state, see if we can reproduce
   400  	// that with the shim
   401  	testApplyDiff(t, r, s, actual, d)
   402  }
   403  
   404  func TestShimResourceApply_destroyCreate(t *testing.T) {
   405  	r := &Resource{
   406  		Schema: map[string]*Schema{
   407  			"foo": &Schema{
   408  				Type:     TypeInt,
   409  				Optional: true,
   410  				ForceNew: true,
   411  			},
   412  
   413  			"tags": &Schema{
   414  				Type:     TypeMap,
   415  				Optional: true,
   416  				Computed: true,
   417  			},
   418  		},
   419  	}
   420  
   421  	change := false
   422  	r.Create = func(d *ResourceData, m interface{}) error {
   423  		change = d.HasChange("tags")
   424  		d.SetId("foo")
   425  		return nil
   426  	}
   427  	r.Delete = func(d *ResourceData, m interface{}) error {
   428  		return nil
   429  	}
   430  
   431  	var s *tofu.InstanceState = &tofu.InstanceState{
   432  		ID: "bar",
   433  		Attributes: map[string]string{
   434  			"foo":       "7",
   435  			"tags.Name": "foo",
   436  		},
   437  	}
   438  
   439  	d := &tofu.InstanceDiff{
   440  		Attributes: map[string]*tofu.ResourceAttrDiff{
   441  			"id": &tofu.ResourceAttrDiff{
   442  				New: "foo",
   443  			},
   444  			"foo": &tofu.ResourceAttrDiff{
   445  				Old:         "7",
   446  				New:         "42",
   447  				RequiresNew: true,
   448  			},
   449  			"tags.Name": &tofu.ResourceAttrDiff{
   450  				Old:         "foo",
   451  				New:         "foo",
   452  				RequiresNew: true,
   453  			},
   454  		},
   455  	}
   456  
   457  	actual, err := r.Apply(s, d, nil)
   458  	if err != nil {
   459  		t.Fatalf("err: %s", err)
   460  	}
   461  
   462  	if !change {
   463  		t.Fatal("should have change")
   464  	}
   465  
   466  	expected := &tofu.InstanceState{
   467  		ID: "foo",
   468  		Attributes: map[string]string{
   469  			"id":        "foo",
   470  			"foo":       "42",
   471  			"tags.%":    "1",
   472  			"tags.Name": "foo",
   473  		},
   474  	}
   475  
   476  	if !reflect.DeepEqual(actual, expected) {
   477  		cmp.Diff(actual, expected)
   478  	}
   479  
   480  	// Shim
   481  	// now that we have our diff and desired state, see if we can reproduce
   482  	// that with the shim
   483  	// we're not testing Resource.Create, so we need to start with the "created" state
   484  	createdState := &tofu.InstanceState{
   485  		ID: "foo",
   486  		Attributes: map[string]string{
   487  			"id":        "foo",
   488  			"foo":       "7",
   489  			"tags.%":    "1",
   490  			"tags.Name": "foo",
   491  		},
   492  	}
   493  
   494  	testApplyDiff(t, r, createdState, expected, d)
   495  }
   496  
   497  func TestShimSchemaMap_Diff(t *testing.T) {
   498  	cases := []struct {
   499  		Name          string
   500  		Schema        map[string]*Schema
   501  		State         *tofu.InstanceState
   502  		Config        map[string]interface{}
   503  		CustomizeDiff CustomizeDiffFunc
   504  		Diff          *tofu.InstanceDiff
   505  		Err           bool
   506  	}{
   507  		{
   508  			Name: "diff-1",
   509  			Schema: map[string]*Schema{
   510  				"availability_zone": &Schema{
   511  					Type:     TypeString,
   512  					Optional: true,
   513  					Computed: true,
   514  					ForceNew: true,
   515  				},
   516  			},
   517  
   518  			State: nil,
   519  
   520  			Config: map[string]interface{}{
   521  				"availability_zone": "foo",
   522  			},
   523  
   524  			Diff: &tofu.InstanceDiff{
   525  				Attributes: map[string]*tofu.ResourceAttrDiff{
   526  					"availability_zone": &tofu.ResourceAttrDiff{
   527  						Old:         "",
   528  						New:         "foo",
   529  						RequiresNew: true,
   530  					},
   531  				},
   532  			},
   533  
   534  			Err: false,
   535  		},
   536  
   537  		{
   538  			Name: "diff-2",
   539  			Schema: map[string]*Schema{
   540  				"availability_zone": &Schema{
   541  					Type:     TypeString,
   542  					Optional: true,
   543  					Computed: true,
   544  					ForceNew: true,
   545  				},
   546  			},
   547  
   548  			State: nil,
   549  
   550  			Config: map[string]interface{}{},
   551  
   552  			Diff: &tofu.InstanceDiff{
   553  				Attributes: map[string]*tofu.ResourceAttrDiff{
   554  					"availability_zone": &tofu.ResourceAttrDiff{
   555  						Old:         "",
   556  						NewComputed: true,
   557  						RequiresNew: true,
   558  					},
   559  				},
   560  			},
   561  
   562  			Err: false,
   563  		},
   564  
   565  		{
   566  			Name: "diff-3",
   567  			Schema: map[string]*Schema{
   568  				"availability_zone": &Schema{
   569  					Type:     TypeString,
   570  					Optional: true,
   571  					Computed: true,
   572  					ForceNew: true,
   573  				},
   574  			},
   575  
   576  			State: &tofu.InstanceState{
   577  				ID: "foo",
   578  			},
   579  
   580  			Config: map[string]interface{}{},
   581  
   582  			Diff: nil,
   583  
   584  			Err: false,
   585  		},
   586  
   587  		{
   588  			Name: "Computed, but set in config",
   589  			Schema: map[string]*Schema{
   590  				"availability_zone": &Schema{
   591  					Type:     TypeString,
   592  					Optional: true,
   593  					Computed: true,
   594  				},
   595  			},
   596  
   597  			State: &tofu.InstanceState{
   598  				ID: "id",
   599  				Attributes: map[string]string{
   600  					"availability_zone": "foo",
   601  				},
   602  			},
   603  
   604  			Config: map[string]interface{}{
   605  				"availability_zone": "bar",
   606  			},
   607  
   608  			Diff: &tofu.InstanceDiff{
   609  				Attributes: map[string]*tofu.ResourceAttrDiff{
   610  					"availability_zone": &tofu.ResourceAttrDiff{
   611  						Old: "foo",
   612  						New: "bar",
   613  					},
   614  				},
   615  			},
   616  
   617  			Err: false,
   618  		},
   619  
   620  		{
   621  			Name: "Default",
   622  			Schema: map[string]*Schema{
   623  				"availability_zone": &Schema{
   624  					Type:     TypeString,
   625  					Optional: true,
   626  					Default:  "foo",
   627  				},
   628  			},
   629  
   630  			State: nil,
   631  
   632  			Config: nil,
   633  
   634  			Diff: &tofu.InstanceDiff{
   635  				Attributes: map[string]*tofu.ResourceAttrDiff{
   636  					"availability_zone": &tofu.ResourceAttrDiff{
   637  						Old: "",
   638  						New: "foo",
   639  					},
   640  				},
   641  			},
   642  
   643  			Err: false,
   644  		},
   645  
   646  		{
   647  			Name: "DefaultFunc, value",
   648  			Schema: map[string]*Schema{
   649  				"availability_zone": &Schema{
   650  					Type:     TypeString,
   651  					Optional: true,
   652  					DefaultFunc: func() (interface{}, error) {
   653  						return "foo", nil
   654  					},
   655  				},
   656  			},
   657  
   658  			State: nil,
   659  
   660  			Config: nil,
   661  
   662  			Diff: &tofu.InstanceDiff{
   663  				Attributes: map[string]*tofu.ResourceAttrDiff{
   664  					"availability_zone": &tofu.ResourceAttrDiff{
   665  						Old: "",
   666  						New: "foo",
   667  					},
   668  				},
   669  			},
   670  
   671  			Err: false,
   672  		},
   673  
   674  		{
   675  			Name: "DefaultFunc, configuration set",
   676  			Schema: map[string]*Schema{
   677  				"availability_zone": &Schema{
   678  					Type:     TypeString,
   679  					Optional: true,
   680  					DefaultFunc: func() (interface{}, error) {
   681  						return "foo", nil
   682  					},
   683  				},
   684  			},
   685  
   686  			State: nil,
   687  
   688  			Config: map[string]interface{}{
   689  				"availability_zone": "bar",
   690  			},
   691  
   692  			Diff: &tofu.InstanceDiff{
   693  				Attributes: map[string]*tofu.ResourceAttrDiff{
   694  					"availability_zone": &tofu.ResourceAttrDiff{
   695  						Old: "",
   696  						New: "bar",
   697  					},
   698  				},
   699  			},
   700  
   701  			Err: false,
   702  		},
   703  
   704  		{
   705  			Name: "String with StateFunc",
   706  			Schema: map[string]*Schema{
   707  				"availability_zone": &Schema{
   708  					Type:     TypeString,
   709  					Optional: true,
   710  					Computed: true,
   711  					StateFunc: func(a interface{}) string {
   712  						return a.(string) + "!"
   713  					},
   714  				},
   715  			},
   716  
   717  			State: nil,
   718  
   719  			Config: map[string]interface{}{
   720  				"availability_zone": "foo",
   721  			},
   722  
   723  			Diff: &tofu.InstanceDiff{
   724  				Attributes: map[string]*tofu.ResourceAttrDiff{
   725  					"availability_zone": &tofu.ResourceAttrDiff{
   726  						Old:      "",
   727  						New:      "foo!",
   728  						NewExtra: "foo",
   729  					},
   730  				},
   731  			},
   732  
   733  			Err: false,
   734  		},
   735  
   736  		{
   737  			Name: "StateFunc not called with nil value",
   738  			Schema: map[string]*Schema{
   739  				"availability_zone": &Schema{
   740  					Type:     TypeString,
   741  					Optional: true,
   742  					Computed: true,
   743  					StateFunc: func(a interface{}) string {
   744  						t.Error("should not get here!")
   745  						return ""
   746  					},
   747  				},
   748  			},
   749  
   750  			State: nil,
   751  
   752  			Config: map[string]interface{}{},
   753  
   754  			Diff: &tofu.InstanceDiff{
   755  				Attributes: map[string]*tofu.ResourceAttrDiff{
   756  					"availability_zone": &tofu.ResourceAttrDiff{
   757  						Old:         "",
   758  						New:         "",
   759  						NewComputed: true,
   760  					},
   761  				},
   762  			},
   763  
   764  			Err: false,
   765  		},
   766  
   767  		{
   768  			Name: "Variable computed",
   769  			Schema: map[string]*Schema{
   770  				"availability_zone": &Schema{
   771  					Type:     TypeString,
   772  					Optional: true,
   773  				},
   774  			},
   775  
   776  			State: nil,
   777  
   778  			Config: map[string]interface{}{
   779  				"availability_zone": hcl2shim.UnknownVariableValue,
   780  			},
   781  
   782  			Diff: &tofu.InstanceDiff{
   783  				Attributes: map[string]*tofu.ResourceAttrDiff{
   784  					"availability_zone": &tofu.ResourceAttrDiff{
   785  						Old:         "",
   786  						New:         hcl2shim.UnknownVariableValue,
   787  						NewComputed: true,
   788  					},
   789  				},
   790  			},
   791  
   792  			Err: false,
   793  		},
   794  
   795  		{
   796  			Name: "Int decode",
   797  			Schema: map[string]*Schema{
   798  				"port": &Schema{
   799  					Type:     TypeInt,
   800  					Optional: true,
   801  					Computed: true,
   802  					ForceNew: true,
   803  				},
   804  			},
   805  
   806  			State: nil,
   807  
   808  			Config: map[string]interface{}{
   809  				"port": 27,
   810  			},
   811  
   812  			Diff: &tofu.InstanceDiff{
   813  				Attributes: map[string]*tofu.ResourceAttrDiff{
   814  					"port": &tofu.ResourceAttrDiff{
   815  						Old:         "",
   816  						New:         "27",
   817  						RequiresNew: true,
   818  					},
   819  				},
   820  			},
   821  
   822  			Err: false,
   823  		},
   824  
   825  		{
   826  			Name: "bool decode",
   827  			Schema: map[string]*Schema{
   828  				"port": &Schema{
   829  					Type:     TypeBool,
   830  					Optional: true,
   831  					Computed: true,
   832  					ForceNew: true,
   833  				},
   834  			},
   835  
   836  			State: nil,
   837  
   838  			Config: map[string]interface{}{
   839  				"port": false,
   840  			},
   841  
   842  			Diff: &tofu.InstanceDiff{
   843  				Attributes: map[string]*tofu.ResourceAttrDiff{
   844  					"port": &tofu.ResourceAttrDiff{
   845  						Old:         "",
   846  						New:         "false",
   847  						RequiresNew: true,
   848  					},
   849  				},
   850  			},
   851  
   852  			Err: false,
   853  		},
   854  
   855  		{
   856  			Name: "Bool",
   857  			Schema: map[string]*Schema{
   858  				"delete": &Schema{
   859  					Type:     TypeBool,
   860  					Optional: true,
   861  					Default:  false,
   862  				},
   863  			},
   864  
   865  			State: &tofu.InstanceState{
   866  				ID: "id",
   867  				Attributes: map[string]string{
   868  					"delete": "false",
   869  				},
   870  			},
   871  
   872  			Config: nil,
   873  
   874  			Diff: nil,
   875  
   876  			Err: false,
   877  		},
   878  
   879  		{
   880  			Name: "List decode",
   881  			Schema: map[string]*Schema{
   882  				"ports": &Schema{
   883  					Type:     TypeList,
   884  					Required: true,
   885  					Elem:     &Schema{Type: TypeInt},
   886  				},
   887  			},
   888  
   889  			State: nil,
   890  
   891  			Config: map[string]interface{}{
   892  				"ports": []interface{}{1, 2, 5},
   893  			},
   894  
   895  			Diff: &tofu.InstanceDiff{
   896  				Attributes: map[string]*tofu.ResourceAttrDiff{
   897  					"ports.#": &tofu.ResourceAttrDiff{
   898  						Old: "0",
   899  						New: "3",
   900  					},
   901  					"ports.0": &tofu.ResourceAttrDiff{
   902  						Old: "",
   903  						New: "1",
   904  					},
   905  					"ports.1": &tofu.ResourceAttrDiff{
   906  						Old: "",
   907  						New: "2",
   908  					},
   909  					"ports.2": &tofu.ResourceAttrDiff{
   910  						Old: "",
   911  						New: "5",
   912  					},
   913  				},
   914  			},
   915  
   916  			Err: false,
   917  		},
   918  
   919  		{
   920  			Name: "List decode with promotion with list",
   921  			Schema: map[string]*Schema{
   922  				"ports": &Schema{
   923  					Type:          TypeList,
   924  					Required:      true,
   925  					Elem:          &Schema{Type: TypeInt},
   926  					PromoteSingle: true,
   927  				},
   928  			},
   929  
   930  			State: nil,
   931  
   932  			Config: map[string]interface{}{
   933  				"ports": []interface{}{"5"},
   934  			},
   935  
   936  			Diff: &tofu.InstanceDiff{
   937  				Attributes: map[string]*tofu.ResourceAttrDiff{
   938  					"ports.#": &tofu.ResourceAttrDiff{
   939  						Old: "0",
   940  						New: "1",
   941  					},
   942  					"ports.0": &tofu.ResourceAttrDiff{
   943  						Old: "",
   944  						New: "5",
   945  					},
   946  				},
   947  			},
   948  
   949  			Err: false,
   950  		},
   951  
   952  		{
   953  			Schema: map[string]*Schema{
   954  				"ports": &Schema{
   955  					Type:     TypeList,
   956  					Required: true,
   957  					Elem:     &Schema{Type: TypeInt},
   958  				},
   959  			},
   960  
   961  			State: nil,
   962  
   963  			Config: map[string]interface{}{
   964  				"ports": []interface{}{1, 2, 5},
   965  			},
   966  
   967  			Diff: &tofu.InstanceDiff{
   968  				Attributes: map[string]*tofu.ResourceAttrDiff{
   969  					"ports.#": &tofu.ResourceAttrDiff{
   970  						Old: "0",
   971  						New: "3",
   972  					},
   973  					"ports.0": &tofu.ResourceAttrDiff{
   974  						Old: "",
   975  						New: "1",
   976  					},
   977  					"ports.1": &tofu.ResourceAttrDiff{
   978  						Old: "",
   979  						New: "2",
   980  					},
   981  					"ports.2": &tofu.ResourceAttrDiff{
   982  						Old: "",
   983  						New: "5",
   984  					},
   985  				},
   986  			},
   987  
   988  			Err: false,
   989  		},
   990  
   991  		{
   992  			Schema: map[string]*Schema{
   993  				"ports": &Schema{
   994  					Type:     TypeList,
   995  					Required: true,
   996  					Elem:     &Schema{Type: TypeInt},
   997  				},
   998  			},
   999  
  1000  			State: nil,
  1001  
  1002  			Config: map[string]interface{}{
  1003  				"ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"},
  1004  			},
  1005  
  1006  			Diff: &tofu.InstanceDiff{
  1007  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1008  					"ports.#": &tofu.ResourceAttrDiff{
  1009  						Old:         "0",
  1010  						New:         "",
  1011  						NewComputed: true,
  1012  					},
  1013  				},
  1014  			},
  1015  
  1016  			Err: false,
  1017  		},
  1018  
  1019  		{
  1020  			Schema: map[string]*Schema{
  1021  				"ports": &Schema{
  1022  					Type:     TypeList,
  1023  					Required: true,
  1024  					Elem:     &Schema{Type: TypeInt},
  1025  				},
  1026  			},
  1027  
  1028  			State: &tofu.InstanceState{
  1029  				ID: "id",
  1030  				Attributes: map[string]string{
  1031  					"ports.#": "3",
  1032  					"ports.0": "1",
  1033  					"ports.1": "2",
  1034  					"ports.2": "5",
  1035  				},
  1036  			},
  1037  
  1038  			Config: map[string]interface{}{
  1039  				"ports": []interface{}{1, 2, 5},
  1040  			},
  1041  
  1042  			Diff: nil,
  1043  
  1044  			Err: false,
  1045  		},
  1046  
  1047  		{
  1048  			Name: "",
  1049  			Schema: map[string]*Schema{
  1050  				"ports": &Schema{
  1051  					Type:     TypeList,
  1052  					Required: true,
  1053  					Elem:     &Schema{Type: TypeInt},
  1054  				},
  1055  			},
  1056  
  1057  			State: &tofu.InstanceState{
  1058  				ID: "id",
  1059  				Attributes: map[string]string{
  1060  					"ports.#": "2",
  1061  					"ports.0": "1",
  1062  					"ports.1": "2",
  1063  				},
  1064  			},
  1065  
  1066  			Config: map[string]interface{}{
  1067  				"ports": []interface{}{1, 2, 5},
  1068  			},
  1069  
  1070  			Diff: &tofu.InstanceDiff{
  1071  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1072  					"ports.#": &tofu.ResourceAttrDiff{
  1073  						Old: "2",
  1074  						New: "3",
  1075  					},
  1076  					"ports.2": &tofu.ResourceAttrDiff{
  1077  						Old: "",
  1078  						New: "5",
  1079  					},
  1080  				},
  1081  			},
  1082  
  1083  			Err: false,
  1084  		},
  1085  
  1086  		{
  1087  			Name: "",
  1088  			Schema: map[string]*Schema{
  1089  				"ports": &Schema{
  1090  					Type:     TypeList,
  1091  					Required: true,
  1092  					Elem:     &Schema{Type: TypeInt},
  1093  					ForceNew: true,
  1094  				},
  1095  			},
  1096  
  1097  			State: nil,
  1098  
  1099  			Config: map[string]interface{}{
  1100  				"ports": []interface{}{1, 2, 5},
  1101  			},
  1102  
  1103  			Diff: &tofu.InstanceDiff{
  1104  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1105  					"ports.#": &tofu.ResourceAttrDiff{
  1106  						Old:         "0",
  1107  						New:         "3",
  1108  						RequiresNew: true,
  1109  					},
  1110  					"ports.0": &tofu.ResourceAttrDiff{
  1111  						Old:         "",
  1112  						New:         "1",
  1113  						RequiresNew: true,
  1114  					},
  1115  					"ports.1": &tofu.ResourceAttrDiff{
  1116  						Old:         "",
  1117  						New:         "2",
  1118  						RequiresNew: true,
  1119  					},
  1120  					"ports.2": &tofu.ResourceAttrDiff{
  1121  						Old:         "",
  1122  						New:         "5",
  1123  						RequiresNew: true,
  1124  					},
  1125  				},
  1126  			},
  1127  
  1128  			Err: false,
  1129  		},
  1130  
  1131  		{
  1132  			Name: "",
  1133  			Schema: map[string]*Schema{
  1134  				"ports": &Schema{
  1135  					Type:     TypeList,
  1136  					Optional: true,
  1137  					Computed: true,
  1138  					Elem:     &Schema{Type: TypeInt},
  1139  				},
  1140  			},
  1141  
  1142  			State: nil,
  1143  
  1144  			Config: map[string]interface{}{},
  1145  
  1146  			Diff: &tofu.InstanceDiff{
  1147  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1148  					"ports.#": &tofu.ResourceAttrDiff{
  1149  						Old:         "",
  1150  						NewComputed: true,
  1151  					},
  1152  				},
  1153  			},
  1154  
  1155  			Err: false,
  1156  		},
  1157  
  1158  		{
  1159  			Name: "List with computed set",
  1160  			Schema: map[string]*Schema{
  1161  				"config": &Schema{
  1162  					Type:     TypeList,
  1163  					Optional: true,
  1164  					ForceNew: true,
  1165  					MinItems: 1,
  1166  					Elem: &Resource{
  1167  						Schema: map[string]*Schema{
  1168  							"name": {
  1169  								Type:     TypeString,
  1170  								Required: true,
  1171  							},
  1172  
  1173  							"rules": {
  1174  								Type:     TypeSet,
  1175  								Computed: true,
  1176  								Elem:     &Schema{Type: TypeString},
  1177  								Set:      HashString,
  1178  							},
  1179  						},
  1180  					},
  1181  				},
  1182  			},
  1183  
  1184  			State: nil,
  1185  
  1186  			Config: map[string]interface{}{
  1187  				"config": []interface{}{
  1188  					map[string]interface{}{
  1189  						"name": "hello",
  1190  					},
  1191  				},
  1192  			},
  1193  
  1194  			Diff: &tofu.InstanceDiff{
  1195  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1196  					"config.#": &tofu.ResourceAttrDiff{
  1197  						Old:         "0",
  1198  						New:         "1",
  1199  						RequiresNew: true,
  1200  					},
  1201  
  1202  					"config.0.name": &tofu.ResourceAttrDiff{
  1203  						Old: "",
  1204  						New: "hello",
  1205  					},
  1206  
  1207  					"config.0.rules.#": &tofu.ResourceAttrDiff{
  1208  						Old:         "",
  1209  						NewComputed: true,
  1210  					},
  1211  				},
  1212  			},
  1213  
  1214  			Err: false,
  1215  		},
  1216  
  1217  		{
  1218  			Name: "Set-1",
  1219  			Schema: map[string]*Schema{
  1220  				"ports": &Schema{
  1221  					Type:     TypeSet,
  1222  					Required: true,
  1223  					Elem:     &Schema{Type: TypeInt},
  1224  					Set: func(a interface{}) int {
  1225  						return a.(int)
  1226  					},
  1227  				},
  1228  			},
  1229  
  1230  			State: nil,
  1231  
  1232  			Config: map[string]interface{}{
  1233  				"ports": []interface{}{5, 2, 1},
  1234  			},
  1235  
  1236  			Diff: &tofu.InstanceDiff{
  1237  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1238  					"ports.#": &tofu.ResourceAttrDiff{
  1239  						Old: "0",
  1240  						New: "3",
  1241  					},
  1242  					"ports.1": &tofu.ResourceAttrDiff{
  1243  						Old: "",
  1244  						New: "1",
  1245  					},
  1246  					"ports.2": &tofu.ResourceAttrDiff{
  1247  						Old: "",
  1248  						New: "2",
  1249  					},
  1250  					"ports.5": &tofu.ResourceAttrDiff{
  1251  						Old: "",
  1252  						New: "5",
  1253  					},
  1254  				},
  1255  			},
  1256  
  1257  			Err: false,
  1258  		},
  1259  
  1260  		{
  1261  			Name: "Set-2",
  1262  			Schema: map[string]*Schema{
  1263  				"ports": &Schema{
  1264  					Type:     TypeSet,
  1265  					Computed: true,
  1266  					Required: true,
  1267  					Elem:     &Schema{Type: TypeInt},
  1268  					Set: func(a interface{}) int {
  1269  						return a.(int)
  1270  					},
  1271  				},
  1272  			},
  1273  
  1274  			State: &tofu.InstanceState{
  1275  				ID: "id",
  1276  				Attributes: map[string]string{
  1277  					"ports.#": "0",
  1278  				},
  1279  			},
  1280  
  1281  			Config: nil,
  1282  
  1283  			Diff: nil,
  1284  
  1285  			Err: false,
  1286  		},
  1287  
  1288  		{
  1289  			Name: "Set-3",
  1290  			Schema: map[string]*Schema{
  1291  				"ports": &Schema{
  1292  					Type:     TypeSet,
  1293  					Optional: true,
  1294  					Computed: true,
  1295  					Elem:     &Schema{Type: TypeInt},
  1296  					Set: func(a interface{}) int {
  1297  						return a.(int)
  1298  					},
  1299  				},
  1300  			},
  1301  
  1302  			State: nil,
  1303  
  1304  			Config: nil,
  1305  
  1306  			Diff: &tofu.InstanceDiff{
  1307  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1308  					"ports.#": &tofu.ResourceAttrDiff{
  1309  						Old:         "",
  1310  						NewComputed: true,
  1311  					},
  1312  				},
  1313  			},
  1314  
  1315  			Err: false,
  1316  		},
  1317  
  1318  		{
  1319  			Name: "Set-4",
  1320  			Schema: map[string]*Schema{
  1321  				"ports": &Schema{
  1322  					Type:     TypeSet,
  1323  					Required: true,
  1324  					Elem:     &Schema{Type: TypeInt},
  1325  					Set: func(a interface{}) int {
  1326  						return a.(int)
  1327  					},
  1328  				},
  1329  			},
  1330  
  1331  			State: nil,
  1332  
  1333  			Config: map[string]interface{}{
  1334  				"ports": []interface{}{"2", "5", 1},
  1335  			},
  1336  
  1337  			Diff: &tofu.InstanceDiff{
  1338  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1339  					"ports.#": &tofu.ResourceAttrDiff{
  1340  						Old: "0",
  1341  						New: "3",
  1342  					},
  1343  					"ports.1": &tofu.ResourceAttrDiff{
  1344  						Old: "",
  1345  						New: "1",
  1346  					},
  1347  					"ports.2": &tofu.ResourceAttrDiff{
  1348  						Old: "",
  1349  						New: "2",
  1350  					},
  1351  					"ports.5": &tofu.ResourceAttrDiff{
  1352  						Old: "",
  1353  						New: "5",
  1354  					},
  1355  				},
  1356  			},
  1357  
  1358  			Err: false,
  1359  		},
  1360  
  1361  		{
  1362  			Name: "Set-5",
  1363  			Schema: map[string]*Schema{
  1364  				"ports": &Schema{
  1365  					Type:     TypeSet,
  1366  					Required: true,
  1367  					Elem:     &Schema{Type: TypeInt},
  1368  					Set: func(a interface{}) int {
  1369  						return a.(int)
  1370  					},
  1371  				},
  1372  			},
  1373  
  1374  			State: nil,
  1375  
  1376  			Config: map[string]interface{}{
  1377  				"ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5},
  1378  			},
  1379  
  1380  			Diff: &tofu.InstanceDiff{
  1381  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1382  					"ports.#": &tofu.ResourceAttrDiff{
  1383  						Old:         "",
  1384  						New:         "",
  1385  						NewComputed: true,
  1386  					},
  1387  				},
  1388  			},
  1389  
  1390  			Err: false,
  1391  		},
  1392  
  1393  		{
  1394  			Name: "Set-6",
  1395  			Schema: map[string]*Schema{
  1396  				"ports": &Schema{
  1397  					Type:     TypeSet,
  1398  					Required: true,
  1399  					Elem:     &Schema{Type: TypeInt},
  1400  					Set: func(a interface{}) int {
  1401  						return a.(int)
  1402  					},
  1403  				},
  1404  			},
  1405  
  1406  			State: &tofu.InstanceState{
  1407  				ID: "id",
  1408  				Attributes: map[string]string{
  1409  					"ports.#": "2",
  1410  					"ports.1": "1",
  1411  					"ports.2": "2",
  1412  				},
  1413  			},
  1414  
  1415  			Config: map[string]interface{}{
  1416  				"ports": []interface{}{5, 2, 1},
  1417  			},
  1418  
  1419  			Diff: &tofu.InstanceDiff{
  1420  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1421  					"ports.#": &tofu.ResourceAttrDiff{
  1422  						Old: "2",
  1423  						New: "3",
  1424  					},
  1425  					"ports.1": &tofu.ResourceAttrDiff{
  1426  						Old: "1",
  1427  						New: "1",
  1428  					},
  1429  					"ports.2": &tofu.ResourceAttrDiff{
  1430  						Old: "2",
  1431  						New: "2",
  1432  					},
  1433  					"ports.5": &tofu.ResourceAttrDiff{
  1434  						Old: "",
  1435  						New: "5",
  1436  					},
  1437  				},
  1438  			},
  1439  
  1440  			Err: false,
  1441  		},
  1442  
  1443  		{
  1444  			Name: "Set-8",
  1445  			Schema: map[string]*Schema{
  1446  				"ports": &Schema{
  1447  					Type:     TypeSet,
  1448  					Optional: true,
  1449  					Computed: true,
  1450  					Elem:     &Schema{Type: TypeInt},
  1451  					Set: func(a interface{}) int {
  1452  						return a.(int)
  1453  					},
  1454  				},
  1455  			},
  1456  
  1457  			State: &tofu.InstanceState{
  1458  				ID: "id",
  1459  				Attributes: map[string]string{
  1460  					"availability_zone": "bar",
  1461  					"ports.#":           "1",
  1462  					"ports.80":          "80",
  1463  				},
  1464  			},
  1465  
  1466  			Config: map[string]interface{}{},
  1467  
  1468  			Diff: nil,
  1469  
  1470  			Err: false,
  1471  		},
  1472  
  1473  		{
  1474  			Name: "Set-9",
  1475  			Schema: map[string]*Schema{
  1476  				"ingress": &Schema{
  1477  					Type:     TypeSet,
  1478  					Required: true,
  1479  					Elem: &Resource{
  1480  						Schema: map[string]*Schema{
  1481  							"ports": &Schema{
  1482  								Type:     TypeList,
  1483  								Optional: true,
  1484  								Elem:     &Schema{Type: TypeInt},
  1485  							},
  1486  						},
  1487  					},
  1488  					Set: func(v interface{}) int {
  1489  						m := v.(map[string]interface{})
  1490  						ps := m["ports"].([]interface{})
  1491  						result := 0
  1492  						for _, p := range ps {
  1493  							result += p.(int)
  1494  						}
  1495  						return result
  1496  					},
  1497  				},
  1498  			},
  1499  
  1500  			State: &tofu.InstanceState{
  1501  				ID: "id",
  1502  				Attributes: map[string]string{
  1503  					"ingress.#":           "2",
  1504  					"ingress.80.ports.#":  "1",
  1505  					"ingress.80.ports.0":  "80",
  1506  					"ingress.443.ports.#": "1",
  1507  					"ingress.443.ports.0": "443",
  1508  				},
  1509  			},
  1510  
  1511  			Config: map[string]interface{}{
  1512  				"ingress": []interface{}{
  1513  					map[string]interface{}{
  1514  						"ports": []interface{}{443},
  1515  					},
  1516  					map[string]interface{}{
  1517  						"ports": []interface{}{80},
  1518  					},
  1519  				},
  1520  			},
  1521  
  1522  			Diff: nil,
  1523  
  1524  			Err: false,
  1525  		},
  1526  
  1527  		{
  1528  			Name: "List of structure decode",
  1529  			Schema: map[string]*Schema{
  1530  				"ingress": &Schema{
  1531  					Type:     TypeList,
  1532  					Required: true,
  1533  					Elem: &Resource{
  1534  						Schema: map[string]*Schema{
  1535  							"from": &Schema{
  1536  								Type:     TypeInt,
  1537  								Required: true,
  1538  							},
  1539  						},
  1540  					},
  1541  				},
  1542  			},
  1543  
  1544  			State: nil,
  1545  
  1546  			Config: map[string]interface{}{
  1547  				"ingress": []interface{}{
  1548  					map[string]interface{}{
  1549  						"from": 8080,
  1550  					},
  1551  				},
  1552  			},
  1553  
  1554  			Diff: &tofu.InstanceDiff{
  1555  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1556  					"ingress.#": &tofu.ResourceAttrDiff{
  1557  						Old: "0",
  1558  						New: "1",
  1559  					},
  1560  					"ingress.0.from": &tofu.ResourceAttrDiff{
  1561  						Old: "",
  1562  						New: "8080",
  1563  					},
  1564  				},
  1565  			},
  1566  
  1567  			Err: false,
  1568  		},
  1569  
  1570  		{
  1571  			Name: "ComputedWhen",
  1572  			Schema: map[string]*Schema{
  1573  				"availability_zone": &Schema{
  1574  					Type:         TypeString,
  1575  					Computed:     true,
  1576  					ComputedWhen: []string{"port"},
  1577  				},
  1578  
  1579  				"port": &Schema{
  1580  					Type:     TypeInt,
  1581  					Optional: true,
  1582  				},
  1583  			},
  1584  
  1585  			State: &tofu.InstanceState{
  1586  				ID: "id",
  1587  				Attributes: map[string]string{
  1588  					"availability_zone": "foo",
  1589  					"port":              "80",
  1590  				},
  1591  			},
  1592  
  1593  			Config: map[string]interface{}{
  1594  				"port": 80,
  1595  			},
  1596  
  1597  			Diff: nil,
  1598  
  1599  			Err: false,
  1600  		},
  1601  
  1602  		{
  1603  			Name: "computed",
  1604  			Schema: map[string]*Schema{
  1605  				"availability_zone": &Schema{
  1606  					Type:         TypeString,
  1607  					Computed:     true,
  1608  					ComputedWhen: []string{"port"},
  1609  				},
  1610  
  1611  				"port": &Schema{
  1612  					Type:     TypeInt,
  1613  					Optional: true,
  1614  				},
  1615  			},
  1616  
  1617  			State: nil,
  1618  
  1619  			Config: map[string]interface{}{
  1620  				"port": 80,
  1621  			},
  1622  
  1623  			Diff: &tofu.InstanceDiff{
  1624  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1625  					"availability_zone": &tofu.ResourceAttrDiff{
  1626  						NewComputed: true,
  1627  					},
  1628  					"port": &tofu.ResourceAttrDiff{
  1629  						New: "80",
  1630  					},
  1631  				},
  1632  			},
  1633  
  1634  			Err: false,
  1635  		},
  1636  
  1637  		{
  1638  			Name: "computed, exists",
  1639  			Schema: map[string]*Schema{
  1640  				"availability_zone": &Schema{
  1641  					Type:         TypeString,
  1642  					Computed:     true,
  1643  					ComputedWhen: []string{"port"},
  1644  				},
  1645  
  1646  				"port": &Schema{
  1647  					Type:     TypeInt,
  1648  					Optional: true,
  1649  				},
  1650  			},
  1651  
  1652  			State: &tofu.InstanceState{
  1653  				ID: "id",
  1654  				Attributes: map[string]string{
  1655  					"port": "80",
  1656  				},
  1657  			},
  1658  
  1659  			Config: map[string]interface{}{
  1660  				"port": 80,
  1661  			},
  1662  
  1663  			// there is no computed diff when the instance exists already
  1664  			Diff: nil,
  1665  
  1666  			Err: false,
  1667  		},
  1668  
  1669  		{
  1670  			Name: "Maps-1",
  1671  			Schema: map[string]*Schema{
  1672  				"config_vars": &Schema{
  1673  					Type: TypeMap,
  1674  				},
  1675  			},
  1676  
  1677  			State: nil,
  1678  
  1679  			Config: map[string]interface{}{
  1680  				"config_vars": map[string]interface{}{
  1681  					"bar": "baz",
  1682  				},
  1683  			},
  1684  
  1685  			Diff: &tofu.InstanceDiff{
  1686  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1687  					"config_vars.%": &tofu.ResourceAttrDiff{
  1688  						Old: "0",
  1689  						New: "1",
  1690  					},
  1691  
  1692  					"config_vars.bar": &tofu.ResourceAttrDiff{
  1693  						Old: "",
  1694  						New: "baz",
  1695  					},
  1696  				},
  1697  			},
  1698  
  1699  			Err: false,
  1700  		},
  1701  
  1702  		{
  1703  			Name: "Maps-2",
  1704  			Schema: map[string]*Schema{
  1705  				"config_vars": &Schema{
  1706  					Type: TypeMap,
  1707  				},
  1708  			},
  1709  
  1710  			State: &tofu.InstanceState{
  1711  				ID: "id",
  1712  				Attributes: map[string]string{
  1713  					"config_vars.%":   "1",
  1714  					"config_vars.foo": "bar",
  1715  				},
  1716  			},
  1717  
  1718  			Config: map[string]interface{}{
  1719  				"config_vars": map[string]interface{}{
  1720  					"bar": "baz",
  1721  				},
  1722  			},
  1723  
  1724  			Diff: &tofu.InstanceDiff{
  1725  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1726  					"config_vars.foo": &tofu.ResourceAttrDiff{
  1727  						Old:        "bar",
  1728  						NewRemoved: true,
  1729  					},
  1730  					"config_vars.bar": &tofu.ResourceAttrDiff{
  1731  						Old: "",
  1732  						New: "baz",
  1733  					},
  1734  				},
  1735  			},
  1736  
  1737  			Err: false,
  1738  		},
  1739  
  1740  		{
  1741  			Name: "Maps-3",
  1742  			Schema: map[string]*Schema{
  1743  				"vars": &Schema{
  1744  					Type:     TypeMap,
  1745  					Optional: true,
  1746  					Computed: true,
  1747  				},
  1748  			},
  1749  
  1750  			State: &tofu.InstanceState{
  1751  				ID: "id",
  1752  				Attributes: map[string]string{
  1753  					"vars.%":   "1",
  1754  					"vars.foo": "bar",
  1755  				},
  1756  			},
  1757  
  1758  			Config: map[string]interface{}{
  1759  				"vars": map[string]interface{}{
  1760  					"bar": "baz",
  1761  				},
  1762  			},
  1763  
  1764  			Diff: &tofu.InstanceDiff{
  1765  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1766  					"vars.foo": &tofu.ResourceAttrDiff{
  1767  						Old:        "bar",
  1768  						New:        "",
  1769  						NewRemoved: true,
  1770  					},
  1771  					"vars.bar": &tofu.ResourceAttrDiff{
  1772  						Old: "",
  1773  						New: "baz",
  1774  					},
  1775  				},
  1776  			},
  1777  
  1778  			Err: false,
  1779  		},
  1780  
  1781  		{
  1782  			Name: "Maps-4",
  1783  			Schema: map[string]*Schema{
  1784  				"vars": &Schema{
  1785  					Type:     TypeMap,
  1786  					Computed: true,
  1787  				},
  1788  			},
  1789  
  1790  			State: &tofu.InstanceState{
  1791  				ID: "id",
  1792  				Attributes: map[string]string{
  1793  					"vars.%":   "1",
  1794  					"vars.foo": "bar",
  1795  				},
  1796  			},
  1797  
  1798  			Config: nil,
  1799  
  1800  			Diff: nil,
  1801  
  1802  			Err: false,
  1803  		},
  1804  
  1805  		{
  1806  			Name: "Maps-5",
  1807  			Schema: map[string]*Schema{
  1808  				"config_vars": &Schema{
  1809  					Type: TypeList,
  1810  					Elem: &Schema{Type: TypeMap},
  1811  				},
  1812  			},
  1813  
  1814  			State: &tofu.InstanceState{
  1815  				ID: "id",
  1816  				Attributes: map[string]string{
  1817  					"config_vars.#":     "1",
  1818  					"config_vars.0.%":   "1",
  1819  					"config_vars.0.foo": "bar",
  1820  				},
  1821  			},
  1822  
  1823  			Config: map[string]interface{}{
  1824  				"config_vars": []interface{}{
  1825  					map[string]interface{}{
  1826  						"bar": "baz",
  1827  					},
  1828  				},
  1829  			},
  1830  
  1831  			Diff: &tofu.InstanceDiff{
  1832  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1833  					"config_vars.0.foo": &tofu.ResourceAttrDiff{
  1834  						Old:        "bar",
  1835  						NewRemoved: true,
  1836  					},
  1837  					"config_vars.0.bar": &tofu.ResourceAttrDiff{
  1838  						Old: "",
  1839  						New: "baz",
  1840  					},
  1841  				},
  1842  			},
  1843  
  1844  			Err: false,
  1845  		},
  1846  
  1847  		{
  1848  			Name: "Maps-6",
  1849  			Schema: map[string]*Schema{
  1850  				"config_vars": &Schema{
  1851  					Type:     TypeList,
  1852  					Elem:     &Schema{Type: TypeMap},
  1853  					Optional: true,
  1854  				},
  1855  			},
  1856  
  1857  			State: &tofu.InstanceState{
  1858  				ID: "id",
  1859  				Attributes: map[string]string{
  1860  					"config_vars.#":     "1",
  1861  					"config_vars.0.%":   "2",
  1862  					"config_vars.0.foo": "bar",
  1863  					"config_vars.0.bar": "baz",
  1864  				},
  1865  			},
  1866  
  1867  			Config: map[string]interface{}{},
  1868  
  1869  			Diff: &tofu.InstanceDiff{
  1870  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1871  					"config_vars.#": &tofu.ResourceAttrDiff{
  1872  						Old: "1",
  1873  						New: "0",
  1874  					},
  1875  					"config_vars.0.%": &tofu.ResourceAttrDiff{
  1876  						Old: "2",
  1877  						New: "0",
  1878  					},
  1879  					"config_vars.0.foo": &tofu.ResourceAttrDiff{
  1880  						Old:        "bar",
  1881  						NewRemoved: true,
  1882  					},
  1883  					"config_vars.0.bar": &tofu.ResourceAttrDiff{
  1884  						Old:        "baz",
  1885  						NewRemoved: true,
  1886  					},
  1887  				},
  1888  			},
  1889  
  1890  			Err: false,
  1891  		},
  1892  
  1893  		{
  1894  			Name: "ForceNews",
  1895  			Schema: map[string]*Schema{
  1896  				"availability_zone": &Schema{
  1897  					Type:     TypeString,
  1898  					Optional: true,
  1899  					ForceNew: true,
  1900  				},
  1901  
  1902  				"address": &Schema{
  1903  					Type:     TypeString,
  1904  					Optional: true,
  1905  					Computed: true,
  1906  				},
  1907  			},
  1908  
  1909  			State: &tofu.InstanceState{
  1910  				ID: "id",
  1911  				Attributes: map[string]string{
  1912  					"availability_zone": "bar",
  1913  					"address":           "foo",
  1914  				},
  1915  			},
  1916  
  1917  			Config: map[string]interface{}{
  1918  				"availability_zone": "foo",
  1919  			},
  1920  
  1921  			Diff: &tofu.InstanceDiff{
  1922  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1923  					"availability_zone": &tofu.ResourceAttrDiff{
  1924  						Old:         "bar",
  1925  						New:         "foo",
  1926  						RequiresNew: true,
  1927  					},
  1928  				},
  1929  			},
  1930  
  1931  			Err: false,
  1932  		},
  1933  
  1934  		{
  1935  			Name: "Set-10",
  1936  			Schema: map[string]*Schema{
  1937  				"availability_zone": &Schema{
  1938  					Type:     TypeString,
  1939  					Optional: true,
  1940  					ForceNew: true,
  1941  				},
  1942  
  1943  				"ports": &Schema{
  1944  					Type:     TypeSet,
  1945  					Optional: true,
  1946  					Computed: true,
  1947  					Elem:     &Schema{Type: TypeInt},
  1948  					Set: func(a interface{}) int {
  1949  						return a.(int)
  1950  					},
  1951  				},
  1952  			},
  1953  
  1954  			State: &tofu.InstanceState{
  1955  				ID: "id",
  1956  				Attributes: map[string]string{
  1957  					"availability_zone": "bar",
  1958  					"ports.#":           "1",
  1959  					"ports.80":          "80",
  1960  				},
  1961  			},
  1962  
  1963  			Config: map[string]interface{}{
  1964  				"availability_zone": "foo",
  1965  			},
  1966  
  1967  			Diff: &tofu.InstanceDiff{
  1968  				Attributes: map[string]*tofu.ResourceAttrDiff{
  1969  					"availability_zone": &tofu.ResourceAttrDiff{
  1970  						Old:         "bar",
  1971  						New:         "foo",
  1972  						RequiresNew: true,
  1973  					},
  1974  				},
  1975  			},
  1976  
  1977  			Err: false,
  1978  		},
  1979  
  1980  		{
  1981  			Name: "Set-11",
  1982  			Schema: map[string]*Schema{
  1983  				"instances": &Schema{
  1984  					Type:     TypeSet,
  1985  					Elem:     &Schema{Type: TypeString},
  1986  					Optional: true,
  1987  					Computed: true,
  1988  					Set: func(v interface{}) int {
  1989  						return len(v.(string))
  1990  					},
  1991  				},
  1992  			},
  1993  
  1994  			State: &tofu.InstanceState{
  1995  				ID: "id",
  1996  				Attributes: map[string]string{
  1997  					"instances.#": "0",
  1998  				},
  1999  			},
  2000  
  2001  			Config: map[string]interface{}{
  2002  				"instances": []interface{}{hcl2shim.UnknownVariableValue},
  2003  			},
  2004  
  2005  			Diff: &tofu.InstanceDiff{
  2006  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2007  					"instances.#": &tofu.ResourceAttrDiff{
  2008  						NewComputed: true,
  2009  					},
  2010  				},
  2011  			},
  2012  
  2013  			Err: false,
  2014  		},
  2015  
  2016  		{
  2017  			Name: "Set-12",
  2018  			Schema: map[string]*Schema{
  2019  				"route": &Schema{
  2020  					Type:     TypeSet,
  2021  					Optional: true,
  2022  					Elem: &Resource{
  2023  						Schema: map[string]*Schema{
  2024  							"index": &Schema{
  2025  								Type:     TypeInt,
  2026  								Required: true,
  2027  							},
  2028  
  2029  							"gateway": &Schema{
  2030  								Type:     TypeString,
  2031  								Optional: true,
  2032  							},
  2033  						},
  2034  					},
  2035  					Set: func(v interface{}) int {
  2036  						m := v.(map[string]interface{})
  2037  						return m["index"].(int)
  2038  					},
  2039  				},
  2040  			},
  2041  
  2042  			State: nil,
  2043  
  2044  			Config: map[string]interface{}{
  2045  				"route": []interface{}{
  2046  					map[string]interface{}{
  2047  						"index":   "1",
  2048  						"gateway": hcl2shim.UnknownVariableValue,
  2049  					},
  2050  				},
  2051  			},
  2052  
  2053  			Diff: &tofu.InstanceDiff{
  2054  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2055  					"route.#": &tofu.ResourceAttrDiff{
  2056  						Old: "0",
  2057  						New: "1",
  2058  					},
  2059  					"route.~1.index": &tofu.ResourceAttrDiff{
  2060  						Old: "",
  2061  						New: "1",
  2062  					},
  2063  					"route.~1.gateway": &tofu.ResourceAttrDiff{
  2064  						Old:         "",
  2065  						New:         hcl2shim.UnknownVariableValue,
  2066  						NewComputed: true,
  2067  					},
  2068  				},
  2069  			},
  2070  
  2071  			Err: false,
  2072  		},
  2073  
  2074  		{
  2075  			Name: "Set-13",
  2076  			Schema: map[string]*Schema{
  2077  				"route": &Schema{
  2078  					Type:     TypeSet,
  2079  					Optional: true,
  2080  					Elem: &Resource{
  2081  						Schema: map[string]*Schema{
  2082  							"index": &Schema{
  2083  								Type:     TypeInt,
  2084  								Required: true,
  2085  							},
  2086  
  2087  							"gateway": &Schema{
  2088  								Type:     TypeSet,
  2089  								Optional: true,
  2090  								Elem:     &Schema{Type: TypeInt},
  2091  								Set: func(a interface{}) int {
  2092  									return a.(int)
  2093  								},
  2094  							},
  2095  						},
  2096  					},
  2097  					Set: func(v interface{}) int {
  2098  						m := v.(map[string]interface{})
  2099  						return m["index"].(int)
  2100  					},
  2101  				},
  2102  			},
  2103  
  2104  			State: nil,
  2105  
  2106  			Config: map[string]interface{}{
  2107  				"route": []interface{}{
  2108  					map[string]interface{}{
  2109  						"index": "1",
  2110  						"gateway": []interface{}{
  2111  							hcl2shim.UnknownVariableValue,
  2112  						},
  2113  					},
  2114  				},
  2115  			},
  2116  
  2117  			Diff: &tofu.InstanceDiff{
  2118  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2119  					"route.#": &tofu.ResourceAttrDiff{
  2120  						Old: "0",
  2121  						New: "1",
  2122  					},
  2123  					"route.~1.index": &tofu.ResourceAttrDiff{
  2124  						Old: "",
  2125  						New: "1",
  2126  					},
  2127  					"route.~1.gateway.#": &tofu.ResourceAttrDiff{
  2128  						NewComputed: true,
  2129  					},
  2130  				},
  2131  			},
  2132  
  2133  			Err: false,
  2134  		},
  2135  
  2136  		{
  2137  			Name: "Computed maps",
  2138  			Schema: map[string]*Schema{
  2139  				"vars": &Schema{
  2140  					Type:     TypeMap,
  2141  					Computed: true,
  2142  				},
  2143  			},
  2144  
  2145  			State: nil,
  2146  
  2147  			Config: nil,
  2148  
  2149  			Diff: &tofu.InstanceDiff{
  2150  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2151  					"vars.%": &tofu.ResourceAttrDiff{
  2152  						Old:         "",
  2153  						NewComputed: true,
  2154  					},
  2155  				},
  2156  			},
  2157  
  2158  			Err: false,
  2159  		},
  2160  
  2161  		{
  2162  			Name: "Computed maps",
  2163  			Schema: map[string]*Schema{
  2164  				"vars": &Schema{
  2165  					Type:     TypeMap,
  2166  					Computed: true,
  2167  				},
  2168  			},
  2169  
  2170  			State: &tofu.InstanceState{
  2171  				ID: "id",
  2172  				Attributes: map[string]string{
  2173  					"vars.%": "0",
  2174  				},
  2175  			},
  2176  
  2177  			Config: map[string]interface{}{
  2178  				"vars": map[string]interface{}{
  2179  					"bar": hcl2shim.UnknownVariableValue,
  2180  				},
  2181  			},
  2182  
  2183  			Diff: &tofu.InstanceDiff{
  2184  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2185  					"vars.%": &tofu.ResourceAttrDiff{
  2186  						Old:         "",
  2187  						NewComputed: true,
  2188  					},
  2189  				},
  2190  			},
  2191  
  2192  			Err: false,
  2193  		},
  2194  
  2195  		{
  2196  			Name:   "Empty",
  2197  			Schema: map[string]*Schema{},
  2198  
  2199  			State: &tofu.InstanceState{},
  2200  
  2201  			Config: map[string]interface{}{},
  2202  
  2203  			Diff: nil,
  2204  
  2205  			Err: false,
  2206  		},
  2207  
  2208  		{
  2209  			Name: "Float",
  2210  			Schema: map[string]*Schema{
  2211  				"some_threshold": &Schema{
  2212  					Type: TypeFloat,
  2213  				},
  2214  			},
  2215  
  2216  			State: &tofu.InstanceState{
  2217  				ID: "id",
  2218  				Attributes: map[string]string{
  2219  					"some_threshold": "567.8",
  2220  				},
  2221  			},
  2222  
  2223  			Config: map[string]interface{}{
  2224  				"some_threshold": 12.34,
  2225  			},
  2226  
  2227  			Diff: &tofu.InstanceDiff{
  2228  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2229  					"some_threshold": &tofu.ResourceAttrDiff{
  2230  						Old: "567.8",
  2231  						New: "12.34",
  2232  					},
  2233  				},
  2234  			},
  2235  
  2236  			Err: false,
  2237  		},
  2238  
  2239  		{
  2240  			Name: "https://github.com/hashicorp/terraform/issues/824",
  2241  			Schema: map[string]*Schema{
  2242  				"block_device": &Schema{
  2243  					Type:     TypeSet,
  2244  					Optional: true,
  2245  					Computed: true,
  2246  					Elem: &Resource{
  2247  						Schema: map[string]*Schema{
  2248  							"device_name": &Schema{
  2249  								Type:     TypeString,
  2250  								Required: true,
  2251  							},
  2252  							"delete_on_termination": &Schema{
  2253  								Type:     TypeBool,
  2254  								Optional: true,
  2255  								Default:  true,
  2256  							},
  2257  						},
  2258  					},
  2259  					Set: func(v interface{}) int {
  2260  						var buf bytes.Buffer
  2261  						m := v.(map[string]interface{})
  2262  						buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
  2263  						buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool)))
  2264  						return hashcode.String(buf.String())
  2265  					},
  2266  				},
  2267  			},
  2268  
  2269  			State: &tofu.InstanceState{
  2270  				ID: "id",
  2271  				Attributes: map[string]string{
  2272  					"block_device.#": "2",
  2273  					"block_device.616397234.delete_on_termination":  "true",
  2274  					"block_device.616397234.device_name":            "/dev/sda1",
  2275  					"block_device.2801811477.delete_on_termination": "true",
  2276  					"block_device.2801811477.device_name":           "/dev/sdx",
  2277  				},
  2278  			},
  2279  
  2280  			Config: map[string]interface{}{
  2281  				"block_device": []interface{}{
  2282  					map[string]interface{}{
  2283  						"device_name": "/dev/sda1",
  2284  					},
  2285  					map[string]interface{}{
  2286  						"device_name": "/dev/sdx",
  2287  					},
  2288  				},
  2289  			},
  2290  			Diff: nil,
  2291  			Err:  false,
  2292  		},
  2293  
  2294  		{
  2295  			Name: "Zero value in state shouldn't result in diff",
  2296  			Schema: map[string]*Schema{
  2297  				"port": &Schema{
  2298  					Type:     TypeBool,
  2299  					Optional: true,
  2300  					ForceNew: true,
  2301  				},
  2302  			},
  2303  
  2304  			State: &tofu.InstanceState{
  2305  				ID: "id",
  2306  				Attributes: map[string]string{
  2307  					"port": "false",
  2308  				},
  2309  			},
  2310  
  2311  			Config: map[string]interface{}{},
  2312  
  2313  			Diff: nil,
  2314  
  2315  			Err: false,
  2316  		},
  2317  
  2318  		{
  2319  			Name: "Same as prev, but for sets",
  2320  			Schema: map[string]*Schema{
  2321  				"route": &Schema{
  2322  					Type:     TypeSet,
  2323  					Optional: true,
  2324  					Elem: &Resource{
  2325  						Schema: map[string]*Schema{
  2326  							"index": &Schema{
  2327  								Type:     TypeInt,
  2328  								Required: true,
  2329  							},
  2330  
  2331  							"gateway": &Schema{
  2332  								Type:     TypeSet,
  2333  								Optional: true,
  2334  								Elem:     &Schema{Type: TypeInt},
  2335  								Set: func(a interface{}) int {
  2336  									return a.(int)
  2337  								},
  2338  							},
  2339  						},
  2340  					},
  2341  					Set: func(v interface{}) int {
  2342  						m := v.(map[string]interface{})
  2343  						return m["index"].(int)
  2344  					},
  2345  				},
  2346  			},
  2347  
  2348  			State: &tofu.InstanceState{
  2349  				ID: "id",
  2350  				Attributes: map[string]string{
  2351  					"route.#": "0",
  2352  				},
  2353  			},
  2354  
  2355  			Config: map[string]interface{}{},
  2356  
  2357  			Diff: nil,
  2358  
  2359  			Err: false,
  2360  		},
  2361  
  2362  		{
  2363  			Name: "A set computed element shouldn't cause a diff",
  2364  			Schema: map[string]*Schema{
  2365  				"active": &Schema{
  2366  					Type:     TypeBool,
  2367  					Computed: true,
  2368  					ForceNew: true,
  2369  				},
  2370  			},
  2371  
  2372  			State: &tofu.InstanceState{
  2373  				ID: "id",
  2374  				Attributes: map[string]string{
  2375  					"active": "true",
  2376  				},
  2377  			},
  2378  
  2379  			Config: map[string]interface{}{},
  2380  
  2381  			Diff: nil,
  2382  
  2383  			Err: false,
  2384  		},
  2385  
  2386  		{
  2387  			Name: "An empty set should show up in the diff",
  2388  			Schema: map[string]*Schema{
  2389  				"instances": &Schema{
  2390  					Type:     TypeSet,
  2391  					Elem:     &Schema{Type: TypeString},
  2392  					Optional: true,
  2393  					ForceNew: true,
  2394  					Set: func(v interface{}) int {
  2395  						return len(v.(string))
  2396  					},
  2397  				},
  2398  			},
  2399  
  2400  			State: &tofu.InstanceState{
  2401  				ID: "id",
  2402  				Attributes: map[string]string{
  2403  					"instances.#": "1",
  2404  					"instances.3": "foo",
  2405  				},
  2406  			},
  2407  
  2408  			Config: map[string]interface{}{},
  2409  
  2410  			Diff: &tofu.InstanceDiff{
  2411  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2412  					"instances.#": &tofu.ResourceAttrDiff{
  2413  						Old:         "1",
  2414  						New:         "0",
  2415  						RequiresNew: true,
  2416  					},
  2417  					"instances.3": &tofu.ResourceAttrDiff{
  2418  						Old:         "foo",
  2419  						New:         "",
  2420  						NewRemoved:  true,
  2421  						RequiresNew: true,
  2422  					},
  2423  				},
  2424  			},
  2425  
  2426  			Err: false,
  2427  		},
  2428  
  2429  		{
  2430  			Name: "Map with empty value",
  2431  			Schema: map[string]*Schema{
  2432  				"vars": &Schema{
  2433  					Type: TypeMap,
  2434  				},
  2435  			},
  2436  
  2437  			State: nil,
  2438  
  2439  			Config: map[string]interface{}{
  2440  				"vars": map[string]interface{}{
  2441  					"foo": "",
  2442  				},
  2443  			},
  2444  
  2445  			Diff: &tofu.InstanceDiff{
  2446  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2447  					"vars.%": &tofu.ResourceAttrDiff{
  2448  						Old: "0",
  2449  						New: "1",
  2450  					},
  2451  					"vars.foo": &tofu.ResourceAttrDiff{
  2452  						Old: "",
  2453  						New: "",
  2454  					},
  2455  				},
  2456  			},
  2457  
  2458  			Err: false,
  2459  		},
  2460  
  2461  		{
  2462  			Name: "Unset bool, not in state",
  2463  			Schema: map[string]*Schema{
  2464  				"force": &Schema{
  2465  					Type:     TypeBool,
  2466  					Optional: true,
  2467  					ForceNew: true,
  2468  				},
  2469  			},
  2470  
  2471  			State: nil,
  2472  
  2473  			Config: map[string]interface{}{},
  2474  
  2475  			Diff: nil,
  2476  
  2477  			Err: false,
  2478  		},
  2479  
  2480  		{
  2481  			Name: "Unset set, not in state",
  2482  			Schema: map[string]*Schema{
  2483  				"metadata_keys": &Schema{
  2484  					Type:     TypeSet,
  2485  					Optional: true,
  2486  					ForceNew: true,
  2487  					Elem:     &Schema{Type: TypeInt},
  2488  					Set:      func(interface{}) int { return 0 },
  2489  				},
  2490  			},
  2491  
  2492  			State: nil,
  2493  
  2494  			Config: map[string]interface{}{},
  2495  
  2496  			Diff: nil,
  2497  
  2498  			Err: false,
  2499  		},
  2500  
  2501  		{
  2502  			Name: "Unset list in state, should not show up computed",
  2503  			Schema: map[string]*Schema{
  2504  				"metadata_keys": &Schema{
  2505  					Type:     TypeList,
  2506  					Optional: true,
  2507  					Computed: true,
  2508  					ForceNew: true,
  2509  					Elem:     &Schema{Type: TypeInt},
  2510  				},
  2511  			},
  2512  
  2513  			State: &tofu.InstanceState{
  2514  				ID: "id",
  2515  				Attributes: map[string]string{
  2516  					"metadata_keys.#": "0",
  2517  				},
  2518  			},
  2519  
  2520  			Config: map[string]interface{}{},
  2521  
  2522  			Diff: nil,
  2523  
  2524  			Err: false,
  2525  		},
  2526  
  2527  		{
  2528  			Name: "Computed map without config that's known to be empty does not generate diff",
  2529  			Schema: map[string]*Schema{
  2530  				"tags": &Schema{
  2531  					Type:     TypeMap,
  2532  					Computed: true,
  2533  				},
  2534  			},
  2535  
  2536  			Config: nil,
  2537  
  2538  			State: &tofu.InstanceState{
  2539  				ID: "id",
  2540  				Attributes: map[string]string{
  2541  					"tags.%": "0",
  2542  				},
  2543  			},
  2544  
  2545  			Diff: nil,
  2546  
  2547  			Err: false,
  2548  		},
  2549  
  2550  		{
  2551  			Name: "Set with hyphen keys",
  2552  			Schema: map[string]*Schema{
  2553  				"route": &Schema{
  2554  					Type:     TypeSet,
  2555  					Optional: true,
  2556  					Elem: &Resource{
  2557  						Schema: map[string]*Schema{
  2558  							"index": &Schema{
  2559  								Type:     TypeInt,
  2560  								Required: true,
  2561  							},
  2562  
  2563  							"gateway-name": &Schema{
  2564  								Type:     TypeString,
  2565  								Optional: true,
  2566  							},
  2567  						},
  2568  					},
  2569  					Set: func(v interface{}) int {
  2570  						m := v.(map[string]interface{})
  2571  						return m["index"].(int)
  2572  					},
  2573  				},
  2574  			},
  2575  
  2576  			State: nil,
  2577  
  2578  			Config: map[string]interface{}{
  2579  				"route": []interface{}{
  2580  					map[string]interface{}{
  2581  						"index":        "1",
  2582  						"gateway-name": "hello",
  2583  					},
  2584  				},
  2585  			},
  2586  
  2587  			Diff: &tofu.InstanceDiff{
  2588  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2589  					"route.#": &tofu.ResourceAttrDiff{
  2590  						Old: "0",
  2591  						New: "1",
  2592  					},
  2593  					"route.1.index": &tofu.ResourceAttrDiff{
  2594  						Old: "",
  2595  						New: "1",
  2596  					},
  2597  					"route.1.gateway-name": &tofu.ResourceAttrDiff{
  2598  						Old: "",
  2599  						New: "hello",
  2600  					},
  2601  				},
  2602  			},
  2603  
  2604  			Err: false,
  2605  		},
  2606  
  2607  		{
  2608  			Name: "StateFunc in nested set (#1759)",
  2609  			Schema: map[string]*Schema{
  2610  				"service_account": &Schema{
  2611  					Type:     TypeList,
  2612  					Optional: true,
  2613  					ForceNew: true,
  2614  					Elem: &Resource{
  2615  						Schema: map[string]*Schema{
  2616  							"scopes": &Schema{
  2617  								Type:     TypeSet,
  2618  								Required: true,
  2619  								ForceNew: true,
  2620  								Elem: &Schema{
  2621  									Type: TypeString,
  2622  									StateFunc: func(v interface{}) string {
  2623  										return v.(string) + "!"
  2624  									},
  2625  								},
  2626  								Set: func(v interface{}) int {
  2627  									i, err := strconv.Atoi(v.(string))
  2628  									if err != nil {
  2629  										t.Fatalf("err: %s", err)
  2630  									}
  2631  									return i
  2632  								},
  2633  							},
  2634  						},
  2635  					},
  2636  				},
  2637  			},
  2638  
  2639  			State: nil,
  2640  
  2641  			Config: map[string]interface{}{
  2642  				"service_account": []interface{}{
  2643  					map[string]interface{}{
  2644  						"scopes": []interface{}{"123"},
  2645  					},
  2646  				},
  2647  			},
  2648  
  2649  			Diff: &tofu.InstanceDiff{
  2650  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2651  					"service_account.#": &tofu.ResourceAttrDiff{
  2652  						Old:         "0",
  2653  						New:         "1",
  2654  						RequiresNew: true,
  2655  					},
  2656  					"service_account.0.scopes.#": &tofu.ResourceAttrDiff{
  2657  						Old:         "0",
  2658  						New:         "1",
  2659  						RequiresNew: true,
  2660  					},
  2661  					"service_account.0.scopes.123": &tofu.ResourceAttrDiff{
  2662  						Old:         "",
  2663  						New:         "123!",
  2664  						NewExtra:    "123",
  2665  						RequiresNew: true,
  2666  					},
  2667  				},
  2668  			},
  2669  
  2670  			Err: false,
  2671  		},
  2672  
  2673  		{
  2674  			Name: "Removing set elements",
  2675  			Schema: map[string]*Schema{
  2676  				"instances": &Schema{
  2677  					Type:     TypeSet,
  2678  					Elem:     &Schema{Type: TypeString},
  2679  					Optional: true,
  2680  					ForceNew: true,
  2681  					Set: func(v interface{}) int {
  2682  						return len(v.(string))
  2683  					},
  2684  				},
  2685  			},
  2686  
  2687  			State: &tofu.InstanceState{
  2688  				ID: "id",
  2689  				Attributes: map[string]string{
  2690  					"instances.#": "2",
  2691  					"instances.3": "333",
  2692  					"instances.2": "22",
  2693  				},
  2694  			},
  2695  
  2696  			Config: map[string]interface{}{
  2697  				"instances": []interface{}{"333", "4444"},
  2698  			},
  2699  
  2700  			Diff: &tofu.InstanceDiff{
  2701  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2702  					"instances.2": &tofu.ResourceAttrDiff{
  2703  						Old:         "22",
  2704  						New:         "",
  2705  						NewRemoved:  true,
  2706  						RequiresNew: true,
  2707  					},
  2708  					"instances.3": &tofu.ResourceAttrDiff{
  2709  						Old: "333",
  2710  						New: "333",
  2711  					},
  2712  					"instances.4": &tofu.ResourceAttrDiff{
  2713  						Old:         "",
  2714  						New:         "4444",
  2715  						RequiresNew: true,
  2716  					},
  2717  				},
  2718  			},
  2719  
  2720  			Err: false,
  2721  		},
  2722  
  2723  		{
  2724  			Name: "Bools can be set with 0/1 in config, still get true/false",
  2725  			Schema: map[string]*Schema{
  2726  				"one": &Schema{
  2727  					Type:     TypeBool,
  2728  					Optional: true,
  2729  				},
  2730  				"two": &Schema{
  2731  					Type:     TypeBool,
  2732  					Optional: true,
  2733  				},
  2734  				"three": &Schema{
  2735  					Type:     TypeBool,
  2736  					Optional: true,
  2737  				},
  2738  			},
  2739  
  2740  			State: &tofu.InstanceState{
  2741  				ID: "id",
  2742  				Attributes: map[string]string{
  2743  					"one":   "false",
  2744  					"two":   "true",
  2745  					"three": "true",
  2746  				},
  2747  			},
  2748  
  2749  			Config: map[string]interface{}{
  2750  				"one": "1",
  2751  				"two": "0",
  2752  			},
  2753  
  2754  			Diff: &tofu.InstanceDiff{
  2755  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2756  					"one": &tofu.ResourceAttrDiff{
  2757  						Old: "false",
  2758  						New: "true",
  2759  					},
  2760  					"two": &tofu.ResourceAttrDiff{
  2761  						Old: "true",
  2762  						New: "false",
  2763  					},
  2764  					"three": &tofu.ResourceAttrDiff{
  2765  						Old:        "true",
  2766  						New:        "false",
  2767  						NewRemoved: true,
  2768  					},
  2769  				},
  2770  			},
  2771  
  2772  			Err: false,
  2773  		},
  2774  
  2775  		{
  2776  			Name:   "tainted in state w/ no attr changes is still a replacement",
  2777  			Schema: map[string]*Schema{},
  2778  
  2779  			State: &tofu.InstanceState{
  2780  				ID: "id",
  2781  				Attributes: map[string]string{
  2782  					"id": "someid",
  2783  				},
  2784  				Tainted: true,
  2785  			},
  2786  
  2787  			Config: map[string]interface{}{},
  2788  
  2789  			Diff: &tofu.InstanceDiff{
  2790  				Attributes:     map[string]*tofu.ResourceAttrDiff{},
  2791  				DestroyTainted: true,
  2792  			},
  2793  		},
  2794  
  2795  		{
  2796  			Name: "Set ForceNew only marks the changing element as ForceNew",
  2797  			Schema: map[string]*Schema{
  2798  				"ports": &Schema{
  2799  					Type:     TypeSet,
  2800  					Required: true,
  2801  					ForceNew: true,
  2802  					Elem:     &Schema{Type: TypeInt},
  2803  					Set: func(a interface{}) int {
  2804  						return a.(int)
  2805  					},
  2806  				},
  2807  			},
  2808  
  2809  			State: &tofu.InstanceState{
  2810  				ID: "id",
  2811  				Attributes: map[string]string{
  2812  					"ports.#": "3",
  2813  					"ports.1": "1",
  2814  					"ports.2": "2",
  2815  					"ports.4": "4",
  2816  				},
  2817  			},
  2818  
  2819  			Config: map[string]interface{}{
  2820  				"ports": []interface{}{5, 2, 1},
  2821  			},
  2822  
  2823  			Diff: &tofu.InstanceDiff{
  2824  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2825  					"ports.1": &tofu.ResourceAttrDiff{
  2826  						Old: "1",
  2827  						New: "1",
  2828  					},
  2829  					"ports.2": &tofu.ResourceAttrDiff{
  2830  						Old: "2",
  2831  						New: "2",
  2832  					},
  2833  					"ports.5": &tofu.ResourceAttrDiff{
  2834  						Old:         "",
  2835  						New:         "5",
  2836  						RequiresNew: true,
  2837  					},
  2838  					"ports.4": &tofu.ResourceAttrDiff{
  2839  						Old:         "4",
  2840  						New:         "0",
  2841  						NewRemoved:  true,
  2842  						RequiresNew: true,
  2843  					},
  2844  				},
  2845  			},
  2846  		},
  2847  
  2848  		{
  2849  			Name: "removed optional items should trigger ForceNew",
  2850  			Schema: map[string]*Schema{
  2851  				"description": &Schema{
  2852  					Type:     TypeString,
  2853  					ForceNew: true,
  2854  					Optional: true,
  2855  				},
  2856  			},
  2857  
  2858  			State: &tofu.InstanceState{
  2859  				ID: "id",
  2860  				Attributes: map[string]string{
  2861  					"description": "foo",
  2862  				},
  2863  			},
  2864  
  2865  			Config: map[string]interface{}{},
  2866  
  2867  			Diff: &tofu.InstanceDiff{
  2868  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2869  					"description": &tofu.ResourceAttrDiff{
  2870  						Old:         "foo",
  2871  						New:         "",
  2872  						RequiresNew: true,
  2873  						NewRemoved:  true,
  2874  					},
  2875  				},
  2876  			},
  2877  
  2878  			Err: false,
  2879  		},
  2880  
  2881  		// GH-7715
  2882  		{
  2883  			Name: "computed value for boolean field",
  2884  			Schema: map[string]*Schema{
  2885  				"foo": &Schema{
  2886  					Type:     TypeBool,
  2887  					ForceNew: true,
  2888  					Computed: true,
  2889  					Optional: true,
  2890  				},
  2891  			},
  2892  
  2893  			State: &tofu.InstanceState{
  2894  				ID: "id",
  2895  			},
  2896  
  2897  			Config: map[string]interface{}{
  2898  				"foo": hcl2shim.UnknownVariableValue,
  2899  			},
  2900  
  2901  			Diff: &tofu.InstanceDiff{
  2902  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2903  					"foo": &tofu.ResourceAttrDiff{
  2904  						Old:         "",
  2905  						New:         "false",
  2906  						NewComputed: true,
  2907  						RequiresNew: true,
  2908  					},
  2909  				},
  2910  			},
  2911  
  2912  			Err: false,
  2913  		},
  2914  
  2915  		{
  2916  			Name: "Set ForceNew marks count as ForceNew if computed",
  2917  			Schema: map[string]*Schema{
  2918  				"ports": &Schema{
  2919  					Type:     TypeSet,
  2920  					Required: true,
  2921  					ForceNew: true,
  2922  					Elem:     &Schema{Type: TypeInt},
  2923  					Set: func(a interface{}) int {
  2924  						return a.(int)
  2925  					},
  2926  				},
  2927  			},
  2928  
  2929  			State: &tofu.InstanceState{
  2930  				ID: "id",
  2931  				Attributes: map[string]string{
  2932  					"ports.#": "3",
  2933  					"ports.1": "1",
  2934  					"ports.2": "2",
  2935  					"ports.4": "4",
  2936  				},
  2937  			},
  2938  
  2939  			Config: map[string]interface{}{
  2940  				"ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1},
  2941  			},
  2942  
  2943  			Diff: &tofu.InstanceDiff{
  2944  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2945  					"ports.#": &tofu.ResourceAttrDiff{
  2946  						NewComputed: true,
  2947  						RequiresNew: true,
  2948  					},
  2949  				},
  2950  			},
  2951  		},
  2952  
  2953  		{
  2954  			Name: "List with computed schema and ForceNew",
  2955  			Schema: map[string]*Schema{
  2956  				"config": &Schema{
  2957  					Type:     TypeList,
  2958  					Optional: true,
  2959  					ForceNew: true,
  2960  					Elem: &Schema{
  2961  						Type: TypeString,
  2962  					},
  2963  				},
  2964  			},
  2965  
  2966  			State: &tofu.InstanceState{
  2967  				ID: "id",
  2968  				Attributes: map[string]string{
  2969  					"config.#": "2",
  2970  					"config.0": "a",
  2971  					"config.1": "b",
  2972  				},
  2973  			},
  2974  
  2975  			Config: map[string]interface{}{
  2976  				"config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue},
  2977  			},
  2978  
  2979  			Diff: &tofu.InstanceDiff{
  2980  				Attributes: map[string]*tofu.ResourceAttrDiff{
  2981  					"config.#": &tofu.ResourceAttrDiff{
  2982  						Old:         "2",
  2983  						New:         "",
  2984  						RequiresNew: true,
  2985  						NewComputed: true,
  2986  					},
  2987  				},
  2988  			},
  2989  
  2990  			Err: false,
  2991  		},
  2992  
  2993  		{
  2994  			Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema",
  2995  			Schema: map[string]*Schema{
  2996  				"availability_zone": &Schema{
  2997  					Type:     TypeString,
  2998  					Optional: true,
  2999  					Computed: true,
  3000  				},
  3001  			},
  3002  
  3003  			State: nil,
  3004  
  3005  			Config: map[string]interface{}{
  3006  				"availability_zone": "foo",
  3007  			},
  3008  
  3009  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3010  				if err := d.SetNew("availability_zone", "bar"); err != nil {
  3011  					return err
  3012  				}
  3013  				if err := d.ForceNew("availability_zone"); err != nil {
  3014  					return err
  3015  				}
  3016  				return nil
  3017  			},
  3018  
  3019  			Diff: &tofu.InstanceDiff{
  3020  				Attributes: map[string]*tofu.ResourceAttrDiff{
  3021  					"availability_zone": &tofu.ResourceAttrDiff{
  3022  						Old:         "",
  3023  						New:         "bar",
  3024  						RequiresNew: true,
  3025  					},
  3026  				},
  3027  			},
  3028  
  3029  			Err: false,
  3030  		},
  3031  
  3032  		{
  3033  			// NOTE: This case is technically impossible in the current
  3034  			// implementation, because optional+computed values never show up in the
  3035  			// diff. In the event behavior changes this test should ensure that the
  3036  			// intended diff still shows up.
  3037  			Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema",
  3038  			Schema: map[string]*Schema{
  3039  				"availability_zone": &Schema{
  3040  					Type:     TypeString,
  3041  					Optional: true,
  3042  					Computed: true,
  3043  				},
  3044  			},
  3045  
  3046  			State: nil,
  3047  
  3048  			Config: map[string]interface{}{},
  3049  
  3050  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3051  				if err := d.SetNew("availability_zone", "bar"); err != nil {
  3052  					return err
  3053  				}
  3054  				if err := d.ForceNew("availability_zone"); err != nil {
  3055  					return err
  3056  				}
  3057  				return nil
  3058  			},
  3059  
  3060  			Diff: &tofu.InstanceDiff{
  3061  				Attributes: map[string]*tofu.ResourceAttrDiff{
  3062  					"availability_zone": &tofu.ResourceAttrDiff{
  3063  						Old:         "",
  3064  						New:         "bar",
  3065  						RequiresNew: true,
  3066  					},
  3067  				},
  3068  			},
  3069  
  3070  			Err: false,
  3071  		},
  3072  
  3073  		{
  3074  
  3075  			Name: "overridden diff with a CustomizeDiff function, ForceNew in schema",
  3076  			Schema: map[string]*Schema{
  3077  				"availability_zone": &Schema{
  3078  					Type:     TypeString,
  3079  					Optional: true,
  3080  					Computed: true,
  3081  					ForceNew: true,
  3082  				},
  3083  			},
  3084  
  3085  			State: nil,
  3086  
  3087  			Config: map[string]interface{}{
  3088  				"availability_zone": "foo",
  3089  			},
  3090  
  3091  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3092  				if err := d.SetNew("availability_zone", "bar"); err != nil {
  3093  					return err
  3094  				}
  3095  				return nil
  3096  			},
  3097  
  3098  			Diff: &tofu.InstanceDiff{
  3099  				Attributes: map[string]*tofu.ResourceAttrDiff{
  3100  					"availability_zone": &tofu.ResourceAttrDiff{
  3101  						Old:         "",
  3102  						New:         "bar",
  3103  						RequiresNew: true,
  3104  					},
  3105  				},
  3106  			},
  3107  
  3108  			Err: false,
  3109  		},
  3110  
  3111  		{
  3112  			Name: "required field with computed diff added with CustomizeDiff function",
  3113  			Schema: map[string]*Schema{
  3114  				"ami_id": &Schema{
  3115  					Type:     TypeString,
  3116  					Required: true,
  3117  				},
  3118  				"instance_id": &Schema{
  3119  					Type:     TypeString,
  3120  					Computed: true,
  3121  				},
  3122  			},
  3123  
  3124  			State: nil,
  3125  
  3126  			Config: map[string]interface{}{
  3127  				"ami_id": "foo",
  3128  			},
  3129  
  3130  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3131  				if err := d.SetNew("instance_id", "bar"); err != nil {
  3132  					return err
  3133  				}
  3134  				return nil
  3135  			},
  3136  
  3137  			Diff: &tofu.InstanceDiff{
  3138  				Attributes: map[string]*tofu.ResourceAttrDiff{
  3139  					"ami_id": &tofu.ResourceAttrDiff{
  3140  						Old: "",
  3141  						New: "foo",
  3142  					},
  3143  					"instance_id": &tofu.ResourceAttrDiff{
  3144  						Old: "",
  3145  						New: "bar",
  3146  					},
  3147  				},
  3148  			},
  3149  
  3150  			Err: false,
  3151  		},
  3152  
  3153  		{
  3154  			Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition",
  3155  			Schema: map[string]*Schema{
  3156  				"ports": &Schema{
  3157  					Type:     TypeSet,
  3158  					Optional: true,
  3159  					Computed: true,
  3160  					Elem:     &Schema{Type: TypeInt},
  3161  					Set: func(a interface{}) int {
  3162  						return a.(int)
  3163  					},
  3164  				},
  3165  			},
  3166  
  3167  			State: &tofu.InstanceState{
  3168  				ID: "id",
  3169  				Attributes: map[string]string{
  3170  					"ports.#": "3",
  3171  					"ports.1": "1",
  3172  					"ports.2": "2",
  3173  					"ports.4": "4",
  3174  				},
  3175  			},
  3176  
  3177  			Config: map[string]interface{}{
  3178  				"ports": []interface{}{5, 2, 6},
  3179  			},
  3180  
  3181  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3182  				if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil {
  3183  					return err
  3184  				}
  3185  				if err := d.ForceNew("ports"); err != nil {
  3186  					return err
  3187  				}
  3188  				return nil
  3189  			},
  3190  
  3191  			Diff: &tofu.InstanceDiff{
  3192  				Attributes: map[string]*tofu.ResourceAttrDiff{
  3193  					"ports.1": &tofu.ResourceAttrDiff{
  3194  						Old: "1",
  3195  						New: "1",
  3196  					},
  3197  					"ports.2": &tofu.ResourceAttrDiff{
  3198  						Old: "2",
  3199  						New: "2",
  3200  					},
  3201  					"ports.5": &tofu.ResourceAttrDiff{
  3202  						Old:         "",
  3203  						New:         "5",
  3204  						RequiresNew: true,
  3205  					},
  3206  					"ports.4": &tofu.ResourceAttrDiff{
  3207  						Old:         "4",
  3208  						New:         "0",
  3209  						NewRemoved:  true,
  3210  						RequiresNew: true,
  3211  					},
  3212  				},
  3213  			},
  3214  		},
  3215  
  3216  		{
  3217  			Name:   "tainted resource does not run CustomizeDiffFunc",
  3218  			Schema: map[string]*Schema{},
  3219  
  3220  			State: &tofu.InstanceState{
  3221  				ID: "someid",
  3222  				Attributes: map[string]string{
  3223  					"id": "someid",
  3224  				},
  3225  				Tainted: true,
  3226  			},
  3227  
  3228  			Config: map[string]interface{}{},
  3229  
  3230  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3231  				return errors.New("diff customization should not have run")
  3232  			},
  3233  
  3234  			Diff: &tofu.InstanceDiff{
  3235  				Attributes:     map[string]*tofu.ResourceAttrDiff{},
  3236  				DestroyTainted: true,
  3237  			},
  3238  
  3239  			Err: false,
  3240  		},
  3241  
  3242  		{
  3243  			Name: "NewComputed based on a conditional with CustomizeDiffFunc",
  3244  			Schema: map[string]*Schema{
  3245  				"etag": &Schema{
  3246  					Type:     TypeString,
  3247  					Optional: true,
  3248  					Computed: true,
  3249  				},
  3250  				"version_id": &Schema{
  3251  					Type:     TypeString,
  3252  					Computed: true,
  3253  				},
  3254  			},
  3255  
  3256  			State: &tofu.InstanceState{
  3257  				ID: "id",
  3258  				Attributes: map[string]string{
  3259  					"etag":       "foo",
  3260  					"version_id": "1",
  3261  				},
  3262  			},
  3263  
  3264  			Config: map[string]interface{}{
  3265  				"etag": "bar",
  3266  			},
  3267  
  3268  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3269  				if d.HasChange("etag") {
  3270  					d.SetNewComputed("version_id")
  3271  				}
  3272  				return nil
  3273  			},
  3274  
  3275  			Diff: &tofu.InstanceDiff{
  3276  				Attributes: map[string]*tofu.ResourceAttrDiff{
  3277  					"etag": &tofu.ResourceAttrDiff{
  3278  						Old: "foo",
  3279  						New: "bar",
  3280  					},
  3281  					"version_id": &tofu.ResourceAttrDiff{
  3282  						Old:         "1",
  3283  						New:         "",
  3284  						NewComputed: true,
  3285  					},
  3286  				},
  3287  			},
  3288  
  3289  			Err: false,
  3290  		},
  3291  
  3292  		{
  3293  			Name: "vetoing a diff",
  3294  			Schema: map[string]*Schema{
  3295  				"foo": &Schema{
  3296  					Type:     TypeString,
  3297  					Optional: true,
  3298  					Computed: true,
  3299  				},
  3300  			},
  3301  
  3302  			State: &tofu.InstanceState{
  3303  				ID: "id",
  3304  				Attributes: map[string]string{
  3305  					"foo": "bar",
  3306  				},
  3307  			},
  3308  
  3309  			Config: map[string]interface{}{
  3310  				"foo": "baz",
  3311  			},
  3312  
  3313  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3314  				return fmt.Errorf("diff vetoed")
  3315  			},
  3316  
  3317  			Err: true,
  3318  		},
  3319  
  3320  		// A lot of resources currently depended on using the empty string as a
  3321  		// nil/unset value.
  3322  		{
  3323  			Name: "optional, computed, empty string",
  3324  			Schema: map[string]*Schema{
  3325  				"attr": &Schema{
  3326  					Type:     TypeString,
  3327  					Optional: true,
  3328  					Computed: true,
  3329  				},
  3330  			},
  3331  
  3332  			State: &tofu.InstanceState{
  3333  				ID: "id",
  3334  				Attributes: map[string]string{
  3335  					"attr": "bar",
  3336  				},
  3337  			},
  3338  
  3339  			Config: map[string]interface{}{
  3340  				"attr": "",
  3341  			},
  3342  		},
  3343  
  3344  		{
  3345  			Name: "optional, computed, empty string should not crash in CustomizeDiff",
  3346  			Schema: map[string]*Schema{
  3347  				"unrelated_set": {
  3348  					Type:     TypeSet,
  3349  					Optional: true,
  3350  					Elem:     &Schema{Type: TypeString},
  3351  				},
  3352  				"stream_enabled": {
  3353  					Type:     TypeBool,
  3354  					Optional: true,
  3355  				},
  3356  				"stream_view_type": {
  3357  					Type:     TypeString,
  3358  					Optional: true,
  3359  					Computed: true,
  3360  				},
  3361  			},
  3362  
  3363  			State: &tofu.InstanceState{
  3364  				ID: "id",
  3365  				Attributes: map[string]string{
  3366  					"unrelated_set.#":  "0",
  3367  					"stream_enabled":   "true",
  3368  					"stream_view_type": "KEYS_ONLY",
  3369  				},
  3370  			},
  3371  			Config: map[string]interface{}{
  3372  				"stream_enabled":   false,
  3373  				"stream_view_type": "",
  3374  			},
  3375  			CustomizeDiff: func(diff *ResourceDiff, v interface{}) error {
  3376  				v, ok := diff.GetOk("unrelated_set")
  3377  				if ok {
  3378  					return fmt.Errorf("Didn't expect unrelated_set: %#v", v)
  3379  				}
  3380  				return nil
  3381  			},
  3382  			Diff: &tofu.InstanceDiff{
  3383  				Attributes: map[string]*tofu.ResourceAttrDiff{
  3384  					"stream_enabled": {
  3385  						Old: "true",
  3386  						New: "false",
  3387  					},
  3388  				},
  3389  			},
  3390  		},
  3391  	}
  3392  
  3393  	for i, tc := range cases {
  3394  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
  3395  			c := tofu.NewResourceConfigRaw(tc.Config)
  3396  
  3397  			{
  3398  				d, err := schemaMap(tc.Schema).Diff(tc.State, c, tc.CustomizeDiff, nil, false)
  3399  				if err != nil != tc.Err {
  3400  					t.Fatalf("err: %s", err)
  3401  				}
  3402  				if !cmp.Equal(d, tc.Diff, equateEmpty) {
  3403  					t.Fatal(cmp.Diff(d, tc.Diff, equateEmpty))
  3404  				}
  3405  			}
  3406  			// up to here is already tested in helper/schema; we're just
  3407  			// verify that we haven't broken any tests in transition.
  3408  
  3409  			// create a schema from the schemaMap
  3410  			testSchema := resourceSchemaToBlock(tc.Schema)
  3411  
  3412  			// get our initial state cty.Value
  3413  			stateVal, err := StateValueFromInstanceState(tc.State, testSchema.ImpliedType())
  3414  			if err != nil {
  3415  				t.Fatal(err)
  3416  			}
  3417  
  3418  			// this is the desired cty.Value from the configuration
  3419  			configVal := hcl2shim.HCL2ValueFromConfigValue(c.Config)
  3420  
  3421  			// verify that we can round-trip the config
  3422  			origConfig := hcl2shim.ConfigValueFromHCL2(configVal)
  3423  			if !cmp.Equal(c.Config, origConfig, equateEmpty) {
  3424  				t.Fatal(cmp.Diff(c.Config, origConfig, equateEmpty))
  3425  			}
  3426  
  3427  			// make sure our config conforms precisely to the schema
  3428  			configVal, err = testSchema.CoerceValue(configVal)
  3429  			if err != nil {
  3430  				t.Fatal(tfdiags.FormatError(err))
  3431  			}
  3432  
  3433  			// The new API requires returning the desired state rather than a
  3434  			// diff, so we need to verify that we can combine the state and
  3435  			// diff and recreate a new state.
  3436  
  3437  			// now verify that we can create diff, using the new config and state values
  3438  			// customize isn't run on tainted resources
  3439  			tainted := tc.State != nil && tc.State.Tainted
  3440  			if tainted {
  3441  				tc.CustomizeDiff = nil
  3442  			}
  3443  
  3444  			res := &Resource{Schema: tc.Schema}
  3445  
  3446  			d, err := diffFromValues(stateVal, configVal, res, tc.CustomizeDiff)
  3447  			if err != nil {
  3448  				if !tc.Err {
  3449  					t.Fatal(err)
  3450  				}
  3451  			}
  3452  
  3453  			// In a real "apply" operation there would be no unknown values,
  3454  			// so for tests containing unknowns we'll stop here: the steps
  3455  			// after this point apply only to the apply phase.
  3456  			if !configVal.IsWhollyKnown() {
  3457  				return
  3458  			}
  3459  
  3460  			// our diff function can't set DestroyTainted, but match the
  3461  			// expected value here for the test fixtures
  3462  			if tainted {
  3463  				if d == nil {
  3464  					d = &tofu.InstanceDiff{}
  3465  				}
  3466  				d.DestroyTainted = true
  3467  			}
  3468  
  3469  			if eq, _ := d.Same(tc.Diff); !eq {
  3470  				t.Fatal(cmp.Diff(d, tc.Diff))
  3471  			}
  3472  
  3473  		})
  3474  	}
  3475  }
  3476  
  3477  func resourceSchemaToBlock(s map[string]*Schema) *configschema.Block {
  3478  	return (&Resource{Schema: s}).CoreConfigSchema()
  3479  }
  3480  
  3481  func TestRemoveConfigUnknowns(t *testing.T) {
  3482  	cfg := map[string]interface{}{
  3483  		"id": "74D93920-ED26-11E3-AC10-0800200C9A66",
  3484  		"route_rules": []interface{}{
  3485  			map[string]interface{}{
  3486  				"cidr_block":        "74D93920-ED26-11E3-AC10-0800200C9A66",
  3487  				"destination":       "0.0.0.0/0",
  3488  				"destination_type":  "CIDR_BLOCK",
  3489  				"network_entity_id": "1",
  3490  			},
  3491  			map[string]interface{}{
  3492  				"cidr_block":       "74D93920-ED26-11E3-AC10-0800200C9A66",
  3493  				"destination":      "0.0.0.0/0",
  3494  				"destination_type": "CIDR_BLOCK",
  3495  				"sub_block": []interface{}{
  3496  					map[string]interface{}{
  3497  						"computed": "74D93920-ED26-11E3-AC10-0800200C9A66",
  3498  					},
  3499  				},
  3500  			},
  3501  		},
  3502  	}
  3503  
  3504  	expect := map[string]interface{}{
  3505  		"route_rules": []interface{}{
  3506  			map[string]interface{}{
  3507  				"destination":       "0.0.0.0/0",
  3508  				"destination_type":  "CIDR_BLOCK",
  3509  				"network_entity_id": "1",
  3510  			},
  3511  			map[string]interface{}{
  3512  				"destination":      "0.0.0.0/0",
  3513  				"destination_type": "CIDR_BLOCK",
  3514  				"sub_block": []interface{}{
  3515  					map[string]interface{}{},
  3516  				},
  3517  			},
  3518  		},
  3519  	}
  3520  
  3521  	removeConfigUnknowns(cfg)
  3522  
  3523  	if !reflect.DeepEqual(cfg, expect) {
  3524  		t.Fatalf("\nexpected: %#v\ngot:      %#v", expect, cfg)
  3525  	}
  3526  }