github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonformat/differ/change_test.go (about)

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