diff --git a/helper/resource/testing.go b/helper/resource/testing.go index 00da8a92ee..83ef7713c9 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -971,7 +971,19 @@ func TestCheckModuleResourceAttr(mp []string, name string, key string, value str } func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, value string) error { + // Empty containers may be elided from the state. + // If the intent here is to check for an empty container, allow the key to + // also be non-existent. + emptyCheck := false + if value == "0" && (strings.HasSuffix(key, ".#") || strings.HasSuffix(key, ".%")) { + emptyCheck = true + } + if v, ok := is.Attributes[key]; !ok || v != value { + if emptyCheck && !ok { + return nil + } + if !ok { return fmt.Errorf("%s: Attribute '%s' not found", name, key) } @@ -1014,7 +1026,20 @@ func TestCheckModuleNoResourceAttr(mp []string, name string, key string) TestChe } func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key string) error { - if _, ok := is.Attributes[key]; ok { + // Empty containers may sometimes be included in the state. + // If the intent here is to check for an empty container, allow the value to + // also be "0". + emptyCheck := false + if strings.HasSuffix(key, ".#") || strings.HasSuffix(key, ".%") { + emptyCheck = true + } + + val, exists := is.Attributes[key] + if emptyCheck && val == "0" { + return nil + } + + if exists { return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) } diff --git a/helper/resource/testing_test.go b/helper/resource/testing_test.go index 59d2f17b43..885bfabf9b 100644 --- a/helper/resource/testing_test.go +++ b/helper/resource/testing_test.go @@ -1113,3 +1113,65 @@ resource "test_instance" "foo" {} const testConfigStrProvider = ` provider "test" {} ` + +func TestCheckResourceAttr_empty(t *testing.T) { + s := terraform.NewState() + s.AddModuleState(&terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_resource": &terraform.ResourceState{ + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "empty_list.#": "0", + "empty_map.%": "0", + }, + }, + }, + }, + }) + + for _, key := range []string{ + "empty_list.#", + "empty_map.%", + "missing_list.#", + "missing_map.%", + } { + t.Run(key, func(t *testing.T) { + check := TestCheckResourceAttr("test_resource", key, "0") + if err := check(s); err != nil { + t.Fatal(err) + } + }) + } +} + +func TestCheckNoResourceAttr_empty(t *testing.T) { + s := terraform.NewState() + s.AddModuleState(&terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_resource": &terraform.ResourceState{ + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "empty_list.#": "0", + "empty_map.%": "0", + }, + }, + }, + }, + }) + + for _, key := range []string{ + "empty_list.#", + "empty_map.%", + "missing_list.#", + "missing_map.%", + } { + t.Run(key, func(t *testing.T) { + check := TestCheckNoResourceAttr("test_resource", key) + if err := check(s); err != nil { + t.Fatal(err) + } + }) + } +}