package schema import ( "reflect" "testing" "github.com/hashicorp/hil/ast" "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/terraform" ) func TestConfigFieldReader_impl(t *testing.T) { var _ FieldReader = new(ConfigFieldReader) } func TestConfigFieldReader(t *testing.T) { testFieldReader(t, func(s map[string]*Schema) FieldReader { return &ConfigFieldReader{ Schema: s, Config: testConfig(t, map[string]interface{}{ "bool": true, "float": 3.1415, "int": 42, "string": "string", "list": []interface{}{"foo", "bar"}, "listInt": []interface{}{21, 42}, "map": map[string]interface{}{ "foo": "bar", "bar": "baz", }, "set": []interface{}{10, 50}, "setDeep": []interface{}{ map[string]interface{}{ "index": 10, "value": "foo", }, map[string]interface{}{ "index": 50, "value": "bar", }, }, }), } }) } // This contains custom table tests for our ConfigFieldReader func TestConfigFieldReader_custom(t *testing.T) { schema := map[string]*Schema{ "bool": &Schema{ Type: TypeBool, }, } cases := map[string]struct { Addr []string Result FieldReadResult Config *terraform.ResourceConfig Err bool }{ "basic": { []string{"bool"}, FieldReadResult{ Value: true, Exists: true, }, testConfig(t, map[string]interface{}{ "bool": true, }), false, }, "computed": { []string{"bool"}, FieldReadResult{ Exists: true, Computed: true, }, testConfigInterpolate(t, map[string]interface{}{ "bool": "${var.foo}", }, map[string]ast.Variable{ "var.foo": ast.Variable{ Value: config.UnknownVariableValue, Type: ast.TypeString, }, }), false, }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { r := &ConfigFieldReader{ Schema: schema, Config: tc.Config, } out, err := r.ReadField(tc.Addr) if err != nil != tc.Err { t.Fatalf("%s: err: %s", name, err) } if s, ok := out.Value.(*Set); ok { // If it is a set, convert to a list so its more easily checked. out.Value = s.List() } if !reflect.DeepEqual(tc.Result, out) { t.Fatalf("%s: bad: %#v", name, out) } }) } } func TestConfigFieldReader_DefaultHandling(t *testing.T) { schema := map[string]*Schema{ "strWithDefault": &Schema{ Type: TypeString, Default: "ImADefault", }, "strWithDefaultFunc": &Schema{ Type: TypeString, DefaultFunc: func() (interface{}, error) { return "FuncDefault", nil }, }, } cases := map[string]struct { Addr []string Result FieldReadResult Config *terraform.ResourceConfig Err bool }{ "gets default value when no config set": { []string{"strWithDefault"}, FieldReadResult{ Value: "ImADefault", Exists: true, Computed: false, }, testConfig(t, map[string]interface{}{}), false, }, "config overrides default value": { []string{"strWithDefault"}, FieldReadResult{ Value: "fromConfig", Exists: true, Computed: false, }, testConfig(t, map[string]interface{}{ "strWithDefault": "fromConfig", }), false, }, "gets default from function when no config set": { []string{"strWithDefaultFunc"}, FieldReadResult{ Value: "FuncDefault", Exists: true, Computed: false, }, testConfig(t, map[string]interface{}{}), false, }, "config overrides default function": { []string{"strWithDefaultFunc"}, FieldReadResult{ Value: "fromConfig", Exists: true, Computed: false, }, testConfig(t, map[string]interface{}{ "strWithDefaultFunc": "fromConfig", }), false, }, } for name, tc := range cases { r := &ConfigFieldReader{ Schema: schema, Config: tc.Config, } out, err := r.ReadField(tc.Addr) if err != nil != tc.Err { t.Fatalf("%s: err: %s", name, err) } if s, ok := out.Value.(*Set); ok { // If it is a set, convert to a list so its more easily checked. out.Value = s.List() } if !reflect.DeepEqual(tc.Result, out) { t.Fatalf("%s: bad: %#v", name, out) } } } func TestConfigFieldReader_ComputedMap(t *testing.T) { schema := map[string]*Schema{ "map": &Schema{ Type: TypeMap, Computed: true, }, } cases := map[string]struct { Addr []string Result FieldReadResult Config *terraform.ResourceConfig Err bool }{ "set, normal": { []string{"map"}, FieldReadResult{ Value: map[string]interface{}{ "foo": "bar", }, Exists: true, Computed: false, }, testConfig(t, map[string]interface{}{ "map": map[string]interface{}{ "foo": "bar", }, }), false, }, "computed element": { []string{"map"}, FieldReadResult{ Exists: true, Computed: true, }, testConfigInterpolate(t, map[string]interface{}{ "map": map[string]interface{}{ "foo": "${var.foo}", }, }, map[string]ast.Variable{ "var.foo": ast.Variable{ Value: config.UnknownVariableValue, Type: ast.TypeString, }, }), false, }, "native map": { []string{"map"}, FieldReadResult{ Value: map[string]interface{}{ "bar": "baz", "baz": "bar", }, Exists: true, Computed: false, }, testConfigInterpolate(t, map[string]interface{}{ "map": "${var.foo}", }, map[string]ast.Variable{ "var.foo": ast.Variable{ Type: ast.TypeMap, Value: map[string]ast.Variable{ "bar": ast.Variable{ Type: ast.TypeString, Value: "baz", }, "baz": ast.Variable{ Type: ast.TypeString, Value: "bar", }, }, }, }), false, }, } for name, tc := range cases { r := &ConfigFieldReader{ Schema: schema, Config: tc.Config, } out, err := r.ReadField(tc.Addr) if err != nil != tc.Err { t.Fatalf("%s: err: %s", name, err) } if s, ok := out.Value.(*Set); ok { // If it is a set, convert to the raw map out.Value = s.m if len(s.m) == 0 { out.Value = nil } } if !reflect.DeepEqual(tc.Result, out) { t.Fatalf("%s: bad: %#v", name, out) } } } func TestConfigFieldReader_ComputedSet(t *testing.T) { schema := map[string]*Schema{ "strSet": &Schema{ Type: TypeSet, Elem: &Schema{Type: TypeString}, Set: HashString, }, } cases := map[string]struct { Addr []string Result FieldReadResult Config *terraform.ResourceConfig Err bool }{ "set, normal": { []string{"strSet"}, FieldReadResult{ Value: map[string]interface{}{ "2356372769": "foo", }, Exists: true, Computed: false, }, testConfig(t, map[string]interface{}{ "strSet": []interface{}{"foo"}, }), false, }, "set, computed element": { []string{"strSet"}, FieldReadResult{ Value: nil, Exists: true, Computed: true, }, testConfigInterpolate(t, map[string]interface{}{ "strSet": []interface{}{"${var.foo}"}, }, map[string]ast.Variable{ "var.foo": ast.Variable{ Value: config.UnknownVariableValue, Type: ast.TypeUnknown, }, }), false, }, "set, computed element substring": { []string{"strSet"}, FieldReadResult{ Value: nil, Exists: true, Computed: true, }, testConfigInterpolate(t, map[string]interface{}{ "strSet": []interface{}{"${var.foo}/32"}, }, map[string]ast.Variable{ "var.foo": ast.Variable{ Value: config.UnknownVariableValue, Type: ast.TypeUnknown, }, }), false, }, } for name, tc := range cases { r := &ConfigFieldReader{ Schema: schema, Config: tc.Config, } out, err := r.ReadField(tc.Addr) if err != nil != tc.Err { t.Fatalf("%s: err: %s", name, err) } if s, ok := out.Value.(*Set); ok { // If it is a set, convert to the raw map out.Value = s.m if len(s.m) == 0 { out.Value = nil } } if !reflect.DeepEqual(tc.Result, out) { t.Fatalf("%s: bad: %#v", name, out) } } } func testConfig( t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig { return testConfigInterpolate(t, raw, nil) } func testConfigInterpolate( t *testing.T, raw map[string]interface{}, vs map[string]ast.Variable) *terraform.ResourceConfig { rc, err := config.NewRawConfig(raw) if err != nil { t.Fatalf("err: %s", err) } if len(vs) > 0 { if err := rc.Interpolate(vs); err != nil { t.Fatalf("err: %s", err) } } return terraform.NewResourceConfig(rc) }