terraform/helper/schema/core_schema_test.go

308 lines
6.7 KiB
Go

package schema
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/configs/configschema"
)
// add the implicit "id" attribute for test resources
func testResource(block *configschema.Block) *configschema.Block {
if block.Attributes == nil {
block.Attributes = make(map[string]*configschema.Attribute)
}
if block.BlockTypes == nil {
block.BlockTypes = make(map[string]*configschema.NestedBlock)
}
if block.Attributes["id"] == nil {
block.Attributes["id"] = &configschema.Attribute{
Type: cty.String,
Optional: true,
Computed: true,
}
}
return block
}
func TestSchemaMapCoreConfigSchema(t *testing.T) {
tests := map[string]struct {
Schema map[string]*Schema
Want *configschema.Block
}{
"empty": {
map[string]*Schema{},
testResource(&configschema.Block{}),
},
"primitives": {
map[string]*Schema{
"int": {
Type: TypeInt,
Required: true,
Description: "foo bar baz",
},
"float": {
Type: TypeFloat,
Optional: true,
},
"bool": {
Type: TypeBool,
Computed: true,
},
"string": {
Type: TypeString,
Optional: true,
Computed: true,
},
},
testResource(&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"int": {
Type: cty.Number,
Required: true,
Description: "foo bar baz",
},
"float": {
Type: cty.Number,
Optional: true,
},
"bool": {
Type: cty.Bool,
Computed: true,
},
"string": {
Type: cty.String,
Optional: true,
Computed: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{},
}),
},
"simple collections": {
map[string]*Schema{
"list": {
Type: TypeList,
Required: true,
Elem: &Schema{
Type: TypeInt,
},
},
"set": {
Type: TypeSet,
Optional: true,
Elem: &Schema{
Type: TypeString,
},
},
"map": {
Type: TypeMap,
Optional: true,
Elem: &Schema{
Type: TypeBool,
},
},
"map_default_type": {
Type: TypeMap,
Optional: true,
// Maps historically don't have elements because we
// assumed they would be strings, so this needs to work
// for pre-existing schemas.
},
},
testResource(&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"list": {
Type: cty.List(cty.Number),
Required: true,
},
"set": {
Type: cty.Set(cty.String),
Optional: true,
},
"map": {
Type: cty.Map(cty.Bool),
Optional: true,
},
"map_default_type": {
Type: cty.Map(cty.String),
Optional: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{},
}),
},
"incorrectly-specified collections": {
// Historically we tolerated setting a type directly as the Elem
// attribute, rather than a Schema object. This is common enough
// in existing provider code that we must support it as an alias
// for a schema object with the given type.
map[string]*Schema{
"list": {
Type: TypeList,
Required: true,
Elem: TypeInt,
},
"set": {
Type: TypeSet,
Optional: true,
Elem: TypeString,
},
"map": {
Type: TypeMap,
Optional: true,
Elem: TypeBool,
},
},
testResource(&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"list": {
Type: cty.List(cty.Number),
Required: true,
},
"set": {
Type: cty.Set(cty.String),
Optional: true,
},
"map": {
Type: cty.Map(cty.Bool),
Optional: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{},
}),
},
"sub-resource collections": {
map[string]*Schema{
"list": {
Type: TypeList,
Required: true,
Elem: &Resource{
Schema: map[string]*Schema{},
},
MinItems: 1,
MaxItems: 2,
},
"set": {
Type: TypeSet,
Required: true,
Elem: &Resource{
Schema: map[string]*Schema{},
},
},
"map": {
Type: TypeMap,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{},
},
},
},
testResource(&configschema.Block{
Attributes: map[string]*configschema.Attribute{},
BlockTypes: map[string]*configschema.NestedBlock{
"list": {
Nesting: configschema.NestingList,
Block: configschema.Block{},
MinItems: 1,
MaxItems: 2,
},
"set": {
Nesting: configschema.NestingSet,
Block: configschema.Block{},
MinItems: 1, // because schema is Required
},
"map": {
Nesting: configschema.NestingMap,
Block: configschema.Block{},
},
},
}),
},
"nested attributes and blocks": {
map[string]*Schema{
"foo": {
Type: TypeList,
Required: true,
Elem: &Resource{
Schema: map[string]*Schema{
"bar": {
Type: TypeList,
Required: true,
Elem: &Schema{
Type: TypeList,
Elem: &Schema{
Type: TypeString,
},
},
},
"baz": {
Type: TypeSet,
Optional: true,
Elem: &Resource{
Schema: map[string]*Schema{},
},
},
},
},
},
},
testResource(&configschema.Block{
Attributes: map[string]*configschema.Attribute{},
BlockTypes: map[string]*configschema.NestedBlock{
"foo": &configschema.NestedBlock{
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"bar": {
Type: cty.List(cty.List(cty.String)),
Required: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{
"baz": {
Nesting: configschema.NestingSet,
Block: configschema.Block{},
},
},
},
MinItems: 1, // because schema is Required
},
},
}),
},
"sensitive": {
map[string]*Schema{
"string": {
Type: TypeString,
Optional: true,
Sensitive: true,
},
},
testResource(&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"string": {
Type: cty.String,
Optional: true,
Sensitive: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{},
}),
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := (&Resource{Schema: test.Schema}).CoreConfigSchema()
if !cmp.Equal(got, test.Want, equateEmpty, typeComparer) {
t.Error(cmp.Diff(got, test.Want, equateEmpty, typeComparer))
}
})
}
}