github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/resource_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema"
    10  	"github.com/zclconf/go-cty/cty"
    11  
    12  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim"
    13  	"github.com/mitchellh/reflectwalk"
    14  )
    15  
    16  func TestInstanceInfoResourceAddress(t *testing.T) {
    17  	tests := []struct {
    18  		Input *InstanceInfo
    19  		Want  string
    20  	}{
    21  		{
    22  			&InstanceInfo{
    23  				Id: "test_resource.baz",
    24  			},
    25  			"test_resource.baz",
    26  		},
    27  		{
    28  			&InstanceInfo{
    29  				Id:         "test_resource.baz",
    30  				ModulePath: rootModulePath,
    31  			},
    32  			"test_resource.baz",
    33  		},
    34  		{
    35  			&InstanceInfo{
    36  				Id:         "test_resource.baz",
    37  				ModulePath: []string{"root", "foo"},
    38  			},
    39  			"module.foo.test_resource.baz",
    40  		},
    41  		{
    42  			&InstanceInfo{
    43  				Id:         "test_resource.baz",
    44  				ModulePath: []string{"root", "foo", "bar"},
    45  			},
    46  			"module.foo.module.bar.test_resource.baz",
    47  		},
    48  		{
    49  			&InstanceInfo{
    50  				Id: "test_resource.baz (tainted)",
    51  			},
    52  			"test_resource.baz.tainted",
    53  		},
    54  		{
    55  			&InstanceInfo{
    56  				Id: "test_resource.baz (deposed #0)",
    57  			},
    58  			"test_resource.baz.deposed",
    59  		},
    60  	}
    61  
    62  	for i, test := range tests {
    63  		t.Run(strconv.Itoa(i), func(t *testing.T) {
    64  			gotAddr := test.Input.ResourceAddress()
    65  			got := gotAddr.String()
    66  			if got != test.Want {
    67  				t.Fatalf("wrong result\ngot:  %s\nwant: %s", got, test.Want)
    68  			}
    69  		})
    70  	}
    71  }
    72  
    73  func TestResourceConfigGet(t *testing.T) {
    74  	fooStringSchema := &configschema.Block{
    75  		Attributes: map[string]*configschema.Attribute{
    76  			"foo": {Type: cty.String, Optional: true},
    77  		},
    78  	}
    79  	fooListSchema := &configschema.Block{
    80  		Attributes: map[string]*configschema.Attribute{
    81  			"foo": {Type: cty.List(cty.Number), Optional: true},
    82  		},
    83  	}
    84  
    85  	cases := []struct {
    86  		Config cty.Value
    87  		Schema *configschema.Block
    88  		Key    string
    89  		Value  interface{}
    90  	}{
    91  		{
    92  			Config: cty.ObjectVal(map[string]cty.Value{
    93  				"foo": cty.StringVal("bar"),
    94  			}),
    95  			Schema: fooStringSchema,
    96  			Key:    "foo",
    97  			Value:  "bar",
    98  		},
    99  
   100  		{
   101  			Config: cty.ObjectVal(map[string]cty.Value{
   102  				"foo": cty.UnknownVal(cty.String),
   103  			}),
   104  			Schema: fooStringSchema,
   105  			Key:    "foo",
   106  			Value:  hcl2shim.UnknownVariableValue,
   107  		},
   108  
   109  		{
   110  			Config: cty.ObjectVal(map[string]cty.Value{
   111  				"foo": cty.ListVal([]cty.Value{
   112  					cty.NumberIntVal(1),
   113  					cty.NumberIntVal(2),
   114  					cty.NumberIntVal(5),
   115  				}),
   116  			}),
   117  			Schema: fooListSchema,
   118  			Key:    "foo.0",
   119  			Value:  1,
   120  		},
   121  
   122  		{
   123  			Config: cty.ObjectVal(map[string]cty.Value{
   124  				"foo": cty.ListVal([]cty.Value{
   125  					cty.NumberIntVal(1),
   126  					cty.NumberIntVal(2),
   127  					cty.NumberIntVal(5),
   128  				}),
   129  			}),
   130  			Schema: fooListSchema,
   131  			Key:    "foo.5",
   132  			Value:  nil,
   133  		},
   134  
   135  		{
   136  			Config: cty.ObjectVal(map[string]cty.Value{
   137  				"foo": cty.ListVal([]cty.Value{
   138  					cty.NumberIntVal(1),
   139  					cty.NumberIntVal(2),
   140  					cty.NumberIntVal(5),
   141  				}),
   142  			}),
   143  			Schema: fooListSchema,
   144  			Key:    "foo.-1",
   145  			Value:  nil,
   146  		},
   147  
   148  		// get from map
   149  		{
   150  			Config: cty.ObjectVal(map[string]cty.Value{
   151  				"mapname": cty.ListVal([]cty.Value{
   152  					cty.MapVal(map[string]cty.Value{
   153  						"key": cty.NumberIntVal(1),
   154  					}),
   155  				}),
   156  			}),
   157  			Schema: &configschema.Block{
   158  				Attributes: map[string]*configschema.Attribute{
   159  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   160  				},
   161  			},
   162  			Key:   "mapname.0.key",
   163  			Value: 1,
   164  		},
   165  
   166  		// get from map with dot in key
   167  		{
   168  			Config: cty.ObjectVal(map[string]cty.Value{
   169  				"mapname": cty.ListVal([]cty.Value{
   170  					cty.MapVal(map[string]cty.Value{
   171  						"key.name": cty.NumberIntVal(1),
   172  					}),
   173  				}),
   174  			}),
   175  			Schema: &configschema.Block{
   176  				Attributes: map[string]*configschema.Attribute{
   177  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   178  				},
   179  			},
   180  			Key:   "mapname.0.key.name",
   181  			Value: 1,
   182  		},
   183  
   184  		// get from map with overlapping key names
   185  		{
   186  			Config: cty.ObjectVal(map[string]cty.Value{
   187  				"mapname": cty.ListVal([]cty.Value{
   188  					cty.MapVal(map[string]cty.Value{
   189  						"key.name":   cty.NumberIntVal(1),
   190  						"key.name.2": cty.NumberIntVal(2),
   191  					}),
   192  				}),
   193  			}),
   194  			Schema: &configschema.Block{
   195  				Attributes: map[string]*configschema.Attribute{
   196  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   197  				},
   198  			},
   199  			Key:   "mapname.0.key.name.2",
   200  			Value: 2,
   201  		},
   202  		{
   203  			Config: cty.ObjectVal(map[string]cty.Value{
   204  				"mapname": cty.ListVal([]cty.Value{
   205  					cty.MapVal(map[string]cty.Value{
   206  						"key.name":     cty.NumberIntVal(1),
   207  						"key.name.foo": cty.NumberIntVal(2),
   208  					}),
   209  				}),
   210  			}),
   211  			Schema: &configschema.Block{
   212  				Attributes: map[string]*configschema.Attribute{
   213  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   214  				},
   215  			},
   216  			Key:   "mapname.0.key.name",
   217  			Value: 1,
   218  		},
   219  		{
   220  			Config: cty.ObjectVal(map[string]cty.Value{
   221  				"mapname": cty.ListVal([]cty.Value{
   222  					cty.MapVal(map[string]cty.Value{
   223  						"listkey": cty.ListVal([]cty.Value{
   224  							cty.MapVal(map[string]cty.Value{
   225  								"key": cty.NumberIntVal(3),
   226  							}),
   227  						}),
   228  					}),
   229  				}),
   230  			}),
   231  			Schema: &configschema.Block{
   232  				Attributes: map[string]*configschema.Attribute{
   233  					"mapname": {Type: cty.List(cty.Map(cty.List(cty.Map(cty.Number)))), Optional: true},
   234  				},
   235  			},
   236  			Key:   "mapname.0.listkey.0.key",
   237  			Value: 3,
   238  		},
   239  	}
   240  
   241  	for i, tc := range cases {
   242  		rc := NewResourceConfigShimmed(tc.Config, tc.Schema)
   243  
   244  		// Test getting a key
   245  		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
   246  			v, ok := rc.Get(tc.Key)
   247  			if ok && v == nil {
   248  				t.Fatal("(nil, true) returned from Get")
   249  			}
   250  
   251  			if !reflect.DeepEqual(v, tc.Value) {
   252  				t.Fatalf("%d bad: %#v", i, v)
   253  			}
   254  		})
   255  
   256  		// Test copying and equality
   257  		t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
   258  			copy := rc.DeepCopy()
   259  			if !reflect.DeepEqual(copy, rc) {
   260  				t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
   261  			}
   262  
   263  			if !copy.Equal(rc) {
   264  				t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
   265  			}
   266  			if !rc.Equal(copy) {
   267  				t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
   268  			}
   269  		})
   270  	}
   271  }
   272  
   273  func TestResourceConfigDeepCopy_nil(t *testing.T) {
   274  	var nilRc *ResourceConfig
   275  	actual := nilRc.DeepCopy()
   276  	if actual != nil {
   277  		t.Fatalf("bad: %#v", actual)
   278  	}
   279  }
   280  
   281  func TestResourceConfigDeepCopy_nilComputed(t *testing.T) {
   282  	rc := &ResourceConfig{}
   283  	actual := rc.DeepCopy()
   284  	if actual.ComputedKeys != nil {
   285  		t.Fatalf("bad: %#v", actual)
   286  	}
   287  }
   288  
   289  func TestResourceConfigEqual_nil(t *testing.T) {
   290  	var nilRc *ResourceConfig
   291  	notNil := NewResourceConfigShimmed(cty.EmptyObjectVal, &configschema.Block{})
   292  
   293  	if nilRc.Equal(notNil) {
   294  		t.Fatal("should not be equal")
   295  	}
   296  
   297  	if notNil.Equal(nilRc) {
   298  		t.Fatal("should not be equal")
   299  	}
   300  }
   301  
   302  func TestResourceConfigEqual_computedKeyOrder(t *testing.T) {
   303  	v := cty.ObjectVal(map[string]cty.Value{
   304  		"foo": cty.UnknownVal(cty.String),
   305  	})
   306  	schema := &configschema.Block{
   307  		Attributes: map[string]*configschema.Attribute{
   308  			"foo": {Type: cty.String, Optional: true},
   309  		},
   310  	}
   311  	rc := NewResourceConfigShimmed(v, schema)
   312  	rc2 := NewResourceConfigShimmed(v, schema)
   313  
   314  	// Set the computed keys manually to force ordering to differ
   315  	rc.ComputedKeys = []string{"foo", "bar"}
   316  	rc2.ComputedKeys = []string{"bar", "foo"}
   317  
   318  	if !rc.Equal(rc2) {
   319  		t.Fatal("should be equal")
   320  	}
   321  }
   322  
   323  func TestUnknownCheckWalker(t *testing.T) {
   324  	cases := []struct {
   325  		Name   string
   326  		Input  interface{}
   327  		Result bool
   328  	}{
   329  		{
   330  			"primitive",
   331  			42,
   332  			false,
   333  		},
   334  
   335  		{
   336  			"primitive computed",
   337  			hcl2shim.UnknownVariableValue,
   338  			true,
   339  		},
   340  
   341  		{
   342  			"list",
   343  			[]interface{}{"foo", hcl2shim.UnknownVariableValue},
   344  			true,
   345  		},
   346  
   347  		{
   348  			"nested list",
   349  			[]interface{}{
   350  				"foo",
   351  				[]interface{}{hcl2shim.UnknownVariableValue},
   352  			},
   353  			true,
   354  		},
   355  	}
   356  
   357  	for i, tc := range cases {
   358  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   359  			var w unknownCheckWalker
   360  			if err := reflectwalk.Walk(tc.Input, &w); err != nil {
   361  				t.Fatalf("err: %s", err)
   362  			}
   363  
   364  			if w.Unknown != tc.Result {
   365  				t.Fatalf("bad: %v", w.Unknown)
   366  			}
   367  		})
   368  	}
   369  }
   370  
   371  func TestNewResourceConfigShimmed(t *testing.T) {
   372  	for _, tc := range []struct {
   373  		Name     string
   374  		Val      cty.Value
   375  		Schema   *configschema.Block
   376  		Expected *ResourceConfig
   377  	}{
   378  		{
   379  			Name: "empty object",
   380  			Val:  cty.NullVal(cty.EmptyObject),
   381  			Schema: &configschema.Block{
   382  				Attributes: map[string]*configschema.Attribute{
   383  					"foo": {
   384  						Type:     cty.String,
   385  						Optional: true,
   386  					},
   387  				},
   388  			},
   389  			Expected: &ResourceConfig{
   390  				Raw:    map[string]interface{}{},
   391  				Config: map[string]interface{}{},
   392  			},
   393  		},
   394  		{
   395  			Name: "basic",
   396  			Val: cty.ObjectVal(map[string]cty.Value{
   397  				"foo": cty.StringVal("bar"),
   398  			}),
   399  			Schema: &configschema.Block{
   400  				Attributes: map[string]*configschema.Attribute{
   401  					"foo": {
   402  						Type:     cty.String,
   403  						Optional: true,
   404  					},
   405  				},
   406  			},
   407  			Expected: &ResourceConfig{
   408  				Raw: map[string]interface{}{
   409  					"foo": "bar",
   410  				},
   411  				Config: map[string]interface{}{
   412  					"foo": "bar",
   413  				},
   414  			},
   415  		},
   416  		{
   417  			Name: "null string",
   418  			Val: cty.ObjectVal(map[string]cty.Value{
   419  				"foo": cty.NullVal(cty.String),
   420  			}),
   421  			Schema: &configschema.Block{
   422  				Attributes: map[string]*configschema.Attribute{
   423  					"foo": {
   424  						Type:     cty.String,
   425  						Optional: true,
   426  					},
   427  				},
   428  			},
   429  			Expected: &ResourceConfig{
   430  				Raw:    map[string]interface{}{},
   431  				Config: map[string]interface{}{},
   432  			},
   433  		},
   434  		{
   435  			Name: "unknown string",
   436  			Val: cty.ObjectVal(map[string]cty.Value{
   437  				"foo": cty.UnknownVal(cty.String),
   438  			}),
   439  			Schema: &configschema.Block{
   440  				Attributes: map[string]*configschema.Attribute{
   441  					"foo": {
   442  						Type:     cty.String,
   443  						Optional: true,
   444  					},
   445  				},
   446  			},
   447  			Expected: &ResourceConfig{
   448  				ComputedKeys: []string{"foo"},
   449  				Raw: map[string]interface{}{
   450  					"foo": hcl2shim.UnknownVariableValue,
   451  				},
   452  				Config: map[string]interface{}{
   453  					"foo": hcl2shim.UnknownVariableValue,
   454  				},
   455  			},
   456  		},
   457  		{
   458  			Name: "unknown collections",
   459  			Val: cty.ObjectVal(map[string]cty.Value{
   460  				"bar": cty.UnknownVal(cty.Map(cty.String)),
   461  				"baz": cty.UnknownVal(cty.List(cty.String)),
   462  			}),
   463  			Schema: &configschema.Block{
   464  				Attributes: map[string]*configschema.Attribute{
   465  					"bar": {
   466  						Type:     cty.Map(cty.String),
   467  						Required: true,
   468  					},
   469  					"baz": {
   470  						Type:     cty.List(cty.String),
   471  						Optional: true,
   472  					},
   473  				},
   474  			},
   475  			Expected: &ResourceConfig{
   476  				ComputedKeys: []string{"bar", "baz"},
   477  				Raw: map[string]interface{}{
   478  					"bar": hcl2shim.UnknownVariableValue,
   479  					"baz": hcl2shim.UnknownVariableValue,
   480  				},
   481  				Config: map[string]interface{}{
   482  					"bar": hcl2shim.UnknownVariableValue,
   483  					"baz": hcl2shim.UnknownVariableValue,
   484  				},
   485  			},
   486  		},
   487  		{
   488  			Name: "null collections",
   489  			Val: cty.ObjectVal(map[string]cty.Value{
   490  				"bar": cty.NullVal(cty.Map(cty.String)),
   491  				"baz": cty.NullVal(cty.List(cty.String)),
   492  			}),
   493  			Schema: &configschema.Block{
   494  				Attributes: map[string]*configschema.Attribute{
   495  					"bar": {
   496  						Type:     cty.Map(cty.String),
   497  						Required: true,
   498  					},
   499  					"baz": {
   500  						Type:     cty.List(cty.String),
   501  						Optional: true,
   502  					},
   503  				},
   504  			},
   505  			Expected: &ResourceConfig{
   506  				Raw:    map[string]interface{}{},
   507  				Config: map[string]interface{}{},
   508  			},
   509  		},
   510  		{
   511  			Name: "unknown blocks",
   512  			Val: cty.ObjectVal(map[string]cty.Value{
   513  				"bar": cty.UnknownVal(cty.Map(cty.String)),
   514  				"baz": cty.UnknownVal(cty.List(cty.String)),
   515  			}),
   516  			Schema: &configschema.Block{
   517  				BlockTypes: map[string]*configschema.NestedBlock{
   518  					"bar": {
   519  						Block:   configschema.Block{},
   520  						Nesting: configschema.NestingList,
   521  					},
   522  					"baz": {
   523  						Block:   configschema.Block{},
   524  						Nesting: configschema.NestingSet,
   525  					},
   526  				},
   527  			},
   528  			Expected: &ResourceConfig{
   529  				ComputedKeys: []string{"bar", "baz"},
   530  				Raw: map[string]interface{}{
   531  					"bar": hcl2shim.UnknownVariableValue,
   532  					"baz": hcl2shim.UnknownVariableValue,
   533  				},
   534  				Config: map[string]interface{}{
   535  					"bar": hcl2shim.UnknownVariableValue,
   536  					"baz": hcl2shim.UnknownVariableValue,
   537  				},
   538  			},
   539  		},
   540  		{
   541  			Name: "unknown in nested blocks",
   542  			Val: cty.ObjectVal(map[string]cty.Value{
   543  				"bar": cty.ListVal([]cty.Value{
   544  					cty.ObjectVal(map[string]cty.Value{
   545  						"baz": cty.ListVal([]cty.Value{
   546  							cty.ObjectVal(map[string]cty.Value{
   547  								"list": cty.UnknownVal(cty.List(cty.String)),
   548  							}),
   549  						}),
   550  					}),
   551  				}),
   552  			}),
   553  			Schema: &configschema.Block{
   554  				BlockTypes: map[string]*configschema.NestedBlock{
   555  					"bar": {
   556  						Block: configschema.Block{
   557  							BlockTypes: map[string]*configschema.NestedBlock{
   558  								"baz": {
   559  									Block: configschema.Block{
   560  										Attributes: map[string]*configschema.Attribute{
   561  											"list": {Type: cty.List(cty.String),
   562  												Optional: true,
   563  											},
   564  										},
   565  									},
   566  									Nesting: configschema.NestingList,
   567  								},
   568  							},
   569  						},
   570  						Nesting: configschema.NestingList,
   571  					},
   572  				},
   573  			},
   574  			Expected: &ResourceConfig{
   575  				ComputedKeys: []string{"bar.0.baz.0.list"},
   576  				Raw: map[string]interface{}{
   577  					"bar": []interface{}{map[string]interface{}{
   578  						"baz": []interface{}{map[string]interface{}{
   579  							"list": "74D93920-ED26-11E3-AC10-0800200C9A66",
   580  						}},
   581  					}},
   582  				},
   583  				Config: map[string]interface{}{
   584  					"bar": []interface{}{map[string]interface{}{
   585  						"baz": []interface{}{map[string]interface{}{
   586  							"list": "74D93920-ED26-11E3-AC10-0800200C9A66",
   587  						}},
   588  					}},
   589  				},
   590  			},
   591  		},
   592  		{
   593  			Name: "unknown in set",
   594  			Val: cty.ObjectVal(map[string]cty.Value{
   595  				"bar": cty.SetVal([]cty.Value{
   596  					cty.ObjectVal(map[string]cty.Value{
   597  						"val": cty.UnknownVal(cty.String),
   598  					}),
   599  				}),
   600  			}),
   601  			Schema: &configschema.Block{
   602  				BlockTypes: map[string]*configschema.NestedBlock{
   603  					"bar": {
   604  						Block: configschema.Block{
   605  							Attributes: map[string]*configschema.Attribute{
   606  								"val": {
   607  									Type:     cty.String,
   608  									Optional: true,
   609  								},
   610  							},
   611  						},
   612  						Nesting: configschema.NestingSet,
   613  					},
   614  				},
   615  			},
   616  			Expected: &ResourceConfig{
   617  				ComputedKeys: []string{"bar.0.val"},
   618  				Raw: map[string]interface{}{
   619  					"bar": []interface{}{map[string]interface{}{
   620  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   621  					}},
   622  				},
   623  				Config: map[string]interface{}{
   624  					"bar": []interface{}{map[string]interface{}{
   625  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   626  					}},
   627  				},
   628  			},
   629  		},
   630  		{
   631  			Name: "unknown in attribute sets",
   632  			Val: cty.ObjectVal(map[string]cty.Value{
   633  				"bar": cty.SetVal([]cty.Value{
   634  					cty.ObjectVal(map[string]cty.Value{
   635  						"val": cty.UnknownVal(cty.String),
   636  					}),
   637  				}),
   638  				"baz": cty.SetVal([]cty.Value{
   639  					cty.ObjectVal(map[string]cty.Value{
   640  						"obj": cty.UnknownVal(cty.Object(map[string]cty.Type{
   641  							"attr": cty.List(cty.String),
   642  						})),
   643  					}),
   644  					cty.ObjectVal(map[string]cty.Value{
   645  						"obj": cty.ObjectVal(map[string]cty.Value{
   646  							"attr": cty.UnknownVal(cty.List(cty.String)),
   647  						}),
   648  					}),
   649  				}),
   650  			}),
   651  			Schema: &configschema.Block{
   652  				Attributes: map[string]*configschema.Attribute{
   653  					"bar": {
   654  						Type: cty.Set(cty.Object(map[string]cty.Type{
   655  							"val": cty.String,
   656  						})),
   657  					},
   658  					"baz": {
   659  						Type: cty.Set(cty.Object(map[string]cty.Type{
   660  							"obj": cty.Object(map[string]cty.Type{
   661  								"attr": cty.List(cty.String),
   662  							}),
   663  						})),
   664  					},
   665  				},
   666  			},
   667  			Expected: &ResourceConfig{
   668  				ComputedKeys: []string{"bar.0.val", "baz.0.obj.attr", "baz.1.obj"},
   669  				Raw: map[string]interface{}{
   670  					"bar": []interface{}{map[string]interface{}{
   671  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   672  					}},
   673  					"baz": []interface{}{
   674  						map[string]interface{}{
   675  							"obj": map[string]interface{}{
   676  								"attr": "74D93920-ED26-11E3-AC10-0800200C9A66",
   677  							},
   678  						},
   679  						map[string]interface{}{
   680  							"obj": "74D93920-ED26-11E3-AC10-0800200C9A66",
   681  						},
   682  					},
   683  				},
   684  				Config: map[string]interface{}{
   685  					"bar": []interface{}{map[string]interface{}{
   686  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   687  					}},
   688  					"baz": []interface{}{
   689  						map[string]interface{}{
   690  							"obj": map[string]interface{}{
   691  								"attr": "74D93920-ED26-11E3-AC10-0800200C9A66",
   692  							},
   693  						},
   694  						map[string]interface{}{
   695  							"obj": "74D93920-ED26-11E3-AC10-0800200C9A66",
   696  						},
   697  					},
   698  				},
   699  			},
   700  		},
   701  		{
   702  			Name: "null blocks",
   703  			Val: cty.ObjectVal(map[string]cty.Value{
   704  				"bar": cty.NullVal(cty.Map(cty.String)),
   705  				"baz": cty.NullVal(cty.List(cty.String)),
   706  			}),
   707  			Schema: &configschema.Block{
   708  				BlockTypes: map[string]*configschema.NestedBlock{
   709  					"bar": {
   710  						Block:   configschema.Block{},
   711  						Nesting: configschema.NestingMap,
   712  					},
   713  					"baz": {
   714  						Block:   configschema.Block{},
   715  						Nesting: configschema.NestingSingle,
   716  					},
   717  				},
   718  			},
   719  			Expected: &ResourceConfig{
   720  				Raw:    map[string]interface{}{},
   721  				Config: map[string]interface{}{},
   722  			},
   723  		},
   724  	} {
   725  		t.Run(tc.Name, func(*testing.T) {
   726  			cfg := NewResourceConfigShimmed(tc.Val, tc.Schema)
   727  			if !tc.Expected.Equal(cfg) {
   728  				t.Fatalf("expected:\n%#v\ngot:\n%#v", tc.Expected, cfg)
   729  			}
   730  		})
   731  	}
   732  }