| package configschema |
| |
| import ( |
| "testing" |
| |
| "github.com/zclconf/go-cty/cty" |
| ) |
| |
| func TestBlockImpliedType(t *testing.T) { |
| tests := map[string]struct { |
| Schema *Block |
| Want cty.Type |
| }{ |
| "nil": { |
| nil, |
| cty.EmptyObject, |
| }, |
| "empty": { |
| &Block{}, |
| cty.EmptyObject, |
| }, |
| "attributes": { |
| &Block{ |
| Attributes: map[string]*Attribute{ |
| "optional": { |
| Type: cty.String, |
| Optional: true, |
| }, |
| "required": { |
| Type: cty.Number, |
| Required: true, |
| }, |
| "computed": { |
| Type: cty.List(cty.Bool), |
| Computed: true, |
| }, |
| "optional_computed": { |
| Type: cty.Map(cty.Bool), |
| Optional: true, |
| Computed: true, |
| }, |
| }, |
| }, |
| cty.Object(map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| }), |
| }, |
| "blocks": { |
| &Block{ |
| BlockTypes: map[string]*NestedBlock{ |
| "single": &NestedBlock{ |
| Nesting: NestingSingle, |
| Block: Block{ |
| Attributes: map[string]*Attribute{ |
| "foo": { |
| Type: cty.DynamicPseudoType, |
| Required: true, |
| }, |
| }, |
| }, |
| }, |
| "list": &NestedBlock{ |
| Nesting: NestingList, |
| }, |
| "set": &NestedBlock{ |
| Nesting: NestingSet, |
| }, |
| "map": &NestedBlock{ |
| Nesting: NestingMap, |
| }, |
| }, |
| }, |
| cty.Object(map[string]cty.Type{ |
| "single": cty.Object(map[string]cty.Type{ |
| "foo": cty.DynamicPseudoType, |
| }), |
| "list": cty.List(cty.EmptyObject), |
| "set": cty.Set(cty.EmptyObject), |
| "map": cty.Map(cty.EmptyObject), |
| }), |
| }, |
| "deep block nesting": { |
| &Block{ |
| BlockTypes: map[string]*NestedBlock{ |
| "single": &NestedBlock{ |
| Nesting: NestingSingle, |
| Block: Block{ |
| BlockTypes: map[string]*NestedBlock{ |
| "list": &NestedBlock{ |
| Nesting: NestingList, |
| Block: Block{ |
| BlockTypes: map[string]*NestedBlock{ |
| "set": &NestedBlock{ |
| Nesting: NestingSet, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| cty.Object(map[string]cty.Type{ |
| "single": cty.Object(map[string]cty.Type{ |
| "list": cty.List(cty.Object(map[string]cty.Type{ |
| "set": cty.Set(cty.EmptyObject), |
| })), |
| }), |
| }), |
| }, |
| "nested objects with optional attrs": { |
| &Block{ |
| Attributes: map[string]*Attribute{ |
| "map": { |
| Optional: true, |
| NestedType: &Object{ |
| Nesting: NestingMap, |
| Attributes: map[string]*Attribute{ |
| "optional": {Type: cty.String, Optional: true}, |
| "required": {Type: cty.Number, Required: true}, |
| "computed": {Type: cty.List(cty.Bool), Computed: true}, |
| "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| // The ImpliedType from the type-level block should not contain any |
| // optional attributes. |
| cty.Object(map[string]cty.Type{ |
| "map": cty.Map(cty.Object( |
| map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| }, |
| )), |
| }), |
| }, |
| } |
| |
| for name, test := range tests { |
| t.Run(name, func(t *testing.T) { |
| got := test.Schema.ImpliedType() |
| if !got.Equals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestBlockContainsSensitive(t *testing.T) { |
| tests := map[string]struct { |
| Schema *Block |
| Want bool |
| }{ |
| "object contains sensitive": { |
| &Block{ |
| Attributes: map[string]*Attribute{ |
| "sensitive": {Sensitive: true}, |
| }, |
| }, |
| true, |
| }, |
| "no sensitive attrs": { |
| &Block{ |
| Attributes: map[string]*Attribute{ |
| "insensitive": {}, |
| }, |
| }, |
| false, |
| }, |
| "nested object contains sensitive": { |
| &Block{ |
| Attributes: map[string]*Attribute{ |
| "nested": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "sensitive": {Sensitive: true}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| true, |
| }, |
| "nested obj, no sensitive attrs": { |
| &Block{ |
| Attributes: map[string]*Attribute{ |
| "nested": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "public": {}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| false, |
| }, |
| } |
| |
| for name, test := range tests { |
| t.Run(name, func(t *testing.T) { |
| got := test.Schema.ContainsSensitive() |
| if got != test.Want { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| |
| } |
| |
| func TestObjectImpliedType(t *testing.T) { |
| tests := map[string]struct { |
| Schema *Object |
| Want cty.Type |
| }{ |
| "nil": { |
| nil, |
| cty.EmptyObject, |
| }, |
| "empty": { |
| &Object{}, |
| cty.EmptyObject, |
| }, |
| "attributes": { |
| &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "optional": {Type: cty.String, Optional: true}, |
| "required": {Type: cty.Number, Required: true}, |
| "computed": {Type: cty.List(cty.Bool), Computed: true}, |
| "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, |
| }, |
| }, |
| cty.Object( |
| map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| }, |
| ), |
| }, |
| "nested attributes": { |
| &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "nested_type": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "optional": {Type: cty.String, Optional: true}, |
| "required": {Type: cty.Number, Required: true}, |
| "computed": {Type: cty.List(cty.Bool), Computed: true}, |
| "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, |
| }, |
| }, |
| Optional: true, |
| }, |
| }, |
| }, |
| cty.Object(map[string]cty.Type{ |
| "nested_type": cty.Object(map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| }), |
| }), |
| }, |
| "nested object-type attributes": { |
| &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "nested_type": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "optional": {Type: cty.String, Optional: true}, |
| "required": {Type: cty.Number, Required: true}, |
| "computed": {Type: cty.List(cty.Bool), Computed: true}, |
| "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, |
| "object": { |
| Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| }, []string{"optional"}), |
| }, |
| }, |
| }, |
| Optional: true, |
| }, |
| }, |
| }, |
| cty.Object(map[string]cty.Type{ |
| "nested_type": cty.Object(map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| "object": cty.Object(map[string]cty.Type{"optional": cty.String, "required": cty.Number}), |
| }), |
| }), |
| }, |
| "NestingList": { |
| &Object{ |
| Nesting: NestingList, |
| Attributes: map[string]*Attribute{ |
| "foo": {Type: cty.String, Optional: true}, |
| }, |
| }, |
| cty.List(cty.Object(map[string]cty.Type{"foo": cty.String})), |
| }, |
| "NestingMap": { |
| &Object{ |
| Nesting: NestingMap, |
| Attributes: map[string]*Attribute{ |
| "foo": {Type: cty.String}, |
| }, |
| }, |
| cty.Map(cty.Object(map[string]cty.Type{"foo": cty.String})), |
| }, |
| "NestingSet": { |
| &Object{ |
| Nesting: NestingSet, |
| Attributes: map[string]*Attribute{ |
| "foo": {Type: cty.String}, |
| }, |
| }, |
| cty.Set(cty.Object(map[string]cty.Type{"foo": cty.String})), |
| }, |
| "deeply nested NestingList": { |
| &Object{ |
| Nesting: NestingList, |
| Attributes: map[string]*Attribute{ |
| "foo": { |
| NestedType: &Object{ |
| Nesting: NestingList, |
| Attributes: map[string]*Attribute{ |
| "bar": {Type: cty.String}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| cty.List(cty.Object(map[string]cty.Type{"foo": cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))})), |
| }, |
| } |
| |
| for name, test := range tests { |
| t.Run(name, func(t *testing.T) { |
| got := test.Schema.ImpliedType() |
| if !got.Equals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |
| |
| func TestObjectContainsSensitive(t *testing.T) { |
| tests := map[string]struct { |
| Schema *Object |
| Want bool |
| }{ |
| "object contains sensitive": { |
| &Object{ |
| Attributes: map[string]*Attribute{ |
| "sensitive": {Sensitive: true}, |
| }, |
| }, |
| true, |
| }, |
| "no sensitive attrs": { |
| &Object{ |
| Attributes: map[string]*Attribute{ |
| "insensitive": {}, |
| }, |
| }, |
| false, |
| }, |
| "nested object contains sensitive": { |
| &Object{ |
| Attributes: map[string]*Attribute{ |
| "nested": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "sensitive": {Sensitive: true}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| true, |
| }, |
| "nested obj, no sensitive attrs": { |
| &Object{ |
| Attributes: map[string]*Attribute{ |
| "nested": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "public": {}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| false, |
| }, |
| "several nested objects, one contains sensitive": { |
| &Object{ |
| Attributes: map[string]*Attribute{ |
| "alpha": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "nonsensitive": {}, |
| }, |
| }, |
| }, |
| "beta": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "sensitive": {Sensitive: true}, |
| }, |
| }, |
| }, |
| "gamma": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "nonsensitive": {}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| true, |
| }, |
| } |
| |
| for name, test := range tests { |
| t.Run(name, func(t *testing.T) { |
| got := test.Schema.ContainsSensitive() |
| if got != test.Want { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| |
| } |
| |
| // Nested attribute should return optional object attributes for decoding. |
| func TestObjectSpecType(t *testing.T) { |
| tests := map[string]struct { |
| Schema *Object |
| Want cty.Type |
| }{ |
| "attributes": { |
| &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "optional": {Type: cty.String, Optional: true}, |
| "required": {Type: cty.Number, Required: true}, |
| "computed": {Type: cty.List(cty.Bool), Computed: true}, |
| "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, |
| }, |
| }, |
| cty.ObjectWithOptionalAttrs( |
| map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| }, |
| []string{"optional", "computed", "optional_computed"}, |
| ), |
| }, |
| "nested attributes": { |
| &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "nested_type": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "optional": {Type: cty.String, Optional: true}, |
| "required": {Type: cty.Number, Required: true}, |
| "computed": {Type: cty.List(cty.Bool), Computed: true}, |
| "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, |
| }, |
| }, |
| Optional: true, |
| }, |
| }, |
| }, |
| cty.ObjectWithOptionalAttrs(map[string]cty.Type{ |
| "nested_type": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| }, []string{"optional", "computed", "optional_computed"}), |
| }, []string{"nested_type"}), |
| }, |
| "nested object-type attributes": { |
| &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "nested_type": { |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "optional": {Type: cty.String, Optional: true}, |
| "required": {Type: cty.Number, Required: true}, |
| "computed": {Type: cty.List(cty.Bool), Computed: true}, |
| "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, |
| "object": { |
| Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| }, []string{"optional"}), |
| }, |
| }, |
| }, |
| Optional: true, |
| }, |
| }, |
| }, |
| cty.ObjectWithOptionalAttrs(map[string]cty.Type{ |
| "nested_type": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ |
| "optional": cty.String, |
| "required": cty.Number, |
| "computed": cty.List(cty.Bool), |
| "optional_computed": cty.Map(cty.Bool), |
| "object": cty.ObjectWithOptionalAttrs(map[string]cty.Type{"optional": cty.String, "required": cty.Number}, []string{"optional"}), |
| }, []string{"optional", "computed", "optional_computed"}), |
| }, []string{"nested_type"}), |
| }, |
| "NestingList": { |
| &Object{ |
| Nesting: NestingList, |
| Attributes: map[string]*Attribute{ |
| "foo": {Type: cty.String, Optional: true}, |
| }, |
| }, |
| cty.List(cty.ObjectWithOptionalAttrs(map[string]cty.Type{"foo": cty.String}, []string{"foo"})), |
| }, |
| "NestingMap": { |
| &Object{ |
| Nesting: NestingMap, |
| Attributes: map[string]*Attribute{ |
| "foo": {Type: cty.String}, |
| }, |
| }, |
| cty.Map(cty.Object(map[string]cty.Type{"foo": cty.String})), |
| }, |
| "NestingSet": { |
| &Object{ |
| Nesting: NestingSet, |
| Attributes: map[string]*Attribute{ |
| "foo": {Type: cty.String}, |
| }, |
| }, |
| cty.Set(cty.Object(map[string]cty.Type{"foo": cty.String})), |
| }, |
| "deeply nested NestingList": { |
| &Object{ |
| Nesting: NestingList, |
| Attributes: map[string]*Attribute{ |
| "foo": { |
| NestedType: &Object{ |
| Nesting: NestingList, |
| Attributes: map[string]*Attribute{ |
| "bar": {Type: cty.String}, |
| }, |
| }, |
| }, |
| }, |
| }, |
| cty.List(cty.Object(map[string]cty.Type{"foo": cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))})), |
| }, |
| } |
| |
| for name, test := range tests { |
| t.Run(name, func(t *testing.T) { |
| got := test.Schema.specType() |
| if !got.Equals(test.Want) { |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) |
| } |
| }) |
| } |
| } |