diff --git a/builtin/providers/test/resource_nested_set.go b/builtin/providers/test/resource_nested_set.go index b808b7c98a..81d7ab0f53 100644 --- a/builtin/providers/test/resource_nested_set.go +++ b/builtin/providers/test/resource_nested_set.go @@ -28,6 +28,19 @@ func testResourceNestedSet() *schema.Resource { Optional: true, ForceNew: true, }, + "type_list": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + }, + }, + }, + }, "single": { Type: schema.TypeSet, Optional: true, @@ -98,7 +111,6 @@ func testResourceNestedSet() *schema.Resource { Type: schema.TypeString, Required: true, }, - "list": { Type: schema.TypeList, Optional: true, @@ -106,6 +118,18 @@ func testResourceNestedSet() *schema.Resource { Type: schema.TypeString, }, }, + "list_block": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "unused": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, }, }, }, diff --git a/builtin/providers/test/resource_nested_set_test.go b/builtin/providers/test/resource_nested_set_test.go index f85e2a1909..99e7651cee 100644 --- a/builtin/providers/test/resource_nested_set_test.go +++ b/builtin/providers/test/resource_nested_set_test.go @@ -3,6 +3,7 @@ package test import ( "errors" "fmt" + "regexp" "strings" "testing" @@ -56,6 +57,118 @@ resource "test_resource_nested_set" "foo" { }) } +// the empty type_list must be passed to the provider with 1 nil element +func TestResourceNestedSet_emptyBlock(t *testing.T) { + checkFunc := func(s *terraform.State) error { + root := s.ModuleByPath(addrs.RootModuleInstance) + res := root.Resources["test_resource_nested_set.foo"] + for k, v := range res.Primary.Attributes { + if strings.HasPrefix(k, "type_list") && v != "1" { + return fmt.Errorf("unexpected set value: %s:%s", k, v) + } + } + return nil + } + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource_nested_set" "foo" { + type_list { + } +} + `), + Check: checkFunc, + }, + }, + }) +} + +func TestResourceNestedSet_emptyNestedListBlock(t *testing.T) { + checkFunc := func(s *terraform.State) error { + root := s.ModuleByPath(addrs.RootModuleInstance) + res := root.Resources["test_resource_nested_set.foo"] + found := false + for k, v := range res.Primary.Attributes { + if !regexp.MustCompile(`^with_list\.\d+\.list_block\.`).MatchString(k) { + continue + } + found = true + + if strings.HasSuffix(k, ".#") { + if v != "1" { + return fmt.Errorf("expected block with no objects: got %s:%s", k, v) + } + continue + } + + // there should be no other attribute values for an empty block + return fmt.Errorf("unexpected attribute: %s:%s", k, v) + } + if !found { + return fmt.Errorf("with_list.X.list_block not found") + } + return nil + } + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource_nested_set" "foo" { + with_list { + required = "ok" + list_block { + } + } +} + `), + Check: checkFunc, + }, + }, + }) +} +func TestResourceNestedSet_emptyNestedList(t *testing.T) { + checkFunc := func(s *terraform.State) error { + root := s.ModuleByPath(addrs.RootModuleInstance) + res := root.Resources["test_resource_nested_set.foo"] + found := false + for k, v := range res.Primary.Attributes { + if regexp.MustCompile(`^with_list\.\d+\.list\.#$`).MatchString(k) { + found = true + if v != "0" { + return fmt.Errorf("expected empty list: %s, got %s", k, v) + } + break + } + } + if !found { + return fmt.Errorf("with_list.X.nested_list not found") + } + return nil + } + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource_nested_set" "foo" { + with_list { + required = "ok" + list = [] + } +} + `), + Check: checkFunc, + }, + }, + }) +} + func TestResourceNestedSet_addRemove(t *testing.T) { var id string checkFunc := func(s *terraform.State) error { @@ -339,3 +452,50 @@ resource "test_resource_nested_set" "foo" { }, }) } + +// This is the same as forceNewEmptyString, but we start with the empty value, +// instead of changing it. +func TestResourceNestedSet_nestedSetEmptyString(t *testing.T) { + checkFunc := func(s *terraform.State) error { + return nil + } + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource_nested_set" "foo" { + multi { + set { + required = "" + } + } +} + `), + Check: checkFunc, + }, + }, + }) +} + +func TestResourceNestedSet_emptySet(t *testing.T) { + checkFunc := func(s *terraform.State) error { + return nil + } + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource_nested_set" "foo" { + multi { + } +} + `), + Check: checkFunc, + }, + }, + }) +} diff --git a/builtin/providers/test/resource_nested_test.go b/builtin/providers/test/resource_nested_test.go index d1a9c1b73e..9237b52fca 100644 --- a/builtin/providers/test/resource_nested_test.go +++ b/builtin/providers/test/resource_nested_test.go @@ -142,6 +142,7 @@ resource "test_resource_nested" "foo" { "nested.0.nested_again.0.string": "a", "nested.1.string": "", "nested.1.optional": "false", + "nested.1.nested_again.#": "0", } delete(got, "id") // it's random, so not useful for testing diff --git a/builtin/providers/test/resource_test.go b/builtin/providers/test/resource_test.go index b9eb064552..12bf568faa 100644 --- a/builtin/providers/test/resource_test.go +++ b/builtin/providers/test/resource_test.go @@ -521,3 +521,23 @@ resource "test_resource" "two" { }, }) } + +func TestResource_emptyMapValue(t *testing.T) { + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource" "foo" { + required = "ok" + required_map = { + a = "a" + b = "" + } +} + `), + }, + }, + }) +}