github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/differ/differ_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 differ
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"testing"
    12  
    13  	"github.com/zclconf/go-cty/cty"
    14  	ctyjson "github.com/zclconf/go-cty/cty/json"
    15  
    16  	"github.com/opentofu/opentofu/internal/command/jsonformat/computed/renderers"
    17  	"github.com/opentofu/opentofu/internal/command/jsonformat/structured"
    18  	"github.com/opentofu/opentofu/internal/command/jsonformat/structured/attribute_path"
    19  	"github.com/opentofu/opentofu/internal/command/jsonprovider"
    20  	"github.com/opentofu/opentofu/internal/plans"
    21  )
    22  
    23  type SetDiff struct {
    24  	Before SetDiffEntry
    25  	After  SetDiffEntry
    26  }
    27  
    28  type SetDiffEntry struct {
    29  	SingleDiff renderers.ValidateDiffFunction
    30  	ObjectDiff map[string]renderers.ValidateDiffFunction
    31  
    32  	Replace bool
    33  	Action  plans.Action
    34  }
    35  
    36  func (entry SetDiffEntry) Validate(obj func(attributes map[string]renderers.ValidateDiffFunction, action plans.Action, replace bool) renderers.ValidateDiffFunction) renderers.ValidateDiffFunction {
    37  	if entry.SingleDiff != nil {
    38  		return entry.SingleDiff
    39  	}
    40  
    41  	return obj(entry.ObjectDiff, entry.Action, entry.Replace)
    42  }
    43  
    44  func TestValue_SimpleBlocks(t *testing.T) {
    45  	// Most of the other test functions wrap the test cases in various
    46  	// collections or blocks. This function just very simply lets you specify
    47  	// individual test cases within blocks for some simple tests.
    48  
    49  	tcs := map[string]struct {
    50  		input    structured.Change
    51  		block    *jsonprovider.Block
    52  		validate renderers.ValidateDiffFunction
    53  	}{
    54  		"delete_with_null_sensitive_value": {
    55  			input: structured.Change{
    56  				Before: map[string]interface{}{
    57  					"normal_attribute": "some value",
    58  				},
    59  				After: nil,
    60  				BeforeSensitive: map[string]interface{}{
    61  					"sensitive_attribute": true,
    62  				},
    63  				AfterSensitive: false,
    64  			},
    65  			block: &jsonprovider.Block{
    66  				Attributes: map[string]*jsonprovider.Attribute{
    67  					"normal_attribute": {
    68  						AttributeType: unmarshalType(t, cty.String),
    69  					},
    70  					"sensitive_attribute": {
    71  						AttributeType: unmarshalType(t, cty.String),
    72  					},
    73  				},
    74  			},
    75  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
    76  				"normal_attribute": renderers.ValidatePrimitive("some value", nil, plans.Delete, false),
    77  			}, nil, nil, nil, nil, plans.Delete, false),
    78  		},
    79  		"create_with_null_sensitive_value": {
    80  			input: structured.Change{
    81  				Before: nil,
    82  				After: map[string]interface{}{
    83  					"normal_attribute": "some value",
    84  				},
    85  				BeforeSensitive: map[string]interface{}{
    86  					"sensitive_attribute": true,
    87  				},
    88  				AfterSensitive: false,
    89  			},
    90  			block: &jsonprovider.Block{
    91  				Attributes: map[string]*jsonprovider.Attribute{
    92  					"normal_attribute": {
    93  						AttributeType: unmarshalType(t, cty.String),
    94  					},
    95  					"sensitive_attribute": {
    96  						AttributeType: unmarshalType(t, cty.String),
    97  					},
    98  				},
    99  			},
   100  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
   101  				"normal_attribute": renderers.ValidatePrimitive(nil, "some value", plans.Create, false),
   102  			}, nil, nil, nil, nil, plans.Create, false),
   103  		},
   104  	}
   105  	for name, tc := range tcs {
   106  		// Set some default values
   107  		if tc.input.ReplacePaths == nil {
   108  			tc.input.ReplacePaths = &attribute_path.PathMatcher{}
   109  		}
   110  
   111  		if tc.input.RelevantAttributes == nil {
   112  			tc.input.RelevantAttributes = attribute_path.AlwaysMatcher()
   113  		}
   114  
   115  		t.Run(name, func(t *testing.T) {
   116  			tc.validate(t, ComputeDiffForBlock(tc.input, tc.block))
   117  		})
   118  	}
   119  }
   120  
   121  func TestValue_ObjectAttributes(t *testing.T) {
   122  	// This function holds a range of test cases creating, deleting and editing
   123  	// objects. It is built in such a way that it can automatically test these
   124  	// operations on objects both directly and nested, as well as within all
   125  	// types of collections.
   126  
   127  	tcs := map[string]struct {
   128  		input                structured.Change
   129  		attributes           map[string]cty.Type
   130  		validateSingleDiff   renderers.ValidateDiffFunction
   131  		validateObject       renderers.ValidateDiffFunction
   132  		validateNestedObject renderers.ValidateDiffFunction
   133  		validateDiffs        map[string]renderers.ValidateDiffFunction
   134  		validateList         renderers.ValidateDiffFunction
   135  		validateReplace      bool
   136  		validateAction       plans.Action
   137  		// Sets break changes out differently to the other collections, so they
   138  		// have their own entry.
   139  		validateSetDiffs *SetDiff
   140  	}{
   141  		"create": {
   142  			input: structured.Change{
   143  				Before: nil,
   144  				After: map[string]interface{}{
   145  					"attribute_one": "new",
   146  				},
   147  			},
   148  			attributes: map[string]cty.Type{
   149  				"attribute_one": cty.String,
   150  			},
   151  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   152  				"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   153  			},
   154  			validateAction:  plans.Create,
   155  			validateReplace: false,
   156  		},
   157  		"delete": {
   158  			input: structured.Change{
   159  				Before: map[string]interface{}{
   160  					"attribute_one": "old",
   161  				},
   162  				After: nil,
   163  			},
   164  			attributes: map[string]cty.Type{
   165  				"attribute_one": cty.String,
   166  			},
   167  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   168  				"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   169  			},
   170  			validateAction:  plans.Delete,
   171  			validateReplace: false,
   172  		},
   173  		"create_sensitive": {
   174  			input: structured.Change{
   175  				Before: nil,
   176  				After: map[string]interface{}{
   177  					"attribute_one": "new",
   178  				},
   179  				AfterSensitive: true,
   180  			},
   181  			attributes: map[string]cty.Type{
   182  				"attribute_one": cty.String,
   183  			},
   184  			validateSingleDiff: renderers.ValidateSensitive(renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
   185  				"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   186  			}, plans.Create, false),
   187  				false,
   188  				true,
   189  				plans.Create,
   190  				false),
   191  			validateNestedObject: renderers.ValidateSensitive(renderers.ValidateNestedObject(map[string]renderers.ValidateDiffFunction{
   192  				"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   193  			}, plans.Create, false),
   194  				false,
   195  				true,
   196  				plans.Create,
   197  				false),
   198  		},
   199  		"delete_sensitive": {
   200  			input: structured.Change{
   201  				Before: map[string]interface{}{
   202  					"attribute_one": "old",
   203  				},
   204  				BeforeSensitive: true,
   205  				After:           nil,
   206  			},
   207  			attributes: map[string]cty.Type{
   208  				"attribute_one": cty.String,
   209  			},
   210  			validateSingleDiff: renderers.ValidateSensitive(renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
   211  				"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   212  			}, plans.Delete, false), true, false, plans.Delete, false),
   213  			validateNestedObject: renderers.ValidateSensitive(renderers.ValidateNestedObject(map[string]renderers.ValidateDiffFunction{
   214  				"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   215  			}, plans.Delete, false), true, false, plans.Delete, false),
   216  		},
   217  		"create_unknown": {
   218  			input: structured.Change{
   219  				Before:  nil,
   220  				After:   nil,
   221  				Unknown: true,
   222  			},
   223  			attributes: map[string]cty.Type{
   224  				"attribute_one": cty.String,
   225  			},
   226  			validateSingleDiff: renderers.ValidateUnknown(nil, plans.Create, false),
   227  		},
   228  		"update_unknown": {
   229  			input: structured.Change{
   230  				Before: map[string]interface{}{
   231  					"attribute_one": "old",
   232  				},
   233  				After:   nil,
   234  				Unknown: true,
   235  			},
   236  			attributes: map[string]cty.Type{
   237  				"attribute_one": cty.String,
   238  			},
   239  			validateObject: renderers.ValidateUnknown(renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
   240  				"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   241  			}, plans.Delete, false), plans.Update, false),
   242  			validateNestedObject: renderers.ValidateUnknown(renderers.ValidateNestedObject(map[string]renderers.ValidateDiffFunction{
   243  				"attribute_one": renderers.ValidateUnknown(renderers.ValidatePrimitive("old", nil, plans.Delete, false), plans.Update, false),
   244  			}, plans.Update, false), plans.Update, false),
   245  			validateSetDiffs: &SetDiff{
   246  				Before: SetDiffEntry{
   247  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   248  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   249  					},
   250  					Action:  plans.Delete,
   251  					Replace: false,
   252  				},
   253  				After: SetDiffEntry{
   254  					SingleDiff: renderers.ValidateUnknown(nil, plans.Create, false),
   255  				},
   256  			},
   257  		},
   258  		"create_attribute": {
   259  			input: structured.Change{
   260  				Before: map[string]interface{}{},
   261  				After: map[string]interface{}{
   262  					"attribute_one": "new",
   263  				},
   264  			},
   265  			attributes: map[string]cty.Type{
   266  				"attribute_one": cty.String,
   267  			},
   268  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   269  				"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   270  			},
   271  			validateAction:  plans.Update,
   272  			validateReplace: false,
   273  			validateSetDiffs: &SetDiff{
   274  				Before: SetDiffEntry{
   275  					ObjectDiff: nil,
   276  					Action:     plans.Delete,
   277  					Replace:    false,
   278  				},
   279  				After: SetDiffEntry{
   280  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   281  						"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   282  					},
   283  					Action:  plans.Create,
   284  					Replace: false,
   285  				},
   286  			},
   287  		},
   288  		"create_attribute_from_explicit_null": {
   289  			input: structured.Change{
   290  				Before: map[string]interface{}{
   291  					"attribute_one": nil,
   292  				},
   293  				After: map[string]interface{}{
   294  					"attribute_one": "new",
   295  				},
   296  			},
   297  			attributes: map[string]cty.Type{
   298  				"attribute_one": cty.String,
   299  			},
   300  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   301  				"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   302  			},
   303  			validateAction:  plans.Update,
   304  			validateReplace: false,
   305  			validateSetDiffs: &SetDiff{
   306  				Before: SetDiffEntry{
   307  					ObjectDiff: nil,
   308  					Action:     plans.Delete,
   309  					Replace:    false,
   310  				},
   311  				After: SetDiffEntry{
   312  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   313  						"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   314  					},
   315  					Action:  plans.Create,
   316  					Replace: false,
   317  				},
   318  			},
   319  		},
   320  		"delete_attribute": {
   321  			input: structured.Change{
   322  				Before: map[string]interface{}{
   323  					"attribute_one": "old",
   324  				},
   325  				After: map[string]interface{}{},
   326  			},
   327  			attributes: map[string]cty.Type{
   328  				"attribute_one": cty.String,
   329  			},
   330  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   331  				"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   332  			},
   333  			validateAction:  plans.Update,
   334  			validateReplace: false,
   335  			validateSetDiffs: &SetDiff{
   336  				Before: SetDiffEntry{
   337  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   338  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   339  					},
   340  					Action:  plans.Delete,
   341  					Replace: false,
   342  				},
   343  				After: SetDiffEntry{
   344  					ObjectDiff: nil,
   345  					Action:     plans.Create,
   346  					Replace:    false,
   347  				},
   348  			},
   349  		},
   350  		"delete_attribute_to_explicit_null": {
   351  			input: structured.Change{
   352  				Before: map[string]interface{}{
   353  					"attribute_one": "old",
   354  				},
   355  				After: map[string]interface{}{
   356  					"attribute_one": nil,
   357  				},
   358  			},
   359  			attributes: map[string]cty.Type{
   360  				"attribute_one": cty.String,
   361  			},
   362  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   363  				"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   364  			},
   365  			validateAction:  plans.Update,
   366  			validateReplace: false,
   367  			validateSetDiffs: &SetDiff{
   368  				Before: SetDiffEntry{
   369  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   370  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   371  					},
   372  					Action:  plans.Delete,
   373  					Replace: false,
   374  				},
   375  				After: SetDiffEntry{
   376  					ObjectDiff: nil,
   377  					Action:     plans.Create,
   378  					Replace:    false,
   379  				},
   380  			},
   381  		},
   382  		"update_attribute": {
   383  			input: structured.Change{
   384  				Before: map[string]interface{}{
   385  					"attribute_one": "old",
   386  				},
   387  				After: map[string]interface{}{
   388  					"attribute_one": "new",
   389  				},
   390  			},
   391  			attributes: map[string]cty.Type{
   392  				"attribute_one": cty.String,
   393  			},
   394  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   395  				"attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false),
   396  			},
   397  			validateAction:  plans.Update,
   398  			validateReplace: false,
   399  			validateSetDiffs: &SetDiff{
   400  				Before: SetDiffEntry{
   401  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   402  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   403  					},
   404  					Action:  plans.Delete,
   405  					Replace: false,
   406  				},
   407  				After: SetDiffEntry{
   408  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   409  						"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   410  					},
   411  					Action:  plans.Create,
   412  					Replace: false,
   413  				},
   414  			},
   415  		},
   416  		"create_sensitive_attribute": {
   417  			input: structured.Change{
   418  				Before: map[string]interface{}{},
   419  				After: map[string]interface{}{
   420  					"attribute_one": "new",
   421  				},
   422  				AfterSensitive: map[string]interface{}{
   423  					"attribute_one": true,
   424  				},
   425  			},
   426  			attributes: map[string]cty.Type{
   427  				"attribute_one": cty.String,
   428  			},
   429  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   430  				"attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false),
   431  			},
   432  			validateAction:  plans.Update,
   433  			validateReplace: false,
   434  			validateSetDiffs: &SetDiff{
   435  				Before: SetDiffEntry{
   436  					ObjectDiff: nil,
   437  					Action:     plans.Delete,
   438  					Replace:    false,
   439  				},
   440  				After: SetDiffEntry{
   441  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   442  						"attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false),
   443  					},
   444  					Action:  plans.Create,
   445  					Replace: false,
   446  				},
   447  			},
   448  		},
   449  		"delete_sensitive_attribute": {
   450  			input: structured.Change{
   451  				Before: map[string]interface{}{
   452  					"attribute_one": "old",
   453  				},
   454  				BeforeSensitive: map[string]interface{}{
   455  					"attribute_one": true,
   456  				},
   457  				After: map[string]interface{}{},
   458  			},
   459  			attributes: map[string]cty.Type{
   460  				"attribute_one": cty.String,
   461  			},
   462  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   463  				"attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false),
   464  			},
   465  			validateAction:  plans.Update,
   466  			validateReplace: false,
   467  			validateSetDiffs: &SetDiff{
   468  				Before: SetDiffEntry{
   469  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   470  						"attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false),
   471  					},
   472  					Action:  plans.Delete,
   473  					Replace: false,
   474  				},
   475  				After: SetDiffEntry{
   476  					ObjectDiff: nil,
   477  					Action:     plans.Create,
   478  					Replace:    false,
   479  				},
   480  			},
   481  		},
   482  		"update_sensitive_attribute": {
   483  			input: structured.Change{
   484  				Before: map[string]interface{}{
   485  					"attribute_one": "old",
   486  				},
   487  				BeforeSensitive: map[string]interface{}{
   488  					"attribute_one": true,
   489  				},
   490  				After: map[string]interface{}{
   491  					"attribute_one": "new",
   492  				},
   493  				AfterSensitive: map[string]interface{}{
   494  					"attribute_one": true,
   495  				},
   496  			},
   497  			attributes: map[string]cty.Type{
   498  				"attribute_one": cty.String,
   499  			},
   500  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   501  				"attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", "new", plans.Update, false), true, true, plans.Update, false),
   502  			},
   503  			validateAction:  plans.Update,
   504  			validateReplace: false,
   505  			validateSetDiffs: &SetDiff{
   506  				Before: SetDiffEntry{
   507  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   508  						"attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false),
   509  					},
   510  					Action:  plans.Delete,
   511  					Replace: false,
   512  				},
   513  				After: SetDiffEntry{
   514  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   515  						"attribute_one": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false),
   516  					},
   517  					Action:  plans.Create,
   518  					Replace: false,
   519  				},
   520  			},
   521  		},
   522  		"create_computed_attribute": {
   523  			input: structured.Change{
   524  				Before: map[string]interface{}{},
   525  				After:  map[string]interface{}{},
   526  				Unknown: map[string]interface{}{
   527  					"attribute_one": true,
   528  				},
   529  			},
   530  			attributes: map[string]cty.Type{
   531  				"attribute_one": cty.String,
   532  			},
   533  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   534  				"attribute_one": renderers.ValidateUnknown(nil, plans.Create, false),
   535  			},
   536  			validateAction:  plans.Update,
   537  			validateReplace: false,
   538  		},
   539  		"update_computed_attribute": {
   540  			input: structured.Change{
   541  				Before: map[string]interface{}{
   542  					"attribute_one": "old",
   543  				},
   544  				After: map[string]interface{}{},
   545  				Unknown: map[string]interface{}{
   546  					"attribute_one": true,
   547  				},
   548  			},
   549  			attributes: map[string]cty.Type{
   550  				"attribute_one": cty.String,
   551  			},
   552  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   553  				"attribute_one": renderers.ValidateUnknown(
   554  					renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   555  					plans.Update,
   556  					false),
   557  			},
   558  			validateAction:  plans.Update,
   559  			validateReplace: false,
   560  			validateSetDiffs: &SetDiff{
   561  				Before: SetDiffEntry{
   562  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   563  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   564  					},
   565  					Action:  plans.Delete,
   566  					Replace: false,
   567  				},
   568  				After: SetDiffEntry{
   569  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   570  						"attribute_one": renderers.ValidateUnknown(nil, plans.Create, false),
   571  					},
   572  					Action:  plans.Create,
   573  					Replace: false,
   574  				},
   575  			},
   576  		},
   577  		"ignores_unset_fields": {
   578  			input: structured.Change{
   579  				Before: map[string]interface{}{},
   580  				After:  map[string]interface{}{},
   581  			},
   582  			attributes: map[string]cty.Type{
   583  				"attribute_one": cty.String,
   584  			},
   585  			validateDiffs:   map[string]renderers.ValidateDiffFunction{},
   586  			validateAction:  plans.NoOp,
   587  			validateReplace: false,
   588  		},
   589  		"update_replace_self": {
   590  			input: structured.Change{
   591  				Before: map[string]interface{}{
   592  					"attribute_one": "old",
   593  				},
   594  				After: map[string]interface{}{
   595  					"attribute_one": "new",
   596  				},
   597  				ReplacePaths: &attribute_path.PathMatcher{
   598  					Paths: [][]interface{}{
   599  						{},
   600  					},
   601  				},
   602  			},
   603  			attributes: map[string]cty.Type{
   604  				"attribute_one": cty.String,
   605  			},
   606  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   607  				"attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false),
   608  			},
   609  			validateAction:  plans.Update,
   610  			validateReplace: true,
   611  			validateSetDiffs: &SetDiff{
   612  				Before: SetDiffEntry{
   613  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   614  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
   615  					},
   616  					Action:  plans.Delete,
   617  					Replace: true,
   618  				},
   619  				After: SetDiffEntry{
   620  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   621  						"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
   622  					},
   623  					Action:  plans.Create,
   624  					Replace: true,
   625  				},
   626  			},
   627  		},
   628  		"update_replace_attribute": {
   629  			input: structured.Change{
   630  				Before: map[string]interface{}{
   631  					"attribute_one": "old",
   632  				},
   633  				After: map[string]interface{}{
   634  					"attribute_one": "new",
   635  				},
   636  				ReplacePaths: &attribute_path.PathMatcher{
   637  					Paths: [][]interface{}{
   638  						{"attribute_one"},
   639  					},
   640  				},
   641  			},
   642  			attributes: map[string]cty.Type{
   643  				"attribute_one": cty.String,
   644  			},
   645  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   646  				"attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, true),
   647  			},
   648  			validateAction:  plans.Update,
   649  			validateReplace: false,
   650  			validateSetDiffs: &SetDiff{
   651  				Before: SetDiffEntry{
   652  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   653  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, true),
   654  					},
   655  					Action:  plans.Delete,
   656  					Replace: false,
   657  				},
   658  				After: SetDiffEntry{
   659  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   660  						"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, true),
   661  					},
   662  					Action:  plans.Create,
   663  					Replace: false,
   664  				},
   665  			},
   666  		},
   667  		"update_includes_relevant_attributes": {
   668  			input: structured.Change{
   669  				Before: map[string]interface{}{
   670  					"attribute_one": "old_one",
   671  					"attribute_two": "old_two",
   672  				},
   673  				After: map[string]interface{}{
   674  					"attribute_one": "new_one",
   675  					"attribute_two": "new_two",
   676  				},
   677  				RelevantAttributes: &attribute_path.PathMatcher{
   678  					Paths: [][]interface{}{
   679  						{"attribute_one"},
   680  					},
   681  				},
   682  			},
   683  			attributes: map[string]cty.Type{
   684  				"attribute_one": cty.String,
   685  				"attribute_two": cty.String,
   686  			},
   687  			validateDiffs: map[string]renderers.ValidateDiffFunction{
   688  				"attribute_one": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false),
   689  				"attribute_two": renderers.ValidatePrimitive("old_two", "old_two", plans.NoOp, false),
   690  			},
   691  			validateList: renderers.ValidateList([]renderers.ValidateDiffFunction{
   692  				renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
   693  					// Lists are a bit special, and in this case is actually
   694  					// going to ignore the relevant attributes. This is
   695  					// deliberate. See the comments in list.go for an
   696  					// explanation.
   697  					"attribute_one": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false),
   698  					"attribute_two": renderers.ValidatePrimitive("old_two", "new_two", plans.Update, false),
   699  				}, plans.Update, false),
   700  			}, plans.Update, false),
   701  			validateAction:  plans.Update,
   702  			validateReplace: false,
   703  			validateSetDiffs: &SetDiff{
   704  				Before: SetDiffEntry{
   705  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   706  						"attribute_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
   707  						"attribute_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
   708  					},
   709  					Action:  plans.Delete,
   710  					Replace: false,
   711  				},
   712  				After: SetDiffEntry{
   713  					ObjectDiff: map[string]renderers.ValidateDiffFunction{
   714  						"attribute_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
   715  						"attribute_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
   716  					},
   717  					Action:  plans.Create,
   718  					Replace: false,
   719  				},
   720  			},
   721  		},
   722  	}
   723  
   724  	for name, tmp := range tcs {
   725  		tc := tmp
   726  
   727  		// Let's set some default values on the input.
   728  		if tc.input.RelevantAttributes == nil {
   729  			tc.input.RelevantAttributes = attribute_path.AlwaysMatcher()
   730  		}
   731  		if tc.input.ReplacePaths == nil {
   732  			tc.input.ReplacePaths = &attribute_path.PathMatcher{}
   733  		}
   734  
   735  		collectionDefaultAction := plans.Update
   736  		if name == "ignores_unset_fields" {
   737  			// Special case for this test, as it is the only one that doesn't
   738  			// have the collection types return an update.
   739  			collectionDefaultAction = plans.NoOp
   740  		}
   741  
   742  		t.Run(name, func(t *testing.T) {
   743  			t.Run("object", func(t *testing.T) {
   744  				attribute := &jsonprovider.Attribute{
   745  					AttributeType: unmarshalType(t, cty.Object(tc.attributes)),
   746  				}
   747  
   748  				if tc.validateObject != nil {
   749  					tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute))
   750  					return
   751  				}
   752  
   753  				if tc.validateSingleDiff != nil {
   754  					tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute))
   755  					return
   756  				}
   757  
   758  				validate := renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace)
   759  				validate(t, ComputeDiffForAttribute(tc.input, attribute))
   760  			})
   761  
   762  			t.Run("map", func(t *testing.T) {
   763  				attribute := &jsonprovider.Attribute{
   764  					AttributeType: unmarshalType(t, cty.Map(cty.Object(tc.attributes))),
   765  				}
   766  
   767  				input := wrapChangeInMap(tc.input)
   768  
   769  				if tc.validateObject != nil {
   770  					validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
   771  						"element": tc.validateObject,
   772  					}, collectionDefaultAction, false)
   773  					validate(t, ComputeDiffForAttribute(input, attribute))
   774  					return
   775  				}
   776  
   777  				if tc.validateSingleDiff != nil {
   778  					validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
   779  						"element": tc.validateSingleDiff,
   780  					}, collectionDefaultAction, false)
   781  					validate(t, ComputeDiffForAttribute(input, attribute))
   782  					return
   783  				}
   784  
   785  				validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
   786  					"element": renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
   787  				}, collectionDefaultAction, false)
   788  				validate(t, ComputeDiffForAttribute(input, attribute))
   789  			})
   790  
   791  			t.Run("list", func(t *testing.T) {
   792  				attribute := &jsonprovider.Attribute{
   793  					AttributeType: unmarshalType(t, cty.List(cty.Object(tc.attributes))),
   794  				}
   795  
   796  				input := wrapChangeInSlice(tc.input)
   797  
   798  				if tc.validateList != nil {
   799  					tc.validateList(t, ComputeDiffForAttribute(input, attribute))
   800  					return
   801  				}
   802  
   803  				if tc.validateObject != nil {
   804  					validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
   805  						tc.validateObject,
   806  					}, collectionDefaultAction, false)
   807  					validate(t, ComputeDiffForAttribute(input, attribute))
   808  					return
   809  				}
   810  
   811  				if tc.validateSingleDiff != nil {
   812  					validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
   813  						tc.validateSingleDiff,
   814  					}, collectionDefaultAction, false)
   815  					validate(t, ComputeDiffForAttribute(input, attribute))
   816  					return
   817  				}
   818  
   819  				validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
   820  					renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
   821  				}, collectionDefaultAction, false)
   822  				validate(t, ComputeDiffForAttribute(input, attribute))
   823  			})
   824  
   825  			t.Run("set", func(t *testing.T) {
   826  				attribute := &jsonprovider.Attribute{
   827  					AttributeType: unmarshalType(t, cty.Set(cty.Object(tc.attributes))),
   828  				}
   829  
   830  				input := wrapChangeInSlice(tc.input)
   831  
   832  				if tc.validateSetDiffs != nil {
   833  					validate := renderers.ValidateSet(func() []renderers.ValidateDiffFunction {
   834  						var ret []renderers.ValidateDiffFunction
   835  						ret = append(ret, tc.validateSetDiffs.Before.Validate(renderers.ValidateObject))
   836  						ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateObject))
   837  						return ret
   838  					}(), collectionDefaultAction, false)
   839  					validate(t, ComputeDiffForAttribute(input, attribute))
   840  					return
   841  				}
   842  
   843  				if tc.validateObject != nil {
   844  					validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
   845  						tc.validateObject,
   846  					}, collectionDefaultAction, false)
   847  					validate(t, ComputeDiffForAttribute(input, attribute))
   848  					return
   849  				}
   850  
   851  				if tc.validateSingleDiff != nil {
   852  					validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
   853  						tc.validateSingleDiff,
   854  					}, collectionDefaultAction, false)
   855  					validate(t, ComputeDiffForAttribute(input, attribute))
   856  					return
   857  				}
   858  
   859  				validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
   860  					renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
   861  				}, collectionDefaultAction, false)
   862  				validate(t, ComputeDiffForAttribute(input, attribute))
   863  			})
   864  		})
   865  
   866  		t.Run(fmt.Sprintf("nested_%s", name), func(t *testing.T) {
   867  			t.Run("object", func(t *testing.T) {
   868  				attribute := &jsonprovider.Attribute{
   869  					AttributeNestedType: &jsonprovider.NestedType{
   870  						Attributes: func() map[string]*jsonprovider.Attribute {
   871  							attributes := make(map[string]*jsonprovider.Attribute)
   872  							for key, attribute := range tc.attributes {
   873  								attributes[key] = &jsonprovider.Attribute{
   874  									AttributeType: unmarshalType(t, attribute),
   875  								}
   876  							}
   877  							return attributes
   878  						}(),
   879  						NestingMode: "single",
   880  					},
   881  				}
   882  
   883  				if tc.validateNestedObject != nil {
   884  					tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute))
   885  					return
   886  				}
   887  
   888  				if tc.validateSingleDiff != nil {
   889  					tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute))
   890  					return
   891  				}
   892  
   893  				validate := renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace)
   894  				validate(t, ComputeDiffForAttribute(tc.input, attribute))
   895  			})
   896  
   897  			t.Run("map", func(t *testing.T) {
   898  				attribute := &jsonprovider.Attribute{
   899  					AttributeNestedType: &jsonprovider.NestedType{
   900  						Attributes: func() map[string]*jsonprovider.Attribute {
   901  							attributes := make(map[string]*jsonprovider.Attribute)
   902  							for key, attribute := range tc.attributes {
   903  								attributes[key] = &jsonprovider.Attribute{
   904  									AttributeType: unmarshalType(t, attribute),
   905  								}
   906  							}
   907  							return attributes
   908  						}(),
   909  						NestingMode: "map",
   910  					},
   911  				}
   912  
   913  				input := wrapChangeInMap(tc.input)
   914  
   915  				if tc.validateNestedObject != nil {
   916  					validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
   917  						"element": tc.validateNestedObject,
   918  					}, collectionDefaultAction, false)
   919  					validate(t, ComputeDiffForAttribute(input, attribute))
   920  					return
   921  				}
   922  
   923  				if tc.validateSingleDiff != nil {
   924  					validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
   925  						"element": tc.validateSingleDiff,
   926  					}, collectionDefaultAction, false)
   927  					validate(t, ComputeDiffForAttribute(input, attribute))
   928  					return
   929  				}
   930  
   931  				validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
   932  					"element": renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
   933  				}, collectionDefaultAction, false)
   934  				validate(t, ComputeDiffForAttribute(input, attribute))
   935  			})
   936  
   937  			t.Run("list", func(t *testing.T) {
   938  				attribute := &jsonprovider.Attribute{
   939  					AttributeNestedType: &jsonprovider.NestedType{
   940  						Attributes: func() map[string]*jsonprovider.Attribute {
   941  							attributes := make(map[string]*jsonprovider.Attribute)
   942  							for key, attribute := range tc.attributes {
   943  								attributes[key] = &jsonprovider.Attribute{
   944  									AttributeType: unmarshalType(t, attribute),
   945  								}
   946  							}
   947  							return attributes
   948  						}(),
   949  						NestingMode: "list",
   950  					},
   951  				}
   952  
   953  				input := wrapChangeInSlice(tc.input)
   954  
   955  				if tc.validateNestedObject != nil {
   956  					validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{
   957  						tc.validateNestedObject,
   958  					}, collectionDefaultAction, false)
   959  					validate(t, ComputeDiffForAttribute(input, attribute))
   960  					return
   961  				}
   962  
   963  				if tc.validateSingleDiff != nil {
   964  					validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{
   965  						tc.validateSingleDiff,
   966  					}, collectionDefaultAction, false)
   967  					validate(t, ComputeDiffForAttribute(input, attribute))
   968  					return
   969  				}
   970  
   971  				validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{
   972  					renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
   973  				}, collectionDefaultAction, false)
   974  				validate(t, ComputeDiffForAttribute(input, attribute))
   975  			})
   976  
   977  			t.Run("set", func(t *testing.T) {
   978  				attribute := &jsonprovider.Attribute{
   979  					AttributeNestedType: &jsonprovider.NestedType{
   980  						Attributes: func() map[string]*jsonprovider.Attribute {
   981  							attributes := make(map[string]*jsonprovider.Attribute)
   982  							for key, attribute := range tc.attributes {
   983  								attributes[key] = &jsonprovider.Attribute{
   984  									AttributeType: unmarshalType(t, attribute),
   985  								}
   986  							}
   987  							return attributes
   988  						}(),
   989  						NestingMode: "set",
   990  					},
   991  				}
   992  
   993  				input := wrapChangeInSlice(tc.input)
   994  
   995  				if tc.validateSetDiffs != nil {
   996  					validate := renderers.ValidateSet(func() []renderers.ValidateDiffFunction {
   997  						var ret []renderers.ValidateDiffFunction
   998  						ret = append(ret, tc.validateSetDiffs.Before.Validate(renderers.ValidateNestedObject))
   999  						ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateNestedObject))
  1000  						return ret
  1001  					}(), collectionDefaultAction, false)
  1002  					validate(t, ComputeDiffForAttribute(input, attribute))
  1003  					return
  1004  				}
  1005  
  1006  				if tc.validateNestedObject != nil {
  1007  					validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
  1008  						tc.validateNestedObject,
  1009  					}, collectionDefaultAction, false)
  1010  					validate(t, ComputeDiffForAttribute(input, attribute))
  1011  					return
  1012  				}
  1013  
  1014  				if tc.validateSingleDiff != nil {
  1015  					validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
  1016  						tc.validateSingleDiff,
  1017  					}, collectionDefaultAction, false)
  1018  					validate(t, ComputeDiffForAttribute(input, attribute))
  1019  					return
  1020  				}
  1021  
  1022  				validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
  1023  					renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace),
  1024  				}, collectionDefaultAction, false)
  1025  				validate(t, ComputeDiffForAttribute(input, attribute))
  1026  			})
  1027  		})
  1028  	}
  1029  }
  1030  
  1031  func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) {
  1032  	// This function tests manipulating simple attributes and blocks within
  1033  	// blocks. It automatically tests these operations within the contexts of
  1034  	// different block types.
  1035  
  1036  	tcs := map[string]struct {
  1037  		before      interface{}
  1038  		after       interface{}
  1039  		block       *jsonprovider.Block
  1040  		validate    renderers.ValidateDiffFunction
  1041  		validateSet []renderers.ValidateDiffFunction
  1042  	}{
  1043  		"create_attribute": {
  1044  			before: map[string]interface{}{},
  1045  			after: map[string]interface{}{
  1046  				"attribute_one": "new",
  1047  			},
  1048  			block: &jsonprovider.Block{
  1049  				Attributes: map[string]*jsonprovider.Attribute{
  1050  					"attribute_one": {
  1051  						AttributeType: unmarshalType(t, cty.String),
  1052  					},
  1053  				},
  1054  			},
  1055  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1056  				"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1057  			}, nil, nil, nil, nil, plans.Update, false),
  1058  			validateSet: []renderers.ValidateDiffFunction{
  1059  				renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Delete, false),
  1060  				renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1061  					"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1062  				}, nil, nil, nil, nil, plans.Create, false),
  1063  			},
  1064  		},
  1065  		"update_attribute": {
  1066  			before: map[string]interface{}{
  1067  				"attribute_one": "old",
  1068  			},
  1069  			after: map[string]interface{}{
  1070  				"attribute_one": "new",
  1071  			},
  1072  			block: &jsonprovider.Block{
  1073  				Attributes: map[string]*jsonprovider.Attribute{
  1074  					"attribute_one": {
  1075  						AttributeType: unmarshalType(t, cty.String),
  1076  					},
  1077  				},
  1078  			},
  1079  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1080  				"attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false),
  1081  			}, nil, nil, nil, nil, plans.Update, false),
  1082  			validateSet: []renderers.ValidateDiffFunction{
  1083  				renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1084  					"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1085  				}, nil, nil, nil, nil, plans.Delete, false),
  1086  				renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1087  					"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1088  				}, nil, nil, nil, nil, plans.Create, false),
  1089  			},
  1090  		},
  1091  		"delete_attribute": {
  1092  			before: map[string]interface{}{
  1093  				"attribute_one": "old",
  1094  			},
  1095  			after: map[string]interface{}{},
  1096  			block: &jsonprovider.Block{
  1097  				Attributes: map[string]*jsonprovider.Attribute{
  1098  					"attribute_one": {
  1099  						AttributeType: unmarshalType(t, cty.String),
  1100  					},
  1101  				},
  1102  			},
  1103  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1104  				"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1105  			}, nil, nil, nil, nil, plans.Update, false),
  1106  			validateSet: []renderers.ValidateDiffFunction{
  1107  				renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1108  					"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1109  				}, nil, nil, nil, nil, plans.Delete, false),
  1110  				renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Create, false),
  1111  			},
  1112  		},
  1113  		"create_block": {
  1114  			before: map[string]interface{}{},
  1115  			after: map[string]interface{}{
  1116  				"block_one": map[string]interface{}{
  1117  					"attribute_one": "new",
  1118  				},
  1119  			},
  1120  			block: &jsonprovider.Block{
  1121  				BlockTypes: map[string]*jsonprovider.BlockType{
  1122  					"block_one": {
  1123  						Block: &jsonprovider.Block{
  1124  							Attributes: map[string]*jsonprovider.Attribute{
  1125  								"attribute_one": {
  1126  									AttributeType: unmarshalType(t, cty.String),
  1127  								},
  1128  							},
  1129  						},
  1130  						NestingMode: "single",
  1131  					},
  1132  				},
  1133  			},
  1134  			validate: renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1135  				"block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1136  					"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1137  				}, nil, nil, nil, nil, plans.Create, false),
  1138  			}, nil, nil, nil, plans.Update, false),
  1139  			validateSet: []renderers.ValidateDiffFunction{
  1140  				renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Delete, false),
  1141  				renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1142  					"block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1143  						"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1144  					}, nil, nil, nil, nil, plans.Create, false),
  1145  				}, nil, nil, nil, plans.Create, false),
  1146  			},
  1147  		},
  1148  		"update_block": {
  1149  			before: map[string]interface{}{
  1150  				"block_one": map[string]interface{}{
  1151  					"attribute_one": "old",
  1152  				},
  1153  			},
  1154  			after: map[string]interface{}{
  1155  				"block_one": map[string]interface{}{
  1156  					"attribute_one": "new",
  1157  				},
  1158  			},
  1159  			block: &jsonprovider.Block{
  1160  				BlockTypes: map[string]*jsonprovider.BlockType{
  1161  					"block_one": {
  1162  						Block: &jsonprovider.Block{
  1163  							Attributes: map[string]*jsonprovider.Attribute{
  1164  								"attribute_one": {
  1165  									AttributeType: unmarshalType(t, cty.String),
  1166  								},
  1167  							},
  1168  						},
  1169  						NestingMode: "single",
  1170  					},
  1171  				},
  1172  			},
  1173  			validate: renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1174  				"block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1175  					"attribute_one": renderers.ValidatePrimitive("old", "new", plans.Update, false),
  1176  				}, nil, nil, nil, nil, plans.Update, false),
  1177  			}, nil, nil, nil, plans.Update, false),
  1178  			validateSet: []renderers.ValidateDiffFunction{
  1179  				renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1180  					"block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1181  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1182  					}, nil, nil, nil, nil, plans.Delete, false),
  1183  				}, nil, nil, nil, plans.Delete, false),
  1184  				renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1185  					"block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1186  						"attribute_one": renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1187  					}, nil, nil, nil, nil, plans.Create, false),
  1188  				}, nil, nil, nil, plans.Create, false),
  1189  			},
  1190  		},
  1191  		"delete_block": {
  1192  			before: map[string]interface{}{
  1193  				"block_one": map[string]interface{}{
  1194  					"attribute_one": "old",
  1195  				},
  1196  			},
  1197  			after: map[string]interface{}{},
  1198  			block: &jsonprovider.Block{
  1199  				BlockTypes: map[string]*jsonprovider.BlockType{
  1200  					"block_one": {
  1201  						Block: &jsonprovider.Block{
  1202  							Attributes: map[string]*jsonprovider.Attribute{
  1203  								"attribute_one": {
  1204  									AttributeType: unmarshalType(t, cty.String),
  1205  								},
  1206  							},
  1207  						},
  1208  						NestingMode: "single",
  1209  					},
  1210  				},
  1211  			},
  1212  			validate: renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1213  				"block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1214  					"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1215  				}, nil, nil, nil, nil, plans.Delete, false),
  1216  			}, nil, nil, nil, plans.Update, false),
  1217  			validateSet: []renderers.ValidateDiffFunction{
  1218  				renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1219  					"block_one": renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  1220  						"attribute_one": renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1221  					}, nil, nil, nil, nil, plans.Delete, false),
  1222  				}, nil, nil, nil, plans.Delete, false),
  1223  				renderers.ValidateBlock(nil, nil, nil, nil, nil, plans.Create, false),
  1224  			},
  1225  		},
  1226  	}
  1227  	for name, tmp := range tcs {
  1228  		tc := tmp
  1229  
  1230  		t.Run(name, func(t *testing.T) {
  1231  			t.Run("single", func(t *testing.T) {
  1232  				input := structured.Change{
  1233  					Before: map[string]interface{}{
  1234  						"block_type": tc.before,
  1235  					},
  1236  					After: map[string]interface{}{
  1237  						"block_type": tc.after,
  1238  					},
  1239  					ReplacePaths:       &attribute_path.PathMatcher{},
  1240  					RelevantAttributes: attribute_path.AlwaysMatcher(),
  1241  				}
  1242  
  1243  				block := &jsonprovider.Block{
  1244  					BlockTypes: map[string]*jsonprovider.BlockType{
  1245  						"block_type": {
  1246  							Block:       tc.block,
  1247  							NestingMode: "single",
  1248  						},
  1249  					},
  1250  				}
  1251  
  1252  				validate := renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{
  1253  					"block_type": tc.validate,
  1254  				}, nil, nil, nil, plans.Update, false)
  1255  				validate(t, ComputeDiffForBlock(input, block))
  1256  			})
  1257  			t.Run("map", func(t *testing.T) {
  1258  				input := structured.Change{
  1259  					Before: map[string]interface{}{
  1260  						"block_type": map[string]interface{}{
  1261  							"one": tc.before,
  1262  						},
  1263  					},
  1264  					After: map[string]interface{}{
  1265  						"block_type": map[string]interface{}{
  1266  							"one": tc.after,
  1267  						},
  1268  					},
  1269  					ReplacePaths:       &attribute_path.PathMatcher{},
  1270  					RelevantAttributes: attribute_path.AlwaysMatcher(),
  1271  				}
  1272  
  1273  				block := &jsonprovider.Block{
  1274  					BlockTypes: map[string]*jsonprovider.BlockType{
  1275  						"block_type": {
  1276  							Block:       tc.block,
  1277  							NestingMode: "map",
  1278  						},
  1279  					},
  1280  				}
  1281  
  1282  				validate := renderers.ValidateBlock(nil, nil, nil, map[string]map[string]renderers.ValidateDiffFunction{
  1283  					"block_type": {
  1284  						"one": tc.validate,
  1285  					},
  1286  				}, nil, plans.Update, false)
  1287  				validate(t, ComputeDiffForBlock(input, block))
  1288  			})
  1289  			t.Run("list", func(t *testing.T) {
  1290  				input := structured.Change{
  1291  					Before: map[string]interface{}{
  1292  						"block_type": []interface{}{
  1293  							tc.before,
  1294  						},
  1295  					},
  1296  					After: map[string]interface{}{
  1297  						"block_type": []interface{}{
  1298  							tc.after,
  1299  						},
  1300  					},
  1301  					ReplacePaths:       &attribute_path.PathMatcher{},
  1302  					RelevantAttributes: attribute_path.AlwaysMatcher(),
  1303  				}
  1304  
  1305  				block := &jsonprovider.Block{
  1306  					BlockTypes: map[string]*jsonprovider.BlockType{
  1307  						"block_type": {
  1308  							Block:       tc.block,
  1309  							NestingMode: "list",
  1310  						},
  1311  					},
  1312  				}
  1313  
  1314  				validate := renderers.ValidateBlock(nil, nil, map[string][]renderers.ValidateDiffFunction{
  1315  					"block_type": {
  1316  						tc.validate,
  1317  					},
  1318  				}, nil, nil, plans.Update, false)
  1319  				validate(t, ComputeDiffForBlock(input, block))
  1320  			})
  1321  			t.Run("set", func(t *testing.T) {
  1322  				input := structured.Change{
  1323  					Before: map[string]interface{}{
  1324  						"block_type": []interface{}{
  1325  							tc.before,
  1326  						},
  1327  					},
  1328  					After: map[string]interface{}{
  1329  						"block_type": []interface{}{
  1330  							tc.after,
  1331  						},
  1332  					},
  1333  					ReplacePaths:       &attribute_path.PathMatcher{},
  1334  					RelevantAttributes: attribute_path.AlwaysMatcher(),
  1335  				}
  1336  
  1337  				block := &jsonprovider.Block{
  1338  					BlockTypes: map[string]*jsonprovider.BlockType{
  1339  						"block_type": {
  1340  							Block:       tc.block,
  1341  							NestingMode: "set",
  1342  						},
  1343  					},
  1344  				}
  1345  
  1346  				validate := renderers.ValidateBlock(nil, nil, nil, nil, map[string][]renderers.ValidateDiffFunction{
  1347  					"block_type": func() []renderers.ValidateDiffFunction {
  1348  						if tc.validateSet != nil {
  1349  							return tc.validateSet
  1350  						}
  1351  						return []renderers.ValidateDiffFunction{tc.validate}
  1352  					}(),
  1353  				}, plans.Update, false)
  1354  				validate(t, ComputeDiffForBlock(input, block))
  1355  			})
  1356  		})
  1357  	}
  1358  }
  1359  
  1360  func TestValue_Outputs(t *testing.T) {
  1361  	tcs := map[string]struct {
  1362  		input        structured.Change
  1363  		validateDiff renderers.ValidateDiffFunction
  1364  	}{
  1365  		"primitive_create": {
  1366  			input: structured.Change{
  1367  				Before: nil,
  1368  				After:  "new",
  1369  			},
  1370  			validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1371  		},
  1372  		"object_create": {
  1373  			input: structured.Change{
  1374  				Before: nil,
  1375  				After: map[string]interface{}{
  1376  					"element_one": "new_one",
  1377  					"element_two": "new_two",
  1378  				},
  1379  			},
  1380  			validateDiff: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  1381  				"element_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
  1382  				"element_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
  1383  			}, plans.Create, false),
  1384  		},
  1385  		"list_create": {
  1386  			input: structured.Change{
  1387  				Before: nil,
  1388  				After: []interface{}{
  1389  					"new_one",
  1390  					"new_two",
  1391  				},
  1392  			},
  1393  			validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{
  1394  				renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
  1395  				renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
  1396  			}, plans.Create, false),
  1397  		},
  1398  		"primitive_update": {
  1399  			input: structured.Change{
  1400  				Before: "old",
  1401  				After:  "new",
  1402  			},
  1403  			validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, false),
  1404  		},
  1405  		"object_update": {
  1406  			input: structured.Change{
  1407  				Before: map[string]interface{}{
  1408  					"element_one": "old_one",
  1409  					"element_two": "old_two",
  1410  				},
  1411  				After: map[string]interface{}{
  1412  					"element_one": "new_one",
  1413  					"element_two": "new_two",
  1414  				},
  1415  			},
  1416  			validateDiff: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  1417  				"element_one": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false),
  1418  				"element_two": renderers.ValidatePrimitive("old_two", "new_two", plans.Update, false),
  1419  			}, plans.Update, false),
  1420  		},
  1421  		"list_update": {
  1422  			input: structured.Change{
  1423  				Before: []interface{}{
  1424  					"old_one",
  1425  					"old_two",
  1426  				},
  1427  				After: []interface{}{
  1428  					"new_one",
  1429  					"new_two",
  1430  				},
  1431  			},
  1432  			validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{
  1433  				renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
  1434  				renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
  1435  				renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
  1436  				renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
  1437  			}, plans.Update, false),
  1438  		},
  1439  		"primitive_delete": {
  1440  			input: structured.Change{
  1441  				Before: "old",
  1442  				After:  nil,
  1443  			},
  1444  			validateDiff: renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1445  		},
  1446  		"object_delete": {
  1447  			input: structured.Change{
  1448  				Before: map[string]interface{}{
  1449  					"element_one": "old_one",
  1450  					"element_two": "old_two",
  1451  				},
  1452  				After: nil,
  1453  			},
  1454  			validateDiff: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  1455  				"element_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
  1456  				"element_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
  1457  			}, plans.Delete, false),
  1458  		},
  1459  		"list_delete": {
  1460  			input: structured.Change{
  1461  				Before: []interface{}{
  1462  					"old_one",
  1463  					"old_two",
  1464  				},
  1465  				After: nil,
  1466  			},
  1467  			validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{
  1468  				renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
  1469  				renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
  1470  			}, plans.Delete, false),
  1471  		},
  1472  		"primitive_to_list": {
  1473  			input: structured.Change{
  1474  				Before: "old",
  1475  				After: []interface{}{
  1476  					"new_one",
  1477  					"new_two",
  1478  				},
  1479  			},
  1480  			validateDiff: renderers.ValidateTypeChange(
  1481  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1482  				renderers.ValidateList([]renderers.ValidateDiffFunction{
  1483  					renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
  1484  					renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
  1485  				}, plans.Create, false), plans.Update, false),
  1486  		},
  1487  		"primitive_to_object": {
  1488  			input: structured.Change{
  1489  				Before: "old",
  1490  				After: map[string]interface{}{
  1491  					"element_one": "new_one",
  1492  					"element_two": "new_two",
  1493  				},
  1494  			},
  1495  			validateDiff: renderers.ValidateTypeChange(
  1496  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1497  				renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  1498  					"element_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
  1499  					"element_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
  1500  				}, plans.Create, false), plans.Update, false),
  1501  		},
  1502  		"list_to_primitive": {
  1503  			input: structured.Change{
  1504  				Before: []interface{}{
  1505  					"old_one",
  1506  					"old_two",
  1507  				},
  1508  				After: "new",
  1509  			},
  1510  			validateDiff: renderers.ValidateTypeChange(
  1511  				renderers.ValidateList([]renderers.ValidateDiffFunction{
  1512  					renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
  1513  					renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
  1514  				}, plans.Delete, false),
  1515  				renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1516  				plans.Update, false),
  1517  		},
  1518  		"list_to_object": {
  1519  			input: structured.Change{
  1520  				Before: []interface{}{
  1521  					"old_one",
  1522  					"old_two",
  1523  				},
  1524  				After: map[string]interface{}{
  1525  					"element_one": "new_one",
  1526  					"element_two": "new_two",
  1527  				},
  1528  			},
  1529  			validateDiff: renderers.ValidateTypeChange(
  1530  				renderers.ValidateList([]renderers.ValidateDiffFunction{
  1531  					renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
  1532  					renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
  1533  				}, plans.Delete, false),
  1534  				renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  1535  					"element_one": renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
  1536  					"element_two": renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
  1537  				}, plans.Create, false), plans.Update, false),
  1538  		},
  1539  		"object_to_primitive": {
  1540  			input: structured.Change{
  1541  				Before: map[string]interface{}{
  1542  					"element_one": "old_one",
  1543  					"element_two": "old_two",
  1544  				},
  1545  				After: "new",
  1546  			},
  1547  			validateDiff: renderers.ValidateTypeChange(
  1548  				renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  1549  					"element_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
  1550  					"element_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
  1551  				}, plans.Delete, false),
  1552  				renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1553  				plans.Update, false),
  1554  		},
  1555  		"object_to_list": {
  1556  			input: structured.Change{
  1557  				Before: map[string]interface{}{
  1558  					"element_one": "old_one",
  1559  					"element_two": "old_two",
  1560  				},
  1561  				After: []interface{}{
  1562  					"new_one",
  1563  					"new_two",
  1564  				},
  1565  			},
  1566  			validateDiff: renderers.ValidateTypeChange(
  1567  				renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  1568  					"element_one": renderers.ValidatePrimitive("old_one", nil, plans.Delete, false),
  1569  					"element_two": renderers.ValidatePrimitive("old_two", nil, plans.Delete, false),
  1570  				}, plans.Delete, false),
  1571  				renderers.ValidateList([]renderers.ValidateDiffFunction{
  1572  					renderers.ValidatePrimitive(nil, "new_one", plans.Create, false),
  1573  					renderers.ValidatePrimitive(nil, "new_two", plans.Create, false),
  1574  				}, plans.Create, false), plans.Update, false),
  1575  		},
  1576  	}
  1577  
  1578  	for name, tc := range tcs {
  1579  
  1580  		// Let's set some default values on the input.
  1581  		if tc.input.RelevantAttributes == nil {
  1582  			tc.input.RelevantAttributes = attribute_path.AlwaysMatcher()
  1583  		}
  1584  		if tc.input.ReplacePaths == nil {
  1585  			tc.input.ReplacePaths = &attribute_path.PathMatcher{}
  1586  		}
  1587  
  1588  		t.Run(name, func(t *testing.T) {
  1589  			tc.validateDiff(t, ComputeDiffForOutput(tc.input))
  1590  		})
  1591  	}
  1592  }
  1593  
  1594  func TestValue_PrimitiveAttributes(t *testing.T) {
  1595  	// This function tests manipulating primitives: creating, deleting and
  1596  	// updating. It also automatically tests these operations within the
  1597  	// contexts of collections.
  1598  
  1599  	tcs := map[string]struct {
  1600  		input              structured.Change
  1601  		attribute          cty.Type
  1602  		validateDiff       renderers.ValidateDiffFunction
  1603  		validateSliceDiffs []renderers.ValidateDiffFunction // Lists are special in some cases.
  1604  	}{
  1605  		"primitive_create": {
  1606  			input: structured.Change{
  1607  				After: "new",
  1608  			},
  1609  			attribute:    cty.String,
  1610  			validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1611  		},
  1612  		"primitive_delete": {
  1613  			input: structured.Change{
  1614  				Before: "old",
  1615  			},
  1616  			attribute:    cty.String,
  1617  			validateDiff: renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1618  		},
  1619  		"primitive_update": {
  1620  			input: structured.Change{
  1621  				Before: "old",
  1622  				After:  "new",
  1623  			},
  1624  			attribute:    cty.String,
  1625  			validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, false),
  1626  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1627  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1628  				renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1629  			},
  1630  		},
  1631  		"primitive_set_explicit_null": {
  1632  			input: structured.Change{
  1633  				Before:        "old",
  1634  				After:         nil,
  1635  				AfterExplicit: true,
  1636  			},
  1637  			attribute:    cty.String,
  1638  			validateDiff: renderers.ValidatePrimitive("old", nil, plans.Update, false),
  1639  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1640  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1641  				renderers.ValidatePrimitive(nil, nil, plans.Create, false),
  1642  			},
  1643  		},
  1644  		"primitive_unset_explicit_null": {
  1645  			input: structured.Change{
  1646  				BeforeExplicit: true,
  1647  				Before:         nil,
  1648  				After:          "new",
  1649  			},
  1650  			attribute:    cty.String,
  1651  			validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Update, false),
  1652  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1653  				renderers.ValidatePrimitive(nil, nil, plans.Delete, false),
  1654  				renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1655  			},
  1656  		},
  1657  		"primitive_create_sensitive": {
  1658  			input: structured.Change{
  1659  				Before:         nil,
  1660  				After:          "new",
  1661  				AfterSensitive: true,
  1662  			},
  1663  			attribute:    cty.String,
  1664  			validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false),
  1665  		},
  1666  		"primitive_delete_sensitive": {
  1667  			input: structured.Change{
  1668  				Before:          "old",
  1669  				BeforeSensitive: true,
  1670  				After:           nil,
  1671  			},
  1672  			attribute:    cty.String,
  1673  			validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false),
  1674  		},
  1675  		"primitive_update_sensitive": {
  1676  			input: structured.Change{
  1677  				Before:          "old",
  1678  				BeforeSensitive: true,
  1679  				After:           "new",
  1680  				AfterSensitive:  true,
  1681  			},
  1682  			attribute:    cty.String,
  1683  			validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive("old", "new", plans.Update, false), true, true, plans.Update, false),
  1684  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1685  				renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false),
  1686  				renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false),
  1687  			},
  1688  		},
  1689  		"primitive_create_computed": {
  1690  			input: structured.Change{
  1691  				Before:  nil,
  1692  				After:   nil,
  1693  				Unknown: true,
  1694  			},
  1695  			attribute:    cty.String,
  1696  			validateDiff: renderers.ValidateUnknown(nil, plans.Create, false),
  1697  		},
  1698  		"primitive_update_computed": {
  1699  			input: structured.Change{
  1700  				Before:  "old",
  1701  				After:   nil,
  1702  				Unknown: true,
  1703  			},
  1704  			attribute:    cty.String,
  1705  			validateDiff: renderers.ValidateUnknown(renderers.ValidatePrimitive("old", nil, plans.Delete, false), plans.Update, false),
  1706  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1707  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1708  				renderers.ValidateUnknown(nil, plans.Create, false),
  1709  			},
  1710  		},
  1711  		"primitive_update_replace": {
  1712  			input: structured.Change{
  1713  				Before: "old",
  1714  				After:  "new",
  1715  				ReplacePaths: &attribute_path.PathMatcher{
  1716  					Paths: [][]interface{}{
  1717  						{}, // An empty path suggests replace should be true.
  1718  					},
  1719  				},
  1720  			},
  1721  			attribute:    cty.String,
  1722  			validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, true),
  1723  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1724  				renderers.ValidatePrimitive("old", nil, plans.Delete, true),
  1725  				renderers.ValidatePrimitive(nil, "new", plans.Create, true),
  1726  			},
  1727  		},
  1728  		"noop": {
  1729  			input: structured.Change{
  1730  				Before: "old",
  1731  				After:  "old",
  1732  			},
  1733  			attribute:    cty.String,
  1734  			validateDiff: renderers.ValidatePrimitive("old", "old", plans.NoOp, false),
  1735  		},
  1736  		"dynamic": {
  1737  			input: structured.Change{
  1738  				Before: "old",
  1739  				After:  "new",
  1740  			},
  1741  			attribute:    cty.DynamicPseudoType,
  1742  			validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, false),
  1743  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1744  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1745  				renderers.ValidatePrimitive(nil, "new", plans.Create, false),
  1746  			},
  1747  		},
  1748  		"dynamic_type_change": {
  1749  			input: structured.Change{
  1750  				Before: "old",
  1751  				After:  json.Number("4"),
  1752  			},
  1753  			attribute: cty.DynamicPseudoType,
  1754  			validateDiff: renderers.ValidateTypeChange(
  1755  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1756  				renderers.ValidatePrimitive(nil, json.Number("4"), plans.Create, false),
  1757  				plans.Update, false),
  1758  			validateSliceDiffs: []renderers.ValidateDiffFunction{
  1759  				renderers.ValidatePrimitive("old", nil, plans.Delete, false),
  1760  				renderers.ValidatePrimitive(nil, json.Number("4"), plans.Create, false),
  1761  			},
  1762  		},
  1763  	}
  1764  	for name, tmp := range tcs {
  1765  		tc := tmp
  1766  
  1767  		// Let's set some default values on the input.
  1768  		if tc.input.RelevantAttributes == nil {
  1769  			tc.input.RelevantAttributes = attribute_path.AlwaysMatcher()
  1770  		}
  1771  		if tc.input.ReplacePaths == nil {
  1772  			tc.input.ReplacePaths = &attribute_path.PathMatcher{}
  1773  		}
  1774  
  1775  		defaultCollectionsAction := plans.Update
  1776  		if name == "noop" {
  1777  			defaultCollectionsAction = plans.NoOp
  1778  		}
  1779  
  1780  		t.Run(name, func(t *testing.T) {
  1781  			t.Run("direct", func(t *testing.T) {
  1782  				tc.validateDiff(t, ComputeDiffForAttribute(tc.input, &jsonprovider.Attribute{
  1783  					AttributeType: unmarshalType(t, tc.attribute),
  1784  				}))
  1785  			})
  1786  
  1787  			t.Run("map", func(t *testing.T) {
  1788  				input := wrapChangeInMap(tc.input)
  1789  				attribute := &jsonprovider.Attribute{
  1790  					AttributeType: unmarshalType(t, cty.Map(tc.attribute)),
  1791  				}
  1792  
  1793  				validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
  1794  					"element": tc.validateDiff,
  1795  				}, defaultCollectionsAction, false)
  1796  				validate(t, ComputeDiffForAttribute(input, attribute))
  1797  			})
  1798  
  1799  			t.Run("list", func(t *testing.T) {
  1800  				input := wrapChangeInSlice(tc.input)
  1801  				attribute := &jsonprovider.Attribute{
  1802  					AttributeType: unmarshalType(t, cty.List(tc.attribute)),
  1803  				}
  1804  
  1805  				if tc.validateSliceDiffs != nil {
  1806  					validate := renderers.ValidateList(tc.validateSliceDiffs, defaultCollectionsAction, false)
  1807  					validate(t, ComputeDiffForAttribute(input, attribute))
  1808  					return
  1809  				}
  1810  
  1811  				validate := renderers.ValidateList([]renderers.ValidateDiffFunction{
  1812  					tc.validateDiff,
  1813  				}, defaultCollectionsAction, false)
  1814  				validate(t, ComputeDiffForAttribute(input, attribute))
  1815  			})
  1816  
  1817  			t.Run("set", func(t *testing.T) {
  1818  				input := wrapChangeInSlice(tc.input)
  1819  				attribute := &jsonprovider.Attribute{
  1820  					AttributeType: unmarshalType(t, cty.Set(tc.attribute)),
  1821  				}
  1822  
  1823  				if tc.validateSliceDiffs != nil {
  1824  					validate := renderers.ValidateSet(tc.validateSliceDiffs, defaultCollectionsAction, false)
  1825  					validate(t, ComputeDiffForAttribute(input, attribute))
  1826  					return
  1827  				}
  1828  
  1829  				validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{
  1830  					tc.validateDiff,
  1831  				}, defaultCollectionsAction, false)
  1832  				validate(t, ComputeDiffForAttribute(input, attribute))
  1833  			})
  1834  		})
  1835  	}
  1836  }
  1837  
  1838  func TestValue_CollectionAttributes(t *testing.T) {
  1839  	// This function tests creating and deleting collections. Note, it does not
  1840  	// generally cover editing collections except in special cases as editing
  1841  	// collections is handled automatically by other functions.
  1842  	tcs := map[string]struct {
  1843  		input        structured.Change
  1844  		attribute    *jsonprovider.Attribute
  1845  		validateDiff renderers.ValidateDiffFunction
  1846  	}{
  1847  		"map_create_empty": {
  1848  			input: structured.Change{
  1849  				Before: nil,
  1850  				After:  map[string]interface{}{},
  1851  			},
  1852  			attribute: &jsonprovider.Attribute{
  1853  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1854  			},
  1855  			validateDiff: renderers.ValidateMap(nil, plans.Create, false),
  1856  		},
  1857  		"map_create_populated": {
  1858  			input: structured.Change{
  1859  				Before: nil,
  1860  				After: map[string]interface{}{
  1861  					"element_one": "one",
  1862  					"element_two": "two",
  1863  				},
  1864  			},
  1865  			attribute: &jsonprovider.Attribute{
  1866  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1867  			},
  1868  			validateDiff: renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
  1869  				"element_one": renderers.ValidatePrimitive(nil, "one", plans.Create, false),
  1870  				"element_two": renderers.ValidatePrimitive(nil, "two", plans.Create, false),
  1871  			}, plans.Create, false),
  1872  		},
  1873  		"map_delete_empty": {
  1874  			input: structured.Change{
  1875  				Before: map[string]interface{}{},
  1876  				After:  nil,
  1877  			},
  1878  			attribute: &jsonprovider.Attribute{
  1879  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1880  			},
  1881  			validateDiff: renderers.ValidateMap(nil, plans.Delete, false),
  1882  		},
  1883  		"map_delete_populated": {
  1884  			input: structured.Change{
  1885  				Before: map[string]interface{}{
  1886  					"element_one": "one",
  1887  					"element_two": "two",
  1888  				},
  1889  				After: nil,
  1890  			},
  1891  			attribute: &jsonprovider.Attribute{
  1892  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1893  			},
  1894  			validateDiff: renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
  1895  				"element_one": renderers.ValidatePrimitive("one", nil, plans.Delete, false),
  1896  				"element_two": renderers.ValidatePrimitive("two", nil, plans.Delete, false),
  1897  			}, plans.Delete, false),
  1898  		},
  1899  		"map_create_sensitive": {
  1900  			input: structured.Change{
  1901  				Before:         nil,
  1902  				After:          map[string]interface{}{},
  1903  				AfterSensitive: true,
  1904  			},
  1905  			attribute: &jsonprovider.Attribute{
  1906  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1907  			},
  1908  			validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(nil, plans.Create, false), false, true, plans.Create, false),
  1909  		},
  1910  		"map_update_sensitive": {
  1911  			input: structured.Change{
  1912  				Before: map[string]interface{}{
  1913  					"element": "one",
  1914  				},
  1915  				BeforeSensitive: true,
  1916  				After:           map[string]interface{}{},
  1917  				AfterSensitive:  true,
  1918  			},
  1919  			attribute: &jsonprovider.Attribute{
  1920  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1921  			},
  1922  			validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
  1923  				"element": renderers.ValidatePrimitive("one", nil, plans.Delete, false),
  1924  			}, plans.Update, false), true, true, plans.Update, false),
  1925  		},
  1926  		"map_delete_sensitive": {
  1927  			input: structured.Change{
  1928  				Before:          map[string]interface{}{},
  1929  				BeforeSensitive: true,
  1930  				After:           nil,
  1931  			},
  1932  			attribute: &jsonprovider.Attribute{
  1933  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1934  			},
  1935  			validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(nil, plans.Delete, false), true, false, plans.Delete, false),
  1936  		},
  1937  		"map_create_unknown": {
  1938  			input: structured.Change{
  1939  				Before:  nil,
  1940  				After:   map[string]interface{}{},
  1941  				Unknown: true,
  1942  			},
  1943  			attribute: &jsonprovider.Attribute{
  1944  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1945  			},
  1946  			validateDiff: renderers.ValidateUnknown(nil, plans.Create, false),
  1947  		},
  1948  		"map_update_unknown": {
  1949  			input: structured.Change{
  1950  				Before: map[string]interface{}{},
  1951  				After: map[string]interface{}{
  1952  					"element": "one",
  1953  				},
  1954  				Unknown: true,
  1955  			},
  1956  			attribute: &jsonprovider.Attribute{
  1957  				AttributeType: unmarshalType(t, cty.Map(cty.String)),
  1958  			},
  1959  			validateDiff: renderers.ValidateUnknown(renderers.ValidateMap(nil, plans.Delete, false), plans.Update, false),
  1960  		},
  1961  		"list_create_empty": {
  1962  			input: structured.Change{
  1963  				Before: nil,
  1964  				After:  []interface{}{},
  1965  			},
  1966  			attribute: &jsonprovider.Attribute{
  1967  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  1968  			},
  1969  			validateDiff: renderers.ValidateList(nil, plans.Create, false),
  1970  		},
  1971  		"list_create_populated": {
  1972  			input: structured.Change{
  1973  				Before: nil,
  1974  				After:  []interface{}{"one", "two"},
  1975  			},
  1976  			attribute: &jsonprovider.Attribute{
  1977  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  1978  			},
  1979  			validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{
  1980  				renderers.ValidatePrimitive(nil, "one", plans.Create, false),
  1981  				renderers.ValidatePrimitive(nil, "two", plans.Create, false),
  1982  			}, plans.Create, false),
  1983  		},
  1984  		"list_delete_empty": {
  1985  			input: structured.Change{
  1986  				Before: []interface{}{},
  1987  				After:  nil,
  1988  			},
  1989  			attribute: &jsonprovider.Attribute{
  1990  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  1991  			},
  1992  			validateDiff: renderers.ValidateList(nil, plans.Delete, false),
  1993  		},
  1994  		"list_delete_populated": {
  1995  			input: structured.Change{
  1996  				Before: []interface{}{"one", "two"},
  1997  				After:  nil,
  1998  			},
  1999  			attribute: &jsonprovider.Attribute{
  2000  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  2001  			},
  2002  			validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{
  2003  				renderers.ValidatePrimitive("one", nil, plans.Delete, false),
  2004  				renderers.ValidatePrimitive("two", nil, plans.Delete, false),
  2005  			}, plans.Delete, false),
  2006  		},
  2007  		"list_create_sensitive": {
  2008  			input: structured.Change{
  2009  				Before:         nil,
  2010  				After:          []interface{}{},
  2011  				AfterSensitive: true,
  2012  			},
  2013  			attribute: &jsonprovider.Attribute{
  2014  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  2015  			},
  2016  			validateDiff: renderers.ValidateSensitive(renderers.ValidateList(nil, plans.Create, false), false, true, plans.Create, false),
  2017  		},
  2018  		"list_update_sensitive": {
  2019  			input: structured.Change{
  2020  				Before:          []interface{}{"one"},
  2021  				BeforeSensitive: true,
  2022  				After:           []interface{}{},
  2023  				AfterSensitive:  true,
  2024  			},
  2025  			attribute: &jsonprovider.Attribute{
  2026  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  2027  			},
  2028  			validateDiff: renderers.ValidateSensitive(renderers.ValidateList([]renderers.ValidateDiffFunction{
  2029  				renderers.ValidatePrimitive("one", nil, plans.Delete, false),
  2030  			}, plans.Update, false), true, true, plans.Update, false),
  2031  		},
  2032  		"list_delete_sensitive": {
  2033  			input: structured.Change{
  2034  				Before:          []interface{}{},
  2035  				BeforeSensitive: true,
  2036  				After:           nil,
  2037  			},
  2038  			attribute: &jsonprovider.Attribute{
  2039  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  2040  			},
  2041  			validateDiff: renderers.ValidateSensitive(renderers.ValidateList(nil, plans.Delete, false), true, false, plans.Delete, false),
  2042  		},
  2043  		"list_create_unknown": {
  2044  			input: structured.Change{
  2045  				Before:  nil,
  2046  				After:   []interface{}{},
  2047  				Unknown: true,
  2048  			},
  2049  			attribute: &jsonprovider.Attribute{
  2050  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  2051  			},
  2052  			validateDiff: renderers.ValidateUnknown(nil, plans.Create, false),
  2053  		},
  2054  		"list_update_unknown": {
  2055  			input: structured.Change{
  2056  				Before:  []interface{}{},
  2057  				After:   []interface{}{"one"},
  2058  				Unknown: true,
  2059  			},
  2060  			attribute: &jsonprovider.Attribute{
  2061  				AttributeType: unmarshalType(t, cty.List(cty.String)),
  2062  			},
  2063  			validateDiff: renderers.ValidateUnknown(renderers.ValidateList(nil, plans.Delete, false), plans.Update, false),
  2064  		},
  2065  		"set_create_empty": {
  2066  			input: structured.Change{
  2067  				Before: nil,
  2068  				After:  []interface{}{},
  2069  			},
  2070  			attribute: &jsonprovider.Attribute{
  2071  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2072  			},
  2073  			validateDiff: renderers.ValidateSet(nil, plans.Create, false),
  2074  		},
  2075  		"set_create_populated": {
  2076  			input: structured.Change{
  2077  				Before: nil,
  2078  				After:  []interface{}{"one", "two"},
  2079  			},
  2080  			attribute: &jsonprovider.Attribute{
  2081  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2082  			},
  2083  			validateDiff: renderers.ValidateSet([]renderers.ValidateDiffFunction{
  2084  				renderers.ValidatePrimitive(nil, "one", plans.Create, false),
  2085  				renderers.ValidatePrimitive(nil, "two", plans.Create, false),
  2086  			}, plans.Create, false),
  2087  		},
  2088  		"set_delete_empty": {
  2089  			input: structured.Change{
  2090  				Before: []interface{}{},
  2091  				After:  nil,
  2092  			},
  2093  			attribute: &jsonprovider.Attribute{
  2094  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2095  			},
  2096  			validateDiff: renderers.ValidateSet(nil, plans.Delete, false),
  2097  		},
  2098  		"set_delete_populated": {
  2099  			input: structured.Change{
  2100  				Before: []interface{}{"one", "two"},
  2101  				After:  nil,
  2102  			},
  2103  			attribute: &jsonprovider.Attribute{
  2104  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2105  			},
  2106  			validateDiff: renderers.ValidateSet([]renderers.ValidateDiffFunction{
  2107  				renderers.ValidatePrimitive("one", nil, plans.Delete, false),
  2108  				renderers.ValidatePrimitive("two", nil, plans.Delete, false),
  2109  			}, plans.Delete, false),
  2110  		},
  2111  		"set_create_sensitive": {
  2112  			input: structured.Change{
  2113  				Before:         nil,
  2114  				After:          []interface{}{},
  2115  				AfterSensitive: true,
  2116  			},
  2117  			attribute: &jsonprovider.Attribute{
  2118  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2119  			},
  2120  			validateDiff: renderers.ValidateSensitive(renderers.ValidateSet(nil, plans.Create, false), false, true, plans.Create, false),
  2121  		},
  2122  		"set_update_sensitive": {
  2123  			input: structured.Change{
  2124  				Before:          []interface{}{"one"},
  2125  				BeforeSensitive: true,
  2126  				After:           []interface{}{},
  2127  				AfterSensitive:  true,
  2128  			},
  2129  			attribute: &jsonprovider.Attribute{
  2130  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2131  			},
  2132  			validateDiff: renderers.ValidateSensitive(renderers.ValidateSet([]renderers.ValidateDiffFunction{
  2133  				renderers.ValidatePrimitive("one", nil, plans.Delete, false),
  2134  			}, plans.Update, false), true, true, plans.Update, false),
  2135  		},
  2136  		"set_delete_sensitive": {
  2137  			input: structured.Change{
  2138  				Before:          []interface{}{},
  2139  				BeforeSensitive: true,
  2140  				After:           nil,
  2141  			},
  2142  			attribute: &jsonprovider.Attribute{
  2143  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2144  			},
  2145  			validateDiff: renderers.ValidateSensitive(renderers.ValidateSet(nil, plans.Delete, false), true, false, plans.Delete, false),
  2146  		},
  2147  		"set_create_unknown": {
  2148  			input: structured.Change{
  2149  				Before:  nil,
  2150  				After:   []interface{}{},
  2151  				Unknown: true,
  2152  			},
  2153  			attribute: &jsonprovider.Attribute{
  2154  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2155  			},
  2156  			validateDiff: renderers.ValidateUnknown(nil, plans.Create, false),
  2157  		},
  2158  		"set_update_unknown": {
  2159  			input: structured.Change{
  2160  				Before:  []interface{}{},
  2161  				After:   []interface{}{"one"},
  2162  				Unknown: true,
  2163  			},
  2164  			attribute: &jsonprovider.Attribute{
  2165  				AttributeType: unmarshalType(t, cty.Set(cty.String)),
  2166  			},
  2167  			validateDiff: renderers.ValidateUnknown(renderers.ValidateSet(nil, plans.Delete, false), plans.Update, false),
  2168  		},
  2169  		"tuple_primitive": {
  2170  			input: structured.Change{
  2171  				Before: []interface{}{
  2172  					"one",
  2173  					json.Number("2"),
  2174  					"three",
  2175  				},
  2176  				After: []interface{}{
  2177  					"one",
  2178  					json.Number("4"),
  2179  					"three",
  2180  				},
  2181  			},
  2182  			attribute: &jsonprovider.Attribute{
  2183  				AttributeType: unmarshalType(t, cty.Tuple([]cty.Type{cty.String, cty.Number, cty.String})),
  2184  			},
  2185  			validateDiff: renderers.ValidateList([]renderers.ValidateDiffFunction{
  2186  				renderers.ValidatePrimitive("one", "one", plans.NoOp, false),
  2187  				renderers.ValidatePrimitive(json.Number("2"), json.Number("4"), plans.Update, false),
  2188  				renderers.ValidatePrimitive("three", "three", plans.NoOp, false),
  2189  			}, plans.Update, false),
  2190  		},
  2191  	}
  2192  
  2193  	for name, tc := range tcs {
  2194  
  2195  		// Let's set some default values on the input.
  2196  		if tc.input.RelevantAttributes == nil {
  2197  			tc.input.RelevantAttributes = attribute_path.AlwaysMatcher()
  2198  		}
  2199  		if tc.input.ReplacePaths == nil {
  2200  			tc.input.ReplacePaths = &attribute_path.PathMatcher{}
  2201  		}
  2202  
  2203  		t.Run(name, func(t *testing.T) {
  2204  			tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute))
  2205  		})
  2206  	}
  2207  }
  2208  
  2209  func TestRelevantAttributes(t *testing.T) {
  2210  	tcs := map[string]struct {
  2211  		input    structured.Change
  2212  		block    *jsonprovider.Block
  2213  		validate renderers.ValidateDiffFunction
  2214  	}{
  2215  		"simple_attributes": {
  2216  			input: structured.Change{
  2217  				Before: map[string]interface{}{
  2218  					"id":     "old_id",
  2219  					"ignore": "doesn't matter",
  2220  				},
  2221  				After: map[string]interface{}{
  2222  					"id":     "new_id",
  2223  					"ignore": "doesn't matter but modified",
  2224  				},
  2225  				RelevantAttributes: &attribute_path.PathMatcher{
  2226  					Paths: [][]interface{}{
  2227  						{
  2228  							"id",
  2229  						},
  2230  					},
  2231  				},
  2232  			},
  2233  			block: &jsonprovider.Block{
  2234  				Attributes: map[string]*jsonprovider.Attribute{
  2235  					"id": {
  2236  						AttributeType: unmarshalType(t, cty.String),
  2237  					},
  2238  					"ignore": {
  2239  						AttributeType: unmarshalType(t, cty.String),
  2240  					},
  2241  				},
  2242  			},
  2243  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2244  				"id":     renderers.ValidatePrimitive("old_id", "new_id", plans.Update, false),
  2245  				"ignore": renderers.ValidatePrimitive("doesn't matter", "doesn't matter", plans.NoOp, false),
  2246  			}, nil, nil, nil, nil, plans.Update, false),
  2247  		},
  2248  		"nested_attributes": {
  2249  			input: structured.Change{
  2250  				Before: map[string]interface{}{
  2251  					"list_block": []interface{}{
  2252  						map[string]interface{}{
  2253  							"id": "old_one",
  2254  						},
  2255  						map[string]interface{}{
  2256  							"id": "ignored",
  2257  						},
  2258  					},
  2259  				},
  2260  				After: map[string]interface{}{
  2261  					"list_block": []interface{}{
  2262  						map[string]interface{}{
  2263  							"id": "new_one",
  2264  						},
  2265  						map[string]interface{}{
  2266  							"id": "ignored_but_changed",
  2267  						},
  2268  					},
  2269  				},
  2270  				RelevantAttributes: &attribute_path.PathMatcher{
  2271  					Paths: [][]interface{}{
  2272  						{
  2273  							"list_block",
  2274  							float64(0),
  2275  							"id",
  2276  						},
  2277  					},
  2278  				},
  2279  			},
  2280  			block: &jsonprovider.Block{
  2281  				BlockTypes: map[string]*jsonprovider.BlockType{
  2282  					"list_block": {
  2283  						Block: &jsonprovider.Block{
  2284  							Attributes: map[string]*jsonprovider.Attribute{
  2285  								"id": {
  2286  									AttributeType: unmarshalType(t, cty.String),
  2287  								},
  2288  							},
  2289  						},
  2290  						NestingMode: "list",
  2291  					},
  2292  				},
  2293  			},
  2294  			validate: renderers.ValidateBlock(nil, nil, map[string][]renderers.ValidateDiffFunction{
  2295  				"list_block": {
  2296  					renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2297  						"id": renderers.ValidatePrimitive("old_one", "new_one", plans.Update, false),
  2298  					}, nil, nil, nil, nil, plans.Update, false),
  2299  					renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2300  						"id": renderers.ValidatePrimitive("ignored", "ignored", plans.NoOp, false),
  2301  					}, nil, nil, nil, nil, plans.NoOp, false),
  2302  				},
  2303  			}, nil, nil, plans.Update, false),
  2304  		},
  2305  		"nested_attributes_in_object": {
  2306  			input: structured.Change{
  2307  				Before: map[string]interface{}{
  2308  					"object": map[string]interface{}{
  2309  						"id": "old_id",
  2310  					},
  2311  				},
  2312  				After: map[string]interface{}{
  2313  					"object": map[string]interface{}{
  2314  						"id": "new_id",
  2315  					},
  2316  				},
  2317  				RelevantAttributes: &attribute_path.PathMatcher{
  2318  					Propagate: true,
  2319  					Paths: [][]interface{}{
  2320  						{
  2321  							"object", // Even though we just specify object, it should now include every below object as well.
  2322  						},
  2323  					},
  2324  				},
  2325  			},
  2326  			block: &jsonprovider.Block{
  2327  				Attributes: map[string]*jsonprovider.Attribute{
  2328  					"object": {
  2329  						AttributeType: unmarshalType(t, cty.Object(map[string]cty.Type{
  2330  							"id": cty.String,
  2331  						})),
  2332  					},
  2333  				},
  2334  			},
  2335  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2336  				"object": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2337  					"id": renderers.ValidatePrimitive("old_id", "new_id", plans.Update, false),
  2338  				}, plans.Update, false),
  2339  			}, nil, nil, nil, nil, plans.Update, false),
  2340  		},
  2341  		"elements_in_list": {
  2342  			input: structured.Change{
  2343  				Before: map[string]interface{}{
  2344  					"list": []interface{}{
  2345  						json.Number("0"), json.Number("1"), json.Number("2"), json.Number("3"), json.Number("4"),
  2346  					},
  2347  				},
  2348  				After: map[string]interface{}{
  2349  					"list": []interface{}{
  2350  						json.Number("0"), json.Number("5"), json.Number("6"), json.Number("7"), json.Number("4"),
  2351  					},
  2352  				},
  2353  				RelevantAttributes: &attribute_path.PathMatcher{
  2354  					Paths: [][]interface{}{ // The list is actually just going to ignore this.
  2355  						{
  2356  							"list",
  2357  							0.0,
  2358  						},
  2359  						{
  2360  							"list",
  2361  							2.0,
  2362  						},
  2363  						{
  2364  							"list",
  2365  							4.0,
  2366  						},
  2367  					},
  2368  				},
  2369  			},
  2370  			block: &jsonprovider.Block{
  2371  				Attributes: map[string]*jsonprovider.Attribute{
  2372  					"list": {
  2373  						AttributeType: unmarshalType(t, cty.List(cty.Number)),
  2374  					},
  2375  				},
  2376  			},
  2377  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2378  				// The list validator below just ignores our relevant
  2379  				// attributes. This is deliberate.
  2380  				"list": renderers.ValidateList([]renderers.ValidateDiffFunction{
  2381  					renderers.ValidatePrimitive(json.Number("0"), json.Number("0"), plans.NoOp, false),
  2382  					renderers.ValidatePrimitive(json.Number("1"), nil, plans.Delete, false),
  2383  					renderers.ValidatePrimitive(json.Number("2"), nil, plans.Delete, false),
  2384  					renderers.ValidatePrimitive(json.Number("3"), nil, plans.Delete, false),
  2385  					renderers.ValidatePrimitive(nil, json.Number("5"), plans.Create, false),
  2386  					renderers.ValidatePrimitive(nil, json.Number("6"), plans.Create, false),
  2387  					renderers.ValidatePrimitive(nil, json.Number("7"), plans.Create, false),
  2388  					renderers.ValidatePrimitive(json.Number("4"), json.Number("4"), plans.NoOp, false),
  2389  				}, plans.Update, false),
  2390  			}, nil, nil, nil, nil, plans.Update, false),
  2391  		},
  2392  		"elements_in_map": {
  2393  			input: structured.Change{
  2394  				Before: map[string]interface{}{
  2395  					"map": map[string]interface{}{
  2396  						"key_one":   "value_one",
  2397  						"key_two":   "value_two",
  2398  						"key_three": "value_three",
  2399  					},
  2400  				},
  2401  				After: map[string]interface{}{
  2402  					"map": map[string]interface{}{
  2403  						"key_one":  "value_three",
  2404  						"key_two":  "value_seven",
  2405  						"key_four": "value_four",
  2406  					},
  2407  				},
  2408  				RelevantAttributes: &attribute_path.PathMatcher{
  2409  					Paths: [][]interface{}{
  2410  						{
  2411  							"map",
  2412  							"key_one",
  2413  						},
  2414  						{
  2415  							"map",
  2416  							"key_three",
  2417  						},
  2418  						{
  2419  							"map",
  2420  							"key_four",
  2421  						},
  2422  					},
  2423  				},
  2424  			},
  2425  			block: &jsonprovider.Block{
  2426  				Attributes: map[string]*jsonprovider.Attribute{
  2427  					"map": {
  2428  						AttributeType: unmarshalType(t, cty.Map(cty.String)),
  2429  					},
  2430  				},
  2431  			},
  2432  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2433  				"map": renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
  2434  					"key_one":   renderers.ValidatePrimitive("value_one", "value_three", plans.Update, false),
  2435  					"key_two":   renderers.ValidatePrimitive("value_two", "value_two", plans.NoOp, false),
  2436  					"key_three": renderers.ValidatePrimitive("value_three", nil, plans.Delete, false),
  2437  					"key_four":  renderers.ValidatePrimitive(nil, "value_four", plans.Create, false),
  2438  				}, plans.Update, false),
  2439  			}, nil, nil, nil, nil, plans.Update, false),
  2440  		},
  2441  		"elements_in_set": {
  2442  			input: structured.Change{
  2443  				Before: map[string]interface{}{
  2444  					"set": []interface{}{
  2445  						json.Number("0"), json.Number("1"), json.Number("2"), json.Number("3"), json.Number("4"),
  2446  					},
  2447  				},
  2448  				After: map[string]interface{}{
  2449  					"set": []interface{}{
  2450  						json.Number("0"), json.Number("2"), json.Number("4"), json.Number("5"), json.Number("6"),
  2451  					},
  2452  				},
  2453  				RelevantAttributes: &attribute_path.PathMatcher{
  2454  					Propagate: true,
  2455  					Paths: [][]interface{}{
  2456  						{
  2457  							"set",
  2458  						},
  2459  					},
  2460  				},
  2461  			},
  2462  			block: &jsonprovider.Block{
  2463  				Attributes: map[string]*jsonprovider.Attribute{
  2464  					"set": {
  2465  						AttributeType: unmarshalType(t, cty.Set(cty.Number)),
  2466  					},
  2467  				},
  2468  			},
  2469  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2470  				"set": renderers.ValidateSet([]renderers.ValidateDiffFunction{
  2471  					renderers.ValidatePrimitive(json.Number("0"), json.Number("0"), plans.NoOp, false),
  2472  					renderers.ValidatePrimitive(json.Number("1"), nil, plans.Delete, false),
  2473  					renderers.ValidatePrimitive(json.Number("2"), json.Number("2"), plans.NoOp, false),
  2474  					renderers.ValidatePrimitive(json.Number("3"), nil, plans.Delete, false),
  2475  					renderers.ValidatePrimitive(json.Number("4"), json.Number("4"), plans.NoOp, false),
  2476  					renderers.ValidatePrimitive(nil, json.Number("5"), plans.Create, false),
  2477  					renderers.ValidatePrimitive(nil, json.Number("6"), plans.Create, false),
  2478  				}, plans.Update, false),
  2479  			}, nil, nil, nil, nil, plans.Update, false),
  2480  		},
  2481  		"dynamic_types": {
  2482  			input: structured.Change{
  2483  				Before: map[string]interface{}{
  2484  					"dynamic_nested_type": map[string]interface{}{
  2485  						"nested_id": "nomatch",
  2486  						"nested_object": map[string]interface{}{
  2487  							"nested_nested_id": "matched",
  2488  						},
  2489  					},
  2490  					"dynamic_nested_type_match": map[string]interface{}{
  2491  						"nested_id": "allmatch",
  2492  						"nested_object": map[string]interface{}{
  2493  							"nested_nested_id": "allmatch",
  2494  						},
  2495  					},
  2496  				},
  2497  				After: map[string]interface{}{
  2498  					"dynamic_nested_type": map[string]interface{}{
  2499  						"nested_id": "nomatch_changed",
  2500  						"nested_object": map[string]interface{}{
  2501  							"nested_nested_id": "matched",
  2502  						},
  2503  					},
  2504  					"dynamic_nested_type_match": map[string]interface{}{
  2505  						"nested_id": "allmatch",
  2506  						"nested_object": map[string]interface{}{
  2507  							"nested_nested_id": "allmatch",
  2508  						},
  2509  					},
  2510  				},
  2511  				RelevantAttributes: &attribute_path.PathMatcher{
  2512  					Propagate: true,
  2513  					Paths: [][]interface{}{
  2514  						{
  2515  							"dynamic_nested_type",
  2516  							"nested_object",
  2517  							"nested_nested_id",
  2518  						},
  2519  						{
  2520  							"dynamic_nested_type_match",
  2521  						},
  2522  					},
  2523  				},
  2524  			},
  2525  			block: &jsonprovider.Block{
  2526  				Attributes: map[string]*jsonprovider.Attribute{
  2527  					"dynamic_nested_type": {
  2528  						AttributeType: unmarshalType(t, cty.DynamicPseudoType),
  2529  					},
  2530  					"dynamic_nested_type_match": {
  2531  						AttributeType: unmarshalType(t, cty.DynamicPseudoType),
  2532  					},
  2533  				},
  2534  			},
  2535  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2536  				"dynamic_nested_type": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2537  					"nested_id": renderers.ValidatePrimitive("nomatch", "nomatch", plans.NoOp, false),
  2538  					"nested_object": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2539  						"nested_nested_id": renderers.ValidatePrimitive("matched", "matched", plans.NoOp, false),
  2540  					}, plans.NoOp, false),
  2541  				}, plans.NoOp, false),
  2542  				"dynamic_nested_type_match": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2543  					"nested_id": renderers.ValidatePrimitive("allmatch", "allmatch", plans.NoOp, false),
  2544  					"nested_object": renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2545  						"nested_nested_id": renderers.ValidatePrimitive("allmatch", "allmatch", plans.NoOp, false),
  2546  					}, plans.NoOp, false),
  2547  				}, plans.NoOp, false),
  2548  			}, nil, nil, nil, nil, plans.NoOp, false),
  2549  		},
  2550  	}
  2551  	for name, tc := range tcs {
  2552  		if tc.input.ReplacePaths == nil {
  2553  			tc.input.ReplacePaths = &attribute_path.PathMatcher{}
  2554  		}
  2555  		t.Run(name, func(t *testing.T) {
  2556  			tc.validate(t, ComputeDiffForBlock(tc.input, tc.block))
  2557  		})
  2558  	}
  2559  }
  2560  
  2561  func TestDynamicPseudoType(t *testing.T) {
  2562  	tcs := map[string]struct {
  2563  		input    structured.Change
  2564  		validate renderers.ValidateDiffFunction
  2565  	}{
  2566  		"after_sensitive_in_dynamic_type": {
  2567  			input: structured.Change{
  2568  				Before: nil,
  2569  				After: map[string]interface{}{
  2570  					"key": "value",
  2571  				},
  2572  				Unknown:         false,
  2573  				BeforeSensitive: false,
  2574  				AfterSensitive: map[string]interface{}{
  2575  					"key": true,
  2576  				},
  2577  				ReplacePaths:       attribute_path.Empty(false),
  2578  				RelevantAttributes: attribute_path.AlwaysMatcher(),
  2579  			},
  2580  			validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2581  				"key": renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "value", plans.Create, false), false, true, plans.Create, false),
  2582  			}, plans.Create, false),
  2583  		},
  2584  		"before_sensitive_in_dynamic_type": {
  2585  			input: structured.Change{
  2586  				Before: map[string]interface{}{
  2587  					"key": "value",
  2588  				},
  2589  				After:   nil,
  2590  				Unknown: false,
  2591  				BeforeSensitive: map[string]interface{}{
  2592  					"key": true,
  2593  				},
  2594  				AfterSensitive:     false,
  2595  				ReplacePaths:       attribute_path.Empty(false),
  2596  				RelevantAttributes: attribute_path.AlwaysMatcher(),
  2597  			},
  2598  			validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2599  				"key": renderers.ValidateSensitive(renderers.ValidatePrimitive("value", nil, plans.Delete, false), true, false, plans.Delete, false),
  2600  			}, plans.Delete, false),
  2601  		},
  2602  		"sensitive_in_dynamic_type": {
  2603  			input: structured.Change{
  2604  				Before: map[string]interface{}{
  2605  					"key": "before",
  2606  				},
  2607  				After: map[string]interface{}{
  2608  					"key": "after",
  2609  				},
  2610  				Unknown: false,
  2611  				BeforeSensitive: map[string]interface{}{
  2612  					"key": true,
  2613  				},
  2614  				AfterSensitive: map[string]interface{}{
  2615  					"key": true,
  2616  				},
  2617  				ReplacePaths:       attribute_path.Empty(false),
  2618  				RelevantAttributes: attribute_path.AlwaysMatcher(),
  2619  			},
  2620  			validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2621  				"key": renderers.ValidateSensitive(renderers.ValidatePrimitive("before", "after", plans.Update, false), true, true, plans.Update, false),
  2622  			}, plans.Update, false),
  2623  		},
  2624  		"create_unknown_in_dynamic_type": {
  2625  			input: structured.Change{
  2626  				Before: nil,
  2627  				After:  map[string]interface{}{},
  2628  				Unknown: map[string]interface{}{
  2629  					"key": true,
  2630  				},
  2631  				BeforeSensitive:    false,
  2632  				AfterSensitive:     false,
  2633  				ReplacePaths:       attribute_path.Empty(false),
  2634  				RelevantAttributes: attribute_path.AlwaysMatcher(),
  2635  			},
  2636  			validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2637  				"key": renderers.ValidateUnknown(nil, plans.Create, false),
  2638  			}, plans.Create, false),
  2639  		},
  2640  		"update_unknown_in_dynamic_type": {
  2641  			input: structured.Change{
  2642  				Before: map[string]interface{}{
  2643  					"key": "before",
  2644  				},
  2645  				After: map[string]interface{}{},
  2646  				Unknown: map[string]interface{}{
  2647  					"key": true,
  2648  				},
  2649  				BeforeSensitive:    false,
  2650  				AfterSensitive:     false,
  2651  				ReplacePaths:       attribute_path.Empty(false),
  2652  				RelevantAttributes: attribute_path.AlwaysMatcher(),
  2653  			},
  2654  			validate: renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2655  				"key": renderers.ValidateUnknown(renderers.ValidatePrimitive("before", nil, plans.Delete, false), plans.Update, false),
  2656  			}, plans.Update, false),
  2657  		},
  2658  	}
  2659  	for key, tc := range tcs {
  2660  		t.Run(key, func(t *testing.T) {
  2661  			tc.validate(t, ComputeDiffForType(tc.input, cty.DynamicPseudoType))
  2662  		})
  2663  	}
  2664  }
  2665  
  2666  func TestSpecificCases(t *testing.T) {
  2667  	// This is a special test that can contain any combination of individual
  2668  	// cases and will execute against them. For testing/fixing specific issues
  2669  	// you can generally put the test case in here.
  2670  	tcs := map[string]struct {
  2671  		input    structured.Change
  2672  		block    *jsonprovider.Block
  2673  		validate renderers.ValidateDiffFunction
  2674  	}{
  2675  		"issues/33016/unknown": {
  2676  			input: structured.Change{
  2677  				Before: nil,
  2678  				After: map[string]interface{}{
  2679  					"triggers": map[string]interface{}{},
  2680  				},
  2681  				Unknown: map[string]interface{}{
  2682  					"id": true,
  2683  					"triggers": map[string]interface{}{
  2684  						"rotation": true,
  2685  					},
  2686  				},
  2687  				BeforeSensitive: false,
  2688  				AfterSensitive: map[string]interface{}{
  2689  					"triggers": map[string]interface{}{},
  2690  				},
  2691  				ReplacePaths:       attribute_path.Empty(false),
  2692  				RelevantAttributes: attribute_path.AlwaysMatcher(),
  2693  			},
  2694  			block: &jsonprovider.Block{
  2695  				Attributes: map[string]*jsonprovider.Attribute{
  2696  					"id": {
  2697  						AttributeType: unmarshalType(t, cty.String),
  2698  					},
  2699  					"triggers": {
  2700  						AttributeType: unmarshalType(t, cty.Map(cty.String)),
  2701  					},
  2702  				},
  2703  			},
  2704  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2705  				"id": renderers.ValidateUnknown(nil, plans.Create, false),
  2706  				"triggers": renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
  2707  					"rotation": renderers.ValidateUnknown(nil, plans.Create, false),
  2708  				}, plans.Create, false),
  2709  			}, nil, nil, nil, nil, plans.Create, false),
  2710  		},
  2711  		"issues/33016/null": {
  2712  			input: structured.Change{
  2713  				Before: nil,
  2714  				After: map[string]interface{}{
  2715  					"triggers": map[string]interface{}{
  2716  						"rotation": nil,
  2717  					},
  2718  				},
  2719  				Unknown: map[string]interface{}{
  2720  					"id":       true,
  2721  					"triggers": map[string]interface{}{},
  2722  				},
  2723  				BeforeSensitive: false,
  2724  				AfterSensitive: map[string]interface{}{
  2725  					"triggers": map[string]interface{}{},
  2726  				},
  2727  				ReplacePaths:       attribute_path.Empty(false),
  2728  				RelevantAttributes: attribute_path.AlwaysMatcher(),
  2729  			},
  2730  			block: &jsonprovider.Block{
  2731  				Attributes: map[string]*jsonprovider.Attribute{
  2732  					"id": {
  2733  						AttributeType: unmarshalType(t, cty.String),
  2734  					},
  2735  					"triggers": {
  2736  						AttributeType: unmarshalType(t, cty.Map(cty.String)),
  2737  					},
  2738  				},
  2739  			},
  2740  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2741  				"id": renderers.ValidateUnknown(nil, plans.Create, false),
  2742  				"triggers": renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{
  2743  					"rotation": renderers.ValidatePrimitive(nil, nil, plans.Create, false),
  2744  				}, plans.Create, false),
  2745  			}, nil, nil, nil, nil, plans.Create, false),
  2746  		},
  2747  
  2748  		// The following tests are from issue 33472. Basically OpenTofu allows
  2749  		// callers to treat numbers as strings in references and expects us
  2750  		// to coerce the strings into numbers. For example the following are
  2751  		// equivalent.
  2752  		//    - test_resource.resource.list[0].attribute
  2753  		//    - test_resource.resource.list["0"].attribute
  2754  		//
  2755  		// We need our attribute_path package (used within the ReplacePaths and
  2756  		// RelevantAttributes fields) to handle coercing strings into numbers
  2757  		// when it's expected.
  2758  
  2759  		"issues/33472/expected": {
  2760  			input: structured.Change{
  2761  				Before: map[string]interface{}{
  2762  					"list": []interface{}{
  2763  						map[string]interface{}{
  2764  							"number": json.Number("-1"),
  2765  						},
  2766  					},
  2767  				},
  2768  				After: map[string]interface{}{
  2769  					"list": []interface{}{
  2770  						map[string]interface{}{
  2771  							"number": json.Number("2"),
  2772  						},
  2773  					},
  2774  				},
  2775  				Unknown:         false,
  2776  				BeforeSensitive: false,
  2777  				AfterSensitive:  false,
  2778  				ReplacePaths:    attribute_path.Empty(false),
  2779  				RelevantAttributes: &attribute_path.PathMatcher{
  2780  					Propagate: true,
  2781  					Paths: [][]interface{}{
  2782  						{
  2783  							"list",
  2784  							0.0, // This is normal and expected so easy case.
  2785  							"number",
  2786  						},
  2787  					},
  2788  				},
  2789  			},
  2790  			block: &jsonprovider.Block{
  2791  				Attributes: map[string]*jsonprovider.Attribute{
  2792  					"list": {
  2793  						AttributeType: unmarshalType(t, cty.List(cty.Object(map[string]cty.Type{
  2794  							"number": cty.Number,
  2795  						}))),
  2796  					},
  2797  				},
  2798  			},
  2799  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2800  				"list": renderers.ValidateList([]renderers.ValidateDiffFunction{
  2801  					renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2802  						"number": renderers.ValidatePrimitive(json.Number("-1"), json.Number("2"), plans.Update, false),
  2803  					}, plans.Update, false),
  2804  				}, plans.Update, false),
  2805  			}, nil, nil, nil, nil, plans.Update, false),
  2806  		},
  2807  
  2808  		"issues/33472/coerce": {
  2809  			input: structured.Change{
  2810  				Before: map[string]interface{}{
  2811  					"list": []interface{}{
  2812  						map[string]interface{}{
  2813  							"number": json.Number("-1"),
  2814  						},
  2815  					},
  2816  				},
  2817  				After: map[string]interface{}{
  2818  					"list": []interface{}{
  2819  						map[string]interface{}{
  2820  							"number": json.Number("2"),
  2821  						},
  2822  					},
  2823  				},
  2824  				Unknown:         false,
  2825  				BeforeSensitive: false,
  2826  				AfterSensitive:  false,
  2827  				ReplacePaths:    attribute_path.Empty(false),
  2828  				RelevantAttributes: &attribute_path.PathMatcher{
  2829  					Propagate: true,
  2830  					Paths: [][]interface{}{
  2831  						{
  2832  							"list",
  2833  							"0", // Difficult but allowed, we need to handle this.
  2834  							"number",
  2835  						},
  2836  					},
  2837  				},
  2838  			},
  2839  			block: &jsonprovider.Block{
  2840  				Attributes: map[string]*jsonprovider.Attribute{
  2841  					"list": {
  2842  						AttributeType: unmarshalType(t, cty.List(cty.Object(map[string]cty.Type{
  2843  							"number": cty.Number,
  2844  						}))),
  2845  					},
  2846  				},
  2847  			},
  2848  			validate: renderers.ValidateBlock(map[string]renderers.ValidateDiffFunction{
  2849  				"list": renderers.ValidateList([]renderers.ValidateDiffFunction{
  2850  					renderers.ValidateObject(map[string]renderers.ValidateDiffFunction{
  2851  						"number": renderers.ValidatePrimitive(json.Number("-1"), json.Number("2"), plans.Update, false),
  2852  					}, plans.Update, false),
  2853  				}, plans.Update, false),
  2854  			}, nil, nil, nil, nil, plans.Update, false),
  2855  		},
  2856  	}
  2857  	for name, tc := range tcs {
  2858  		t.Run(name, func(t *testing.T) {
  2859  			tc.validate(t, ComputeDiffForBlock(tc.input, tc.block))
  2860  		})
  2861  	}
  2862  }
  2863  
  2864  // unmarshalType converts a cty.Type into a json.RawMessage understood by the
  2865  // schema. It also lets the testing framework handle any errors to keep the API
  2866  // clean.
  2867  func unmarshalType(t *testing.T, ctyType cty.Type) json.RawMessage {
  2868  	msg, err := ctyjson.MarshalType(ctyType)
  2869  	if err != nil {
  2870  		t.Fatalf("invalid type: %s", ctyType.FriendlyName())
  2871  	}
  2872  	return msg
  2873  }
  2874  
  2875  // wrapChangeInSlice does the same as wrapChangeInMap, except it wraps it into a
  2876  // slice internally.
  2877  func wrapChangeInSlice(input structured.Change) structured.Change {
  2878  	return wrapChange(input, float64(0), func(value interface{}, unknown interface{}, explicit bool) interface{} {
  2879  		switch value.(type) {
  2880  		case nil:
  2881  			if set, ok := unknown.(bool); (set && ok) || explicit {
  2882  				return []interface{}{nil}
  2883  
  2884  			}
  2885  			return []interface{}{}
  2886  		default:
  2887  			return []interface{}{value}
  2888  		}
  2889  	})
  2890  }
  2891  
  2892  // wrapChangeInMap access a single structured.Change and returns a new
  2893  // structured.Change that represents a map with a single element. That single
  2894  // element is the input value.
  2895  func wrapChangeInMap(input structured.Change) structured.Change {
  2896  	return wrapChange(input, "element", func(value interface{}, unknown interface{}, explicit bool) interface{} {
  2897  		switch value.(type) {
  2898  		case nil:
  2899  			if set, ok := unknown.(bool); (set && ok) || explicit {
  2900  				return map[string]interface{}{
  2901  					"element": nil,
  2902  				}
  2903  			}
  2904  			return map[string]interface{}{}
  2905  		default:
  2906  			return map[string]interface{}{
  2907  				"element": value,
  2908  			}
  2909  		}
  2910  	})
  2911  }
  2912  
  2913  func wrapChange(input structured.Change, step interface{}, wrap func(interface{}, interface{}, bool) interface{}) structured.Change {
  2914  
  2915  	replacePaths := &attribute_path.PathMatcher{}
  2916  	for _, path := range input.ReplacePaths.(*attribute_path.PathMatcher).Paths {
  2917  		var updated []interface{}
  2918  		updated = append(updated, step)
  2919  		updated = append(updated, path...)
  2920  		replacePaths.Paths = append(replacePaths.Paths, updated)
  2921  	}
  2922  
  2923  	// relevantAttributes usually default to AlwaysMatcher, which means we can
  2924  	// just ignore it. But if we have had some paths specified we need to wrap
  2925  	// those as well.
  2926  	relevantAttributes := input.RelevantAttributes
  2927  	if concrete, ok := relevantAttributes.(*attribute_path.PathMatcher); ok {
  2928  
  2929  		newRelevantAttributes := &attribute_path.PathMatcher{}
  2930  		for _, path := range concrete.Paths {
  2931  			var updated []interface{}
  2932  			updated = append(updated, step)
  2933  			updated = append(updated, path...)
  2934  			newRelevantAttributes.Paths = append(newRelevantAttributes.Paths, updated)
  2935  		}
  2936  		relevantAttributes = newRelevantAttributes
  2937  	}
  2938  
  2939  	return structured.Change{
  2940  		Before:             wrap(input.Before, nil, input.BeforeExplicit),
  2941  		After:              wrap(input.After, input.Unknown, input.AfterExplicit),
  2942  		Unknown:            wrap(input.Unknown, nil, false),
  2943  		BeforeSensitive:    wrap(input.BeforeSensitive, nil, false),
  2944  		AfterSensitive:     wrap(input.AfterSensitive, nil, false),
  2945  		ReplacePaths:       replacePaths,
  2946  		RelevantAttributes: relevantAttributes,
  2947  	}
  2948  }