github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/jsonformat/differ/differ_test.go (about)

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