github.com/hooklift/terraform@v0.11.0-beta1.0.20171117000744-6786c1361ffe/helper/schema/resource_diff_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/davecgh/go-spew/spew"
     8  	"github.com/hashicorp/terraform/terraform"
     9  )
    10  
    11  // testSetFunc is a very simple function we use to test a foo/bar complex set.
    12  // Both "foo" and "bar" are int values.
    13  //
    14  // This is not foolproof as since it performs sums, you can run into
    15  // collisions. Spec tests accordingly. :P
    16  func testSetFunc(v interface{}) int {
    17  	m := v.(map[string]interface{})
    18  	return m["foo"].(int) + m["bar"].(int)
    19  }
    20  
    21  // resourceDiffTestCase provides a test case struct for SetNew and SetDiff.
    22  type resourceDiffTestCase struct {
    23  	Name          string
    24  	Schema        map[string]*Schema
    25  	State         *terraform.InstanceState
    26  	Config        *terraform.ResourceConfig
    27  	Diff          *terraform.InstanceDiff
    28  	Key           string
    29  	OldValue      interface{}
    30  	NewValue      interface{}
    31  	Expected      *terraform.InstanceDiff
    32  	ExpectedError bool
    33  }
    34  
    35  // testDiffCases produces a list of test cases for use with SetNew and SetDiff.
    36  func testDiffCases(t *testing.T, oldPrefix string, oldOffset int, computed bool) []resourceDiffTestCase {
    37  	return []resourceDiffTestCase{
    38  		resourceDiffTestCase{
    39  			Name: "basic primitive diff",
    40  			Schema: map[string]*Schema{
    41  				"foo": &Schema{
    42  					Type:     TypeString,
    43  					Optional: true,
    44  					Computed: true,
    45  				},
    46  			},
    47  			State: &terraform.InstanceState{
    48  				Attributes: map[string]string{
    49  					"foo": "bar",
    50  				},
    51  			},
    52  			Config: testConfig(t, map[string]interface{}{
    53  				"foo": "baz",
    54  			}),
    55  			Diff: &terraform.InstanceDiff{
    56  				Attributes: map[string]*terraform.ResourceAttrDiff{
    57  					"foo": &terraform.ResourceAttrDiff{
    58  						Old: "bar",
    59  						New: "baz",
    60  					},
    61  				},
    62  			},
    63  			Key:      "foo",
    64  			NewValue: "qux",
    65  			Expected: &terraform.InstanceDiff{
    66  				Attributes: map[string]*terraform.ResourceAttrDiff{
    67  					"foo": &terraform.ResourceAttrDiff{
    68  						Old: "bar",
    69  						New: func() string {
    70  							if computed {
    71  								return ""
    72  							}
    73  							return "qux"
    74  						}(),
    75  						NewComputed: computed,
    76  					},
    77  				},
    78  			},
    79  		},
    80  		resourceDiffTestCase{
    81  			Name: "basic set diff",
    82  			Schema: map[string]*Schema{
    83  				"foo": &Schema{
    84  					Type:     TypeSet,
    85  					Optional: true,
    86  					Computed: true,
    87  					Elem:     &Schema{Type: TypeString},
    88  					Set:      HashString,
    89  				},
    90  			},
    91  			State: &terraform.InstanceState{
    92  				Attributes: map[string]string{
    93  					"foo.#":          "1",
    94  					"foo.1996459178": "bar",
    95  				},
    96  			},
    97  			Config: testConfig(t, map[string]interface{}{
    98  				"foo": []interface{}{"baz"},
    99  			}),
   100  			Diff: &terraform.InstanceDiff{
   101  				Attributes: map[string]*terraform.ResourceAttrDiff{
   102  					"foo.1996459178": &terraform.ResourceAttrDiff{
   103  						Old:        "bar",
   104  						New:        "",
   105  						NewRemoved: true,
   106  					},
   107  					"foo.2015626392": &terraform.ResourceAttrDiff{
   108  						Old: "",
   109  						New: "baz",
   110  					},
   111  				},
   112  			},
   113  			Key:      "foo",
   114  			NewValue: []interface{}{"qux"},
   115  			Expected: &terraform.InstanceDiff{
   116  				Attributes: func() map[string]*terraform.ResourceAttrDiff {
   117  					result := map[string]*terraform.ResourceAttrDiff{}
   118  					if computed {
   119  						result["foo.#"] = &terraform.ResourceAttrDiff{
   120  							Old:         "1",
   121  							New:         "",
   122  							NewComputed: true,
   123  						}
   124  					} else {
   125  						result["foo.2800005064"] = &terraform.ResourceAttrDiff{
   126  							Old: "",
   127  							New: "qux",
   128  						}
   129  						result["foo.1996459178"] = &terraform.ResourceAttrDiff{
   130  							Old:        "bar",
   131  							New:        "",
   132  							NewRemoved: true,
   133  						}
   134  					}
   135  					return result
   136  				}(),
   137  			},
   138  		},
   139  		resourceDiffTestCase{
   140  			Name: "basic list diff",
   141  			Schema: map[string]*Schema{
   142  				"foo": &Schema{
   143  					Type:     TypeList,
   144  					Optional: true,
   145  					Computed: true,
   146  					Elem:     &Schema{Type: TypeString},
   147  				},
   148  			},
   149  			State: &terraform.InstanceState{
   150  				Attributes: map[string]string{
   151  					"foo.#": "1",
   152  					"foo.0": "bar",
   153  				},
   154  			},
   155  			Config: testConfig(t, map[string]interface{}{
   156  				"foo": []interface{}{"baz"},
   157  			}),
   158  			Diff: &terraform.InstanceDiff{
   159  				Attributes: map[string]*terraform.ResourceAttrDiff{
   160  					"foo.0": &terraform.ResourceAttrDiff{
   161  						Old: "bar",
   162  						New: "baz",
   163  					},
   164  				},
   165  			},
   166  			Key:      "foo",
   167  			NewValue: []interface{}{"qux"},
   168  			Expected: &terraform.InstanceDiff{
   169  				Attributes: func() map[string]*terraform.ResourceAttrDiff {
   170  					result := make(map[string]*terraform.ResourceAttrDiff)
   171  					if computed {
   172  						result["foo.#"] = &terraform.ResourceAttrDiff{
   173  							Old:         "1",
   174  							New:         "",
   175  							NewComputed: true,
   176  						}
   177  					} else {
   178  						result["foo.0"] = &terraform.ResourceAttrDiff{
   179  							Old: "bar",
   180  							New: "qux",
   181  						}
   182  					}
   183  					return result
   184  				}(),
   185  			},
   186  		},
   187  		resourceDiffTestCase{
   188  			Name: "basic map diff",
   189  			Schema: map[string]*Schema{
   190  				"foo": &Schema{
   191  					Type:     TypeMap,
   192  					Optional: true,
   193  					Computed: true,
   194  				},
   195  			},
   196  			State: &terraform.InstanceState{
   197  				Attributes: map[string]string{
   198  					"foo.%":   "1",
   199  					"foo.bar": "baz",
   200  				},
   201  			},
   202  			Config: testConfig(t, map[string]interface{}{
   203  				"foo": map[string]interface{}{"bar": "qux"},
   204  			}),
   205  			Diff: &terraform.InstanceDiff{
   206  				Attributes: map[string]*terraform.ResourceAttrDiff{
   207  					"foo.bar": &terraform.ResourceAttrDiff{
   208  						Old: "baz",
   209  						New: "qux",
   210  					},
   211  				},
   212  			},
   213  			Key:      "foo",
   214  			NewValue: map[string]interface{}{"bar": "quux"},
   215  			Expected: &terraform.InstanceDiff{
   216  				Attributes: func() map[string]*terraform.ResourceAttrDiff {
   217  					result := make(map[string]*terraform.ResourceAttrDiff)
   218  					if computed {
   219  						result["foo.%"] = &terraform.ResourceAttrDiff{
   220  							Old:         "",
   221  							New:         "",
   222  							NewComputed: true,
   223  						}
   224  						result["foo.bar"] = &terraform.ResourceAttrDiff{
   225  							Old:        "baz",
   226  							New:        "",
   227  							NewRemoved: true,
   228  						}
   229  					} else {
   230  						result["foo.bar"] = &terraform.ResourceAttrDiff{
   231  							Old: "baz",
   232  							New: "quux",
   233  						}
   234  					}
   235  					return result
   236  				}(),
   237  			},
   238  		},
   239  		resourceDiffTestCase{
   240  			Name: "additional diff with primitive",
   241  			Schema: map[string]*Schema{
   242  				"foo": &Schema{
   243  					Type:     TypeString,
   244  					Optional: true,
   245  				},
   246  				"one": &Schema{
   247  					Type:     TypeString,
   248  					Optional: true,
   249  					Computed: true,
   250  				},
   251  			},
   252  			State: &terraform.InstanceState{
   253  				Attributes: map[string]string{
   254  					"foo": "bar",
   255  					"one": "two",
   256  				},
   257  			},
   258  			Config: testConfig(t, map[string]interface{}{
   259  				"foo": "baz",
   260  			}),
   261  			Diff: &terraform.InstanceDiff{
   262  				Attributes: map[string]*terraform.ResourceAttrDiff{
   263  					"foo": &terraform.ResourceAttrDiff{
   264  						Old: "bar",
   265  						New: "baz",
   266  					},
   267  				},
   268  			},
   269  			Key:      "one",
   270  			NewValue: "four",
   271  			Expected: &terraform.InstanceDiff{
   272  				Attributes: map[string]*terraform.ResourceAttrDiff{
   273  					"foo": &terraform.ResourceAttrDiff{
   274  						Old: "bar",
   275  						New: "baz",
   276  					},
   277  					"one": &terraform.ResourceAttrDiff{
   278  						Old: "two",
   279  						New: func() string {
   280  							if computed {
   281  								return ""
   282  							}
   283  							return "four"
   284  						}(),
   285  						NewComputed: computed,
   286  					},
   287  				},
   288  			},
   289  		},
   290  		resourceDiffTestCase{
   291  			Name: "additional diff with primitive computed only",
   292  			Schema: map[string]*Schema{
   293  				"foo": &Schema{
   294  					Type:     TypeString,
   295  					Optional: true,
   296  				},
   297  				"one": &Schema{
   298  					Type:     TypeString,
   299  					Computed: true,
   300  				},
   301  			},
   302  			State: &terraform.InstanceState{
   303  				Attributes: map[string]string{
   304  					"foo": "bar",
   305  					"one": "two",
   306  				},
   307  			},
   308  			Config: testConfig(t, map[string]interface{}{
   309  				"foo": "baz",
   310  			}),
   311  			Diff: &terraform.InstanceDiff{
   312  				Attributes: map[string]*terraform.ResourceAttrDiff{
   313  					"foo": &terraform.ResourceAttrDiff{
   314  						Old: "bar",
   315  						New: "baz",
   316  					},
   317  				},
   318  			},
   319  			Key:      "one",
   320  			NewValue: "three",
   321  			Expected: &terraform.InstanceDiff{
   322  				Attributes: map[string]*terraform.ResourceAttrDiff{
   323  					"foo": &terraform.ResourceAttrDiff{
   324  						Old: "bar",
   325  						New: "baz",
   326  					},
   327  					"one": &terraform.ResourceAttrDiff{
   328  						Old: "two",
   329  						New: func() string {
   330  							if computed {
   331  								return ""
   332  							}
   333  							return "three"
   334  						}(),
   335  						NewComputed: computed,
   336  					},
   337  				},
   338  			},
   339  		},
   340  		resourceDiffTestCase{
   341  			Name: "complex-ish set diff",
   342  			Schema: map[string]*Schema{
   343  				"top": &Schema{
   344  					Type:     TypeSet,
   345  					Optional: true,
   346  					Computed: true,
   347  					Elem: &Resource{
   348  						Schema: map[string]*Schema{
   349  							"foo": &Schema{
   350  								Type:     TypeInt,
   351  								Optional: true,
   352  								Computed: true,
   353  							},
   354  							"bar": &Schema{
   355  								Type:     TypeInt,
   356  								Optional: true,
   357  								Computed: true,
   358  							},
   359  						},
   360  					},
   361  					Set: testSetFunc,
   362  				},
   363  			},
   364  			State: &terraform.InstanceState{
   365  				Attributes: map[string]string{
   366  					"top.#":      "2",
   367  					"top.3.foo":  "1",
   368  					"top.3.bar":  "2",
   369  					"top.23.foo": "11",
   370  					"top.23.bar": "12",
   371  				},
   372  			},
   373  			Config: testConfig(t, map[string]interface{}{
   374  				"top": []interface{}{
   375  					map[string]interface{}{
   376  						"foo": 1,
   377  						"bar": 3,
   378  					},
   379  					map[string]interface{}{
   380  						"foo": 12,
   381  						"bar": 12,
   382  					},
   383  				},
   384  			}),
   385  			Diff: &terraform.InstanceDiff{
   386  				Attributes: map[string]*terraform.ResourceAttrDiff{
   387  					"top.4.foo": &terraform.ResourceAttrDiff{
   388  						Old: "",
   389  						New: "1",
   390  					},
   391  					"top.4.bar": &terraform.ResourceAttrDiff{
   392  						Old: "",
   393  						New: "3",
   394  					},
   395  					"top.24.foo": &terraform.ResourceAttrDiff{
   396  						Old: "",
   397  						New: "12",
   398  					},
   399  					"top.24.bar": &terraform.ResourceAttrDiff{
   400  						Old: "",
   401  						New: "12",
   402  					},
   403  				},
   404  			},
   405  			Key: "top",
   406  			NewValue: NewSet(testSetFunc, []interface{}{
   407  				map[string]interface{}{
   408  					"foo": 1,
   409  					"bar": 4,
   410  				},
   411  				map[string]interface{}{
   412  					"foo": 13,
   413  					"bar": 12,
   414  				},
   415  				map[string]interface{}{
   416  					"foo": 21,
   417  					"bar": 22,
   418  				},
   419  			}),
   420  			Expected: &terraform.InstanceDiff{
   421  				Attributes: func() map[string]*terraform.ResourceAttrDiff {
   422  					result := make(map[string]*terraform.ResourceAttrDiff)
   423  					if computed {
   424  						result["top.#"] = &terraform.ResourceAttrDiff{
   425  							Old:         "2",
   426  							New:         "",
   427  							NewComputed: true,
   428  						}
   429  					} else {
   430  						result["top.#"] = &terraform.ResourceAttrDiff{
   431  							Old: "2",
   432  							New: "3",
   433  						}
   434  						result["top.5.foo"] = &terraform.ResourceAttrDiff{
   435  							Old: "",
   436  							New: "1",
   437  						}
   438  						result["top.5.bar"] = &terraform.ResourceAttrDiff{
   439  							Old: "",
   440  							New: "4",
   441  						}
   442  						result["top.25.foo"] = &terraform.ResourceAttrDiff{
   443  							Old: "",
   444  							New: "13",
   445  						}
   446  						result["top.25.bar"] = &terraform.ResourceAttrDiff{
   447  							Old: "",
   448  							New: "12",
   449  						}
   450  						result["top.43.foo"] = &terraform.ResourceAttrDiff{
   451  							Old: "",
   452  							New: "21",
   453  						}
   454  						result["top.43.bar"] = &terraform.ResourceAttrDiff{
   455  							Old: "",
   456  							New: "22",
   457  						}
   458  					}
   459  					return result
   460  				}(),
   461  			},
   462  		},
   463  		resourceDiffTestCase{
   464  			Name: "primitive, no diff, no refresh",
   465  			Schema: map[string]*Schema{
   466  				"foo": &Schema{
   467  					Type:     TypeString,
   468  					Computed: true,
   469  				},
   470  			},
   471  			State: &terraform.InstanceState{
   472  				Attributes: map[string]string{
   473  					"foo": "bar",
   474  				},
   475  			},
   476  			Config:   testConfig(t, map[string]interface{}{}),
   477  			Diff:     &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}},
   478  			Key:      "foo",
   479  			NewValue: "baz",
   480  			Expected: &terraform.InstanceDiff{
   481  				Attributes: map[string]*terraform.ResourceAttrDiff{
   482  					"foo": &terraform.ResourceAttrDiff{
   483  						Old: "bar",
   484  						New: func() string {
   485  							if computed {
   486  								return ""
   487  							}
   488  							return "baz"
   489  						}(),
   490  						NewComputed: computed,
   491  					},
   492  				},
   493  			},
   494  		},
   495  		resourceDiffTestCase{
   496  			Name: "non-computed key, should error",
   497  			Schema: map[string]*Schema{
   498  				"foo": &Schema{
   499  					Type:     TypeString,
   500  					Required: true,
   501  				},
   502  			},
   503  			State: &terraform.InstanceState{
   504  				Attributes: map[string]string{
   505  					"foo": "bar",
   506  				},
   507  			},
   508  			Config: testConfig(t, map[string]interface{}{
   509  				"foo": "baz",
   510  			}),
   511  			Diff: &terraform.InstanceDiff{
   512  				Attributes: map[string]*terraform.ResourceAttrDiff{
   513  					"foo": &terraform.ResourceAttrDiff{
   514  						Old: "bar",
   515  						New: "baz",
   516  					},
   517  				},
   518  			},
   519  			Key:           "foo",
   520  			NewValue:      "qux",
   521  			ExpectedError: true,
   522  		},
   523  		resourceDiffTestCase{
   524  			Name: "bad key, should error",
   525  			Schema: map[string]*Schema{
   526  				"foo": &Schema{
   527  					Type:     TypeString,
   528  					Required: true,
   529  				},
   530  			},
   531  			State: &terraform.InstanceState{
   532  				Attributes: map[string]string{
   533  					"foo": "bar",
   534  				},
   535  			},
   536  			Config: testConfig(t, map[string]interface{}{
   537  				"foo": "baz",
   538  			}),
   539  			Diff: &terraform.InstanceDiff{
   540  				Attributes: map[string]*terraform.ResourceAttrDiff{
   541  					"foo": &terraform.ResourceAttrDiff{
   542  						Old: "bar",
   543  						New: "baz",
   544  					},
   545  				},
   546  			},
   547  			Key:           "bad",
   548  			NewValue:      "qux",
   549  			ExpectedError: true,
   550  		},
   551  	}
   552  }
   553  
   554  func TestSetNew(t *testing.T) {
   555  	testCases := testDiffCases(t, "", 0, false)
   556  	for _, tc := range testCases {
   557  		t.Run(tc.Name, func(t *testing.T) {
   558  			m := schemaMap(tc.Schema)
   559  			d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff)
   560  			err := d.SetNew(tc.Key, tc.NewValue)
   561  			switch {
   562  			case err != nil && !tc.ExpectedError:
   563  				t.Fatalf("bad: %s", err)
   564  			case err == nil && tc.ExpectedError:
   565  				t.Fatalf("Expected error, got none")
   566  			case err != nil && tc.ExpectedError:
   567  				return
   568  			}
   569  			for _, k := range d.UpdatedKeys() {
   570  				if err := m.diff(k, m[k], tc.Diff, d, false); err != nil {
   571  					t.Fatalf("bad: %s", err)
   572  				}
   573  			}
   574  			if !reflect.DeepEqual(tc.Expected, tc.Diff) {
   575  				t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff))
   576  			}
   577  		})
   578  	}
   579  }
   580  
   581  func TestSetNewComputed(t *testing.T) {
   582  	testCases := testDiffCases(t, "", 0, true)
   583  	for _, tc := range testCases {
   584  		t.Run(tc.Name, func(t *testing.T) {
   585  			m := schemaMap(tc.Schema)
   586  			d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff)
   587  			err := d.SetNewComputed(tc.Key)
   588  			switch {
   589  			case err != nil && !tc.ExpectedError:
   590  				t.Fatalf("bad: %s", err)
   591  			case err == nil && tc.ExpectedError:
   592  				t.Fatalf("Expected error, got none")
   593  			case err != nil && tc.ExpectedError:
   594  				return
   595  			}
   596  			for _, k := range d.UpdatedKeys() {
   597  				if err := m.diff(k, m[k], tc.Diff, d, false); err != nil {
   598  					t.Fatalf("bad: %s", err)
   599  				}
   600  			}
   601  			if !reflect.DeepEqual(tc.Expected, tc.Diff) {
   602  				t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff))
   603  			}
   604  		})
   605  	}
   606  }
   607  
   608  func TestForceNew(t *testing.T) {
   609  	cases := []resourceDiffTestCase{
   610  		resourceDiffTestCase{
   611  			Name: "basic primitive diff",
   612  			Schema: map[string]*Schema{
   613  				"foo": &Schema{
   614  					Type:     TypeString,
   615  					Optional: true,
   616  					Computed: true,
   617  				},
   618  			},
   619  			State: &terraform.InstanceState{
   620  				Attributes: map[string]string{
   621  					"foo": "bar",
   622  				},
   623  			},
   624  			Config: testConfig(t, map[string]interface{}{
   625  				"foo": "baz",
   626  			}),
   627  			Diff: &terraform.InstanceDiff{
   628  				Attributes: map[string]*terraform.ResourceAttrDiff{
   629  					"foo": &terraform.ResourceAttrDiff{
   630  						Old: "bar",
   631  						New: "baz",
   632  					},
   633  				},
   634  			},
   635  			Key: "foo",
   636  			Expected: &terraform.InstanceDiff{
   637  				Attributes: map[string]*terraform.ResourceAttrDiff{
   638  					"foo": &terraform.ResourceAttrDiff{
   639  						Old:         "bar",
   640  						New:         "baz",
   641  						RequiresNew: true,
   642  					},
   643  				},
   644  			},
   645  		},
   646  		resourceDiffTestCase{
   647  			Name: "no change, should error",
   648  			Schema: map[string]*Schema{
   649  				"foo": &Schema{
   650  					Type:     TypeString,
   651  					Optional: true,
   652  					Computed: true,
   653  				},
   654  			},
   655  			State: &terraform.InstanceState{
   656  				Attributes: map[string]string{
   657  					"foo": "bar",
   658  				},
   659  			},
   660  			Config: testConfig(t, map[string]interface{}{
   661  				"foo": "bar",
   662  			}),
   663  			ExpectedError: true,
   664  		},
   665  		resourceDiffTestCase{
   666  			Name: "basic primitive, non-computed key",
   667  			Schema: map[string]*Schema{
   668  				"foo": &Schema{
   669  					Type:     TypeString,
   670  					Required: true,
   671  				},
   672  			},
   673  			State: &terraform.InstanceState{
   674  				Attributes: map[string]string{
   675  					"foo": "bar",
   676  				},
   677  			},
   678  			Config: testConfig(t, map[string]interface{}{
   679  				"foo": "baz",
   680  			}),
   681  			Diff: &terraform.InstanceDiff{
   682  				Attributes: map[string]*terraform.ResourceAttrDiff{
   683  					"foo": &terraform.ResourceAttrDiff{
   684  						Old: "bar",
   685  						New: "baz",
   686  					},
   687  				},
   688  			},
   689  			Key: "foo",
   690  			Expected: &terraform.InstanceDiff{
   691  				Attributes: map[string]*terraform.ResourceAttrDiff{
   692  					"foo": &terraform.ResourceAttrDiff{
   693  						Old:         "bar",
   694  						New:         "baz",
   695  						RequiresNew: true,
   696  					},
   697  				},
   698  			},
   699  		},
   700  	}
   701  	for _, tc := range cases {
   702  		t.Run(tc.Name, func(t *testing.T) {
   703  			m := schemaMap(tc.Schema)
   704  			d := newResourceDiff(m, tc.Config, tc.State, tc.Diff)
   705  			err := d.ForceNew(tc.Key)
   706  			switch {
   707  			case err != nil && !tc.ExpectedError:
   708  				t.Fatalf("bad: %s", err)
   709  			case err == nil && tc.ExpectedError:
   710  				t.Fatalf("Expected error, got none")
   711  			case err != nil && tc.ExpectedError:
   712  				return
   713  			}
   714  			for _, k := range d.UpdatedKeys() {
   715  				if err := m.diff(k, m[k], tc.Diff, d, false); err != nil {
   716  					t.Fatalf("bad: %s", err)
   717  				}
   718  			}
   719  			if !reflect.DeepEqual(tc.Expected, tc.Diff) {
   720  				t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff))
   721  			}
   722  		})
   723  	}
   724  }
   725  
   726  func TestClear(t *testing.T) {
   727  	cases := []resourceDiffTestCase{
   728  		resourceDiffTestCase{
   729  			Name: "basic primitive diff",
   730  			Schema: map[string]*Schema{
   731  				"foo": &Schema{
   732  					Type:     TypeString,
   733  					Optional: true,
   734  					Computed: true,
   735  				},
   736  			},
   737  			State: &terraform.InstanceState{
   738  				Attributes: map[string]string{
   739  					"foo": "bar",
   740  				},
   741  			},
   742  			Config: testConfig(t, map[string]interface{}{
   743  				"foo": "baz",
   744  			}),
   745  			Diff: &terraform.InstanceDiff{
   746  				Attributes: map[string]*terraform.ResourceAttrDiff{
   747  					"foo": &terraform.ResourceAttrDiff{
   748  						Old: "bar",
   749  						New: "baz",
   750  					},
   751  				},
   752  			},
   753  			Key:      "foo",
   754  			Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}},
   755  		},
   756  		resourceDiffTestCase{
   757  			Name: "non-computed key, should error",
   758  			Schema: map[string]*Schema{
   759  				"foo": &Schema{
   760  					Type:     TypeString,
   761  					Required: true,
   762  				},
   763  			},
   764  			State: &terraform.InstanceState{
   765  				Attributes: map[string]string{
   766  					"foo": "bar",
   767  				},
   768  			},
   769  			Config: testConfig(t, map[string]interface{}{
   770  				"foo": "baz",
   771  			}),
   772  			Diff: &terraform.InstanceDiff{
   773  				Attributes: map[string]*terraform.ResourceAttrDiff{
   774  					"foo": &terraform.ResourceAttrDiff{
   775  						Old: "bar",
   776  						New: "baz",
   777  					},
   778  				},
   779  			},
   780  			Key:           "foo",
   781  			ExpectedError: true,
   782  		},
   783  		resourceDiffTestCase{
   784  			Name: "multi-value, one removed",
   785  			Schema: map[string]*Schema{
   786  				"foo": &Schema{
   787  					Type:     TypeString,
   788  					Optional: true,
   789  					Computed: true,
   790  				},
   791  				"one": &Schema{
   792  					Type:     TypeString,
   793  					Optional: true,
   794  					Computed: true,
   795  				},
   796  			},
   797  			State: &terraform.InstanceState{
   798  				Attributes: map[string]string{
   799  					"foo": "bar",
   800  					"one": "two",
   801  				},
   802  			},
   803  			Config: testConfig(t, map[string]interface{}{
   804  				"foo": "baz",
   805  				"one": "three",
   806  			}),
   807  			Diff: &terraform.InstanceDiff{
   808  				Attributes: map[string]*terraform.ResourceAttrDiff{
   809  					"foo": &terraform.ResourceAttrDiff{
   810  						Old: "bar",
   811  						New: "baz",
   812  					},
   813  					"one": &terraform.ResourceAttrDiff{
   814  						Old: "two",
   815  						New: "three",
   816  					},
   817  				},
   818  			},
   819  			Key: "one",
   820  			Expected: &terraform.InstanceDiff{
   821  				Attributes: map[string]*terraform.ResourceAttrDiff{
   822  					"foo": &terraform.ResourceAttrDiff{
   823  						Old: "bar",
   824  						New: "baz",
   825  					},
   826  				},
   827  			},
   828  		},
   829  	}
   830  	for _, tc := range cases {
   831  		t.Run(tc.Name, func(t *testing.T) {
   832  			m := schemaMap(tc.Schema)
   833  			d := newResourceDiff(m, tc.Config, tc.State, tc.Diff)
   834  			err := d.Clear(tc.Key)
   835  			switch {
   836  			case err != nil && !tc.ExpectedError:
   837  				t.Fatalf("bad: %s", err)
   838  			case err == nil && tc.ExpectedError:
   839  				t.Fatalf("Expected error, got none")
   840  			case err != nil && tc.ExpectedError:
   841  				return
   842  			}
   843  			for _, k := range d.UpdatedKeys() {
   844  				if err := m.diff(k, m[k], tc.Diff, d, false); err != nil {
   845  					t.Fatalf("bad: %s", err)
   846  				}
   847  			}
   848  			if !reflect.DeepEqual(tc.Expected, tc.Diff) {
   849  				t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff))
   850  			}
   851  		})
   852  	}
   853  }