diff --git a/aws/internal/tfawsresource/testing.go b/aws/internal/tfawsresource/testing.go new file mode 100644 index 000000000000..68eab1549c80 --- /dev/null +++ b/aws/internal/tfawsresource/testing.go @@ -0,0 +1,120 @@ +package tfawsresource + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +const ( + sentinelIndex = "*" +) + +// TestCheckTypeSetElemNestedAttrs is a resource.TestCheckFunc that accepts a resource +// name, an attribute path, which should use the sentinel value '*' for indexing +// into a TypeSet. The function verifies that an element matches the whole value +// map. +// +// Use this function over SDK provided TestCheckFunctions when validating a +// TypeSet where its elements are a nested object with their own attrs/values. +// +// Please note, if the provided value map is not granular enough, there exists +// the possibility you match an element you were not intending to, in the TypeSet. +// Provide a full mapping of attributes to be sure the unique element exists. +func TestCheckTypeSetElemNestedAttrs(res, attr string, values map[string]string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ms := s.RootModule() + rs, ok := ms.Resources[res] + if !ok { + return fmt.Errorf("Not found: %s in %s", res, ms.Path) + } + + is := rs.Primary + if is == nil { + return fmt.Errorf("No primary instance: %s in %s", res, ms.Path) + } + + matches := make(map[string]int) + attrParts := strings.Split(attr, ".") + if attrParts[len(attrParts)-1] != sentinelIndex { + return fmt.Errorf("%q does not end with the special value %q", attr, sentinelIndex) + } + for stateKey, stateValue := range is.Attributes { + stateKeyParts := strings.Split(stateKey, ".") + // a Set/List item with nested attrs would have a flatmap address of + // at least length 3 + // foo.0.name = "bar" + if len(stateKeyParts) < 3 { + continue + } + var pathMatch bool + for i := range attrParts { + if attrParts[i] != stateKeyParts[i] && attrParts[i] != sentinelIndex { + break + } + if i == len(attrParts)-1 { + pathMatch = true + } + } + if !pathMatch { + continue + } + elementId := stateKeyParts[len(attrParts)-1] + nestedAttr := strings.Join(stateKeyParts[len(attrParts):], ".") + if v, keyExists := values[nestedAttr]; keyExists && v == stateValue { + matches[elementId] = matches[elementId] + 1 + if matches[elementId] == len(values) { + return nil + } + } + } + + return fmt.Errorf("%q no TypeSet element %q, with nested attrs %#v in state: %#v", res, attr, values, is.Attributes) + } +} + +// TestCheckTypeSetElemAttr is a resource.TestCheckFunc that accepts a resource +// name, an attribute path, which should use the sentinel value '*' for indexing +// into a TypeSet. The function verifies that an element matches the provided +// value. +// +// Use this function over SDK provided TestCheckFunctions when validating a +// TypeSet where its elements are a simple value +func TestCheckTypeSetElemAttr(res, attr, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ms := s.RootModule() + rs, ok := ms.Resources[res] + if !ok { + return fmt.Errorf("Not found: %s in %s", res, ms.Path) + } + + is := rs.Primary + if is == nil { + return fmt.Errorf("No primary instance: %s in %s", res, ms.Path) + } + + attrParts := strings.Split(attr, ".") + if attrParts[len(attrParts)-1] != sentinelIndex { + return fmt.Errorf("%q does not end with the special value %q", attr, sentinelIndex) + } + for stateKey, stateValue := range is.Attributes { + if stateValue == value { + stateKeyParts := strings.Split(stateKey, ".") + if len(stateKeyParts) == len(attrParts) { + for i := range attrParts { + if attrParts[i] != stateKeyParts[i] && attrParts[i] != sentinelIndex { + break + } + if i == len(attrParts)-1 { + return nil + } + } + } + } + } + + return fmt.Errorf("%q no TypeSet element %q, with value %q in state: %#v", res, attr, value, is.Attributes) + } +} diff --git a/aws/internal/tfawsresource/testing_test.go b/aws/internal/tfawsresource/testing_test.go new file mode 100644 index 000000000000..45c366223bf9 --- /dev/null +++ b/aws/internal/tfawsresource/testing_test.go @@ -0,0 +1,1079 @@ +package tfawsresource + +import ( + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestTestCheckTypeSetElemAttr(t *testing.T) { + testCases := []struct { + Description string + ResourceAddress string + ResourceAttribute string + Value string + TerraformState *terraform.State + ExpectedError func(err error) bool + }{ + { + Description: "no resources", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "resource not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_other_thing.test": { + Type: "example_other_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "no primary instance", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Deposed: []*terraform.InstanceState{ + { + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "No primary instance: example_thing.test") + }, + }, + { + Description: "attribute path does not end with sentinel value", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "does not end with the special value") + }, + }, + { + Description: "attribute not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value1", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value2", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "multiple root TypeSet attribute match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value1", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "2", + "test.12345": "value2", + "test.67890": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "multiple root TypeSet attribute mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value3", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "2", + "test.12345": "value2", + "test.67890": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single nested TypeSet attribute match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Value: "value1", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Value: "value2", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"") + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + err := TestCheckTypeSetElemAttr(testCase.ResourceAddress, testCase.ResourceAttribute, testCase.Value)(testCase.TerraformState) + + if err != nil { + if testCase.ExpectedError == nil { + t.Fatalf("expected no error, got error: %s", err) + } + + if !testCase.ExpectedError(err) { + t.Fatalf("unexpected error: %s", err) + } + + t.Logf("received expected error: %s", err) + return + } + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error, got no error") + } + }) + } +} + +func TestTestCheckTypeSetElemNestedAttrs(t *testing.T) { + testCases := []struct { + Description string + ResourceAddress string + ResourceAttribute string + Values map[string]string + TerraformState *terraform.State + ExpectedError func(err error) bool + }{ + { + Description: "no resources", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "resource not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_other_thing.test": { + Type: "example_other_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "no primary instance", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Deposed: []*terraform.InstanceState{ + { + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "No primary instance: example_thing.test") + }, + }, + { + Description: "attribute path does not end with sentinel value", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "does not end with the special value") + }, + }, + { + Description: "attribute not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute single value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute single nested value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute single nested value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute multiple value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute multiple value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + "key2": "value3", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "multiple root TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "2", + "test.12345.key1": "value2", + "test.67890.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "multiple root TypeSet attribute multiple value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "6", + "id": "11111", + "test.%": "2", + "test.12345.key1": "value2", + "test.12345.key2": "value3", + "test.67890.key1": "value1", + "test.67890.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"") + }, + }, + { + Description: "single nested TypeSet attribute single nested value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "5", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1.%": "1", + "test.0.nested_test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single nested value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "5", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1.%": "1", + "test.0.nested_test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"") + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + err := TestCheckTypeSetElemNestedAttrs(testCase.ResourceAddress, testCase.ResourceAttribute, testCase.Values)(testCase.TerraformState) + + if err != nil { + if testCase.ExpectedError == nil { + t.Fatalf("expected no error, got error: %s", err) + } + + if !testCase.ExpectedError(err) { + t.Fatalf("unexpected error: %s", err) + } + + t.Logf("received expected error: %s", err) + return + } + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error, got no error") + } + }) + } +} diff --git a/aws/resource_aws_apigatewayv2_api_test.go b/aws/resource_aws_apigatewayv2_api_test.go index f95bfd8cea0e..314bd3fd8b91 100644 --- a/aws/resource_aws_apigatewayv2_api_test.go +++ b/aws/resource_aws_apigatewayv2_api_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" ) func init() { @@ -374,10 +375,9 @@ func TestAccAWSAPIGatewayV2Api_CorsConfiguration(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSAPIGatewayV2ApiDestroy, - DisableBinaryDriver: true, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayV2ApiDestroy, Steps: []resource.TestStep{ { Config: testAccAWSAPIGatewayV2ApiConfig_corsConfiguration(rName), @@ -389,12 +389,12 @@ func TestAccAWSAPIGatewayV2Api_CorsConfiguration(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "cors_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_credentials", "false"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_headers.#", "1"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_headers.2053999599", "Authorization"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.allow_headers.*", "Authorization"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_methods.#", "2"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_methods.4248514160", "GET"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_methods.2928708052", "put"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.allow_methods.*", "GET"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.allow_methods.*", "put"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_origins.#", "1"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_origins.89023941", "https://www.example.com"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.allow_origins.*", "https://www.example.com"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.expose_headers.#", "0"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.max_age", "0"), resource.TestCheckResourceAttr(resourceName, "description", ""), @@ -422,12 +422,12 @@ func TestAccAWSAPIGatewayV2Api_CorsConfiguration(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_credentials", "true"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_headers.#", "0"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_methods.#", "1"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_methods.163128923", "*"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.allow_methods.*", "*"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_origins.#", "2"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_origins.1868318776", "HTTP://WWW.EXAMPLE.ORG"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.allow_origins.3551736600", "https://example.io"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.allow_origins.*", "HTTP://WWW.EXAMPLE.ORG"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.allow_origins.*", "https://example.io"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.expose_headers.#", "1"), - resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.expose_headers.115091893", "X-Api-Id"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "cors_configuration.0.expose_headers.*", "X-Api-Id"), resource.TestCheckResourceAttr(resourceName, "cors_configuration.0.max_age", "500"), resource.TestCheckResourceAttr(resourceName, "description", ""), testAccMatchResourceAttrRegionalARN(resourceName, "execution_arn", "execute-api", regexp.MustCompile(`.+`)), diff --git a/aws/resource_aws_db_parameter_group_test.go b/aws/resource_aws_db_parameter_group_test.go index e5786f3c0e6f..ada0ce2ec12e 100644 --- a/aws/resource_aws_db_parameter_group_test.go +++ b/aws/resource_aws_db_parameter_group_test.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" ) func init() { @@ -79,10 +80,9 @@ func TestAccAWSDBParameterGroup_basic(t *testing.T) { groupName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt()) resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSDBParameterGroupDestroy, - DisableBinaryDriver: true, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBParameterGroupDestroy, Steps: []resource.TestStep{ { Config: testAccAWSDBParameterGroupConfig(groupName), @@ -91,12 +91,18 @@ func TestAccAWSDBParameterGroup_basic(t *testing.T) { testAccCheckAWSDBParameterGroupAttributes(&v, groupName), resource.TestCheckResourceAttr(resourceName, "name", groupName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), - resource.TestCheckResourceAttr(resourceName, "parameter.1708034931.name", "character_set_results"), - resource.TestCheckResourceAttr(resourceName, "parameter.1708034931.value", "utf8"), - resource.TestCheckResourceAttr(resourceName, "parameter.2421266705.name", "character_set_server"), - resource.TestCheckResourceAttr(resourceName, "parameter.2421266705.value", "utf8"), - resource.TestCheckResourceAttr(resourceName, "parameter.2478663599.name", "character_set_client"), - resource.TestCheckResourceAttr(resourceName, "parameter.2478663599.value", "utf8"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_results", + "value": "utf8", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_server", + "value": "utf8", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_client", + "value": "utf8", + }), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(fmt.Sprintf("pg:%s$", groupName))), ), }, @@ -112,16 +118,26 @@ func TestAccAWSDBParameterGroup_basic(t *testing.T) { testAccCheckAWSDBParameterGroupAttributes(&v, groupName), resource.TestCheckResourceAttr(resourceName, "name", groupName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), - resource.TestCheckResourceAttr(resourceName, "parameter.1706463059.name", "collation_connection"), - resource.TestCheckResourceAttr(resourceName, "parameter.1706463059.value", "utf8_unicode_ci"), - resource.TestCheckResourceAttr(resourceName, "parameter.1708034931.name", "character_set_results"), - resource.TestCheckResourceAttr(resourceName, "parameter.1708034931.value", "utf8"), - resource.TestCheckResourceAttr(resourceName, "parameter.2421266705.name", "character_set_server"), - resource.TestCheckResourceAttr(resourceName, "parameter.2421266705.value", "utf8"), - resource.TestCheckResourceAttr(resourceName, "parameter.2475805061.name", "collation_server"), - resource.TestCheckResourceAttr(resourceName, "parameter.2475805061.value", "utf8_unicode_ci"), - resource.TestCheckResourceAttr(resourceName, "parameter.2478663599.name", "character_set_client"), - resource.TestCheckResourceAttr(resourceName, "parameter.2478663599.value", "utf8"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "collation_connection", + "value": "utf8_unicode_ci", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_results", + "value": "utf8", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_server", + "value": "utf8", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "collation_server", + "value": "utf8_unicode_ci", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_client", + "value": "utf8", + }), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(fmt.Sprintf("pg:%s$", groupName))), ), }, @@ -132,8 +148,19 @@ func TestAccAWSDBParameterGroup_basic(t *testing.T) { testAccCheckAWSDBParameterGroupAttributes(&v, groupName), testAccCheckAWSDBParameterNotUserDefined(resourceName, "collation_connection"), testAccCheckAWSDBParameterNotUserDefined(resourceName, "collation_server"), - resource.TestCheckNoResourceAttr(resourceName, "parameter.2475805061.value"), - resource.TestCheckNoResourceAttr(resourceName, "parameter.1706463059.value"), + resource.TestCheckResourceAttr(resourceName, "parameter.#", "3"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_results", + "value": "utf8", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_server", + "value": "utf8", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "character_set_client", + "value": "utf8", + }), ), }, }, diff --git a/aws/resource_aws_s3_bucket_test.go b/aws/resource_aws_s3_bucket_test.go index c5970d778ab1..8ce2d0e5cc4e 100644 --- a/aws/resource_aws_s3_bucket_test.go +++ b/aws/resource_aws_s3_bucket_test.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" ) func init() { @@ -1203,10 +1204,9 @@ func TestAccAWSS3Bucket_LifecycleBasic(t *testing.T) { resourceName := "aws_s3_bucket.bucket" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSS3BucketDestroy, - DisableBinaryDriver: true, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketDestroy, Steps: []resource.TestStep{ { Config: testAccAWSS3BucketConfigWithLifecycle(bucketName), @@ -1217,21 +1217,31 @@ func TestAccAWSS3Bucket_LifecycleBasic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.expiration.0.days", "365"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.expiration.0.date", ""), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.expiration.0.expired_object_delete_marker", "false"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.2000431762.date", ""), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.2000431762.days", "30"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.2000431762.storage_class", "STANDARD_IA"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.3601168188.date", ""), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.3601168188.days", "60"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.3601168188.storage_class", "INTELLIGENT_TIERING"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.3854926587.date", ""), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.3854926587.days", "90"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.3854926587.storage_class", "ONEZONE_IA"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.962205413.date", ""), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.962205413.days", "120"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.962205413.storage_class", "GLACIER"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.1571523406.date", ""), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.1571523406.days", "210"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.transition.1571523406.storage_class", "DEEP_ARCHIVE"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.0.transition.*", map[string]string{ + "date": "", + "days": "30", + "storage_class": "STANDARD_IA", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.0.transition.*", map[string]string{ + "date": "", + "days": "60", + "storage_class": "INTELLIGENT_TIERING", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.0.transition.*", map[string]string{ + "date": "", + "days": "90", + "storage_class": "ONEZONE_IA", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.0.transition.*", map[string]string{ + "date": "", + "days": "120", + "storage_class": "GLACIER", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.0.transition.*", map[string]string{ + "date": "", + "days": "210", + "storage_class": "DEEP_ARCHIVE", + }), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.id", "id2"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.prefix", "path2/"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.expiration.0.date", "2016-01-12"), @@ -1239,7 +1249,9 @@ func TestAccAWSS3Bucket_LifecycleBasic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.expiration.0.expired_object_delete_marker", "false"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.2.id", "id3"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.2.prefix", "path3/"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.2.transition.460947558.days", "0"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.2.transition.*", map[string]string{ + "days": "0", + }), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.3.id", "id4"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.3.prefix", "path4/"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.3.tags.tagKey", "tagValue"), @@ -1247,12 +1259,16 @@ func TestAccAWSS3Bucket_LifecycleBasic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.4.id", "id5"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.4.tags.tagKey", "tagValue"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.4.tags.terraform", "hashicorp"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.4.transition.460947558.days", "0"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.4.transition.460947558.storage_class", "GLACIER"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.4.transition.*", map[string]string{ + "days": "0", + "storage_class": "GLACIER", + }), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.5.id", "id6"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.5.tags.tagKey", "tagValue"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.5.transition.460947558.days", "0"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.5.transition.460947558.storage_class", "GLACIER"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.5.transition.*", map[string]string{ + "days": "0", + "storage_class": "GLACIER", + }), ), }, { @@ -1269,18 +1285,24 @@ func TestAccAWSS3Bucket_LifecycleBasic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.prefix", "path1/"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.enabled", "true"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.noncurrent_version_expiration.0.days", "365"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.noncurrent_version_transition.1377917700.days", "30"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.noncurrent_version_transition.1377917700.storage_class", "STANDARD_IA"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.noncurrent_version_transition.2528035817.days", "60"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.0.noncurrent_version_transition.2528035817.storage_class", "GLACIER"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.0.noncurrent_version_transition.*", map[string]string{ + "days": "30", + "storage_class": "STANDARD_IA", + }), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.0.noncurrent_version_transition.*", map[string]string{ + "days": "60", + "storage_class": "GLACIER", + }), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.id", "id2"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.prefix", "path2/"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.enabled", "false"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.1.noncurrent_version_expiration.0.days", "365"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.2.id", "id3"), resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.2.prefix", "path3/"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.2.noncurrent_version_transition.3732708140.days", "0"), - resource.TestCheckResourceAttr(resourceName, "lifecycle_rule.2.noncurrent_version_transition.3732708140.storage_class", "GLACIER"), + tfawsresource.TestCheckTypeSetElemNestedAttrs(resourceName, "lifecycle_rule.2.noncurrent_version_transition.*", map[string]string{ + "days": "0", + "storage_class": "GLACIER", + }), ), }, {