opentofu/helper/schema/field_reader_diff_test.go
James McGill 035d56409f helper/schema: handle TypeMap elem consistently with other collection types
For historical reasons, the handling of element types for maps is inconsistent with other collection types.

Here we begin a multi-step process to make it consistent, starting by supporting both the "consistent" form of using a schema.Schema and an existing erroneous form of using a schema.Type directly. In subsequent commits we will phase out the erroneous form and require the schema.Schema approach, the same as we do for TypeList and TypeSet.
2018-03-14 14:50:41 -07:00

525 lines
10 KiB
Go

package schema
import (
"reflect"
"testing"
"github.com/hashicorp/terraform/terraform"
)
func TestDiffFieldReader_impl(t *testing.T) {
var _ FieldReader = new(DiffFieldReader)
}
func TestDiffFieldReader_NestedSetUpdate(t *testing.T) {
hashFn := func(a interface{}) int {
m := a.(map[string]interface{})
return m["val"].(int)
}
schema := map[string]*Schema{
"list_of_sets_1": &Schema{
Type: TypeList,
Elem: &Resource{
Schema: map[string]*Schema{
"nested_set": &Schema{
Type: TypeSet,
Elem: &Resource{
Schema: map[string]*Schema{
"val": &Schema{
Type: TypeInt,
},
},
},
Set: hashFn,
},
},
},
},
"list_of_sets_2": &Schema{
Type: TypeList,
Elem: &Resource{
Schema: map[string]*Schema{
"nested_set": &Schema{
Type: TypeSet,
Elem: &Resource{
Schema: map[string]*Schema{
"val": &Schema{
Type: TypeInt,
},
},
},
Set: hashFn,
},
},
},
},
}
r := &DiffFieldReader{
Schema: schema,
Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"list_of_sets_1.0.nested_set.1.val": &terraform.ResourceAttrDiff{
Old: "1",
New: "0",
NewRemoved: true,
},
"list_of_sets_1.0.nested_set.2.val": &terraform.ResourceAttrDiff{
New: "2",
},
},
},
}
r.Source = &MultiLevelFieldReader{
Readers: map[string]FieldReader{
"diff": r,
"set": &MapFieldReader{Schema: schema},
"state": &MapFieldReader{
Map: &BasicMapReader{
"list_of_sets_1.#": "1",
"list_of_sets_1.0.nested_set.#": "1",
"list_of_sets_1.0.nested_set.1.val": "1",
"list_of_sets_2.#": "1",
"list_of_sets_2.0.nested_set.#": "1",
"list_of_sets_2.0.nested_set.1.val": "1",
},
Schema: schema,
},
},
Levels: []string{"state", "config"},
}
out, err := r.ReadField([]string{"list_of_sets_2"})
if err != nil {
t.Fatalf("err: %v", err)
}
s := &Set{F: hashFn}
s.Add(map[string]interface{}{"val": 1})
expected := s.List()
l := out.Value.([]interface{})
i := l[0].(map[string]interface{})
actual := i["nested_set"].(*Set).List()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("bad: NestedSetUpdate\n\nexpected: %#v\n\ngot: %#v\n\n", expected, actual)
}
}
// https://github.com/hashicorp/terraform/issues/914
func TestDiffFieldReader_MapHandling(t *testing.T) {
schema := map[string]*Schema{
"tags": &Schema{
Type: TypeMap,
},
}
r := &DiffFieldReader{
Schema: schema,
Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"tags.%": &terraform.ResourceAttrDiff{
Old: "1",
New: "2",
},
"tags.baz": &terraform.ResourceAttrDiff{
Old: "",
New: "qux",
},
},
},
Source: &MapFieldReader{
Schema: schema,
Map: BasicMapReader(map[string]string{
"tags.%": "1",
"tags.foo": "bar",
}),
},
}
result, err := r.ReadField([]string{"tags"})
if err != nil {
t.Fatalf("ReadField failed: %#v", err)
}
expected := map[string]interface{}{
"foo": "bar",
"baz": "qux",
}
if !reflect.DeepEqual(expected, result.Value) {
t.Fatalf("bad: DiffHandling\n\nexpected: %#v\n\ngot: %#v\n\n", expected, result.Value)
}
}
func TestDiffFieldReader_extra(t *testing.T) {
schema := map[string]*Schema{
"stringComputed": &Schema{Type: TypeString},
"listMap": &Schema{
Type: TypeList,
Elem: &Schema{
Type: TypeMap,
},
},
"mapRemove": &Schema{Type: TypeMap},
"setChange": &Schema{
Type: TypeSet,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"index": &Schema{
Type: TypeInt,
Required: true,
},
"value": &Schema{
Type: TypeString,
Required: true,
},
},
},
Set: func(a interface{}) int {
m := a.(map[string]interface{})
return m["index"].(int)
},
},
"setEmpty": &Schema{
Type: TypeSet,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{
"index": &Schema{
Type: TypeInt,
Required: true,
},
"value": &Schema{
Type: TypeString,
Required: true,
},
},
},
Set: func(a interface{}) int {
m := a.(map[string]interface{})
return m["index"].(int)
},
},
}
r := &DiffFieldReader{
Schema: schema,
Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"stringComputed": &terraform.ResourceAttrDiff{
Old: "foo",
New: "bar",
NewComputed: true,
},
"listMap.0.bar": &terraform.ResourceAttrDiff{
NewRemoved: true,
},
"mapRemove.bar": &terraform.ResourceAttrDiff{
NewRemoved: true,
},
"setChange.10.value": &terraform.ResourceAttrDiff{
Old: "50",
New: "80",
},
"setEmpty.#": &terraform.ResourceAttrDiff{
Old: "2",
New: "0",
},
},
},
Source: &MapFieldReader{
Schema: schema,
Map: BasicMapReader(map[string]string{
"listMap.#": "2",
"listMap.0.foo": "bar",
"listMap.0.bar": "baz",
"listMap.1.baz": "baz",
"mapRemove.foo": "bar",
"mapRemove.bar": "bar",
"setChange.#": "1",
"setChange.10.index": "10",
"setChange.10.value": "50",
"setEmpty.#": "2",
"setEmpty.10.index": "10",
"setEmpty.10.value": "50",
"setEmpty.20.index": "20",
"setEmpty.20.value": "50",
}),
},
}
cases := map[string]struct {
Addr []string
Result FieldReadResult
Err bool
}{
"stringComputed": {
[]string{"stringComputed"},
FieldReadResult{
Value: "",
Exists: true,
Computed: true,
},
false,
},
"listMapRemoval": {
[]string{"listMap"},
FieldReadResult{
Value: []interface{}{
map[string]interface{}{
"foo": "bar",
},
map[string]interface{}{
"baz": "baz",
},
},
Exists: true,
},
false,
},
"mapRemove": {
[]string{"mapRemove"},
FieldReadResult{
Value: map[string]interface{}{
"foo": "bar",
},
Exists: true,
Computed: false,
},
false,
},
"setChange": {
[]string{"setChange"},
FieldReadResult{
Value: []interface{}{
map[string]interface{}{
"index": 10,
"value": "80",
},
},
Exists: true,
},
false,
},
"setEmpty": {
[]string{"setEmpty"},
FieldReadResult{
Value: []interface{}{},
Exists: true,
},
false,
},
}
for name, tc := range cases {
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 TestDiffFieldReader(t *testing.T) {
testFieldReader(t, func(s map[string]*Schema) FieldReader {
return &DiffFieldReader{
Schema: s,
Diff: &terraform.InstanceDiff{
Attributes: map[string]*terraform.ResourceAttrDiff{
"bool": &terraform.ResourceAttrDiff{
Old: "",
New: "true",
},
"int": &terraform.ResourceAttrDiff{
Old: "",
New: "42",
},
"float": &terraform.ResourceAttrDiff{
Old: "",
New: "3.1415",
},
"string": &terraform.ResourceAttrDiff{
Old: "",
New: "string",
},
"stringComputed": &terraform.ResourceAttrDiff{
Old: "foo",
New: "bar",
NewComputed: true,
},
"list.#": &terraform.ResourceAttrDiff{
Old: "0",
New: "2",
},
"list.0": &terraform.ResourceAttrDiff{
Old: "",
New: "foo",
},
"list.1": &terraform.ResourceAttrDiff{
Old: "",
New: "bar",
},
"listInt.#": &terraform.ResourceAttrDiff{
Old: "0",
New: "2",
},
"listInt.0": &terraform.ResourceAttrDiff{
Old: "",
New: "21",
},
"listInt.1": &terraform.ResourceAttrDiff{
Old: "",
New: "42",
},
"map.foo": &terraform.ResourceAttrDiff{
Old: "",
New: "bar",
},
"map.bar": &terraform.ResourceAttrDiff{
Old: "",
New: "baz",
},
"mapInt.%": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapInt.one": &terraform.ResourceAttrDiff{
Old: "",
New: "1",
},
"mapInt.two": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapIntNestedSchema.%": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapIntNestedSchema.one": &terraform.ResourceAttrDiff{
Old: "",
New: "1",
},
"mapIntNestedSchema.two": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapFloat.%": &terraform.ResourceAttrDiff{
Old: "",
New: "1",
},
"mapFloat.oneDotTwo": &terraform.ResourceAttrDiff{
Old: "",
New: "1.2",
},
"mapBool.%": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapBool.True": &terraform.ResourceAttrDiff{
Old: "",
New: "true",
},
"mapBool.False": &terraform.ResourceAttrDiff{
Old: "",
New: "false",
},
"set.#": &terraform.ResourceAttrDiff{
Old: "0",
New: "2",
},
"set.10": &terraform.ResourceAttrDiff{
Old: "",
New: "10",
},
"set.50": &terraform.ResourceAttrDiff{
Old: "",
New: "50",
},
"setDeep.#": &terraform.ResourceAttrDiff{
Old: "0",
New: "2",
},
"setDeep.10.index": &terraform.ResourceAttrDiff{
Old: "",
New: "10",
},
"setDeep.10.value": &terraform.ResourceAttrDiff{
Old: "",
New: "foo",
},
"setDeep.50.index": &terraform.ResourceAttrDiff{
Old: "",
New: "50",
},
"setDeep.50.value": &terraform.ResourceAttrDiff{
Old: "",
New: "bar",
},
},
},
Source: &MapFieldReader{
Schema: s,
Map: BasicMapReader(map[string]string{
"listMap.#": "2",
"listMap.0.foo": "bar",
"listMap.0.bar": "baz",
"listMap.1.baz": "baz",
}),
},
}
})
}