mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-09 23:54:17 -06:00
f117906bdb
This checks that a schema complies with the documented constraints on which values are valid. It is primarily intended for use in tests.
239 lines
4.6 KiB
Go
239 lines
4.6 KiB
Go
package configschema
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
multierror "github.com/hashicorp/go-multierror"
|
|
)
|
|
|
|
func TestBlockInternalValidate(t *testing.T) {
|
|
tests := map[string]struct {
|
|
Block *Block
|
|
ErrCount int
|
|
}{
|
|
"empty": {
|
|
&Block{},
|
|
0,
|
|
},
|
|
"valid": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"foo": &Attribute{
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
"bar": &Attribute{
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
"baz": &Attribute{
|
|
Type: cty.String,
|
|
Computed: true,
|
|
},
|
|
"baz_maybe": &Attribute{
|
|
Type: cty.String,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
BlockTypes: map[string]*NestedBlock{
|
|
"single": &NestedBlock{
|
|
Nesting: NestingSingle,
|
|
Block: Block{},
|
|
},
|
|
"single_required": &NestedBlock{
|
|
Nesting: NestingSingle,
|
|
Block: Block{},
|
|
MinItems: 1,
|
|
MaxItems: 1,
|
|
},
|
|
"list": &NestedBlock{
|
|
Nesting: NestingList,
|
|
Block: Block{},
|
|
},
|
|
"list_required": &NestedBlock{
|
|
Nesting: NestingList,
|
|
Block: Block{},
|
|
MinItems: 1,
|
|
},
|
|
"set": &NestedBlock{
|
|
Nesting: NestingSet,
|
|
Block: Block{},
|
|
},
|
|
"set_required": &NestedBlock{
|
|
Nesting: NestingSet,
|
|
Block: Block{},
|
|
MinItems: 1,
|
|
},
|
|
"map": &NestedBlock{
|
|
Nesting: NestingMap,
|
|
Block: Block{},
|
|
},
|
|
},
|
|
},
|
|
0,
|
|
},
|
|
"attribute with no flags set": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"foo": &Attribute{
|
|
Type: cty.String,
|
|
},
|
|
},
|
|
},
|
|
1, // must set one of the flags
|
|
},
|
|
"attribute required and optional": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"foo": &Attribute{
|
|
Type: cty.String,
|
|
Required: true,
|
|
Optional: true,
|
|
},
|
|
},
|
|
},
|
|
1, // both required and optional
|
|
},
|
|
"attribute required and computed": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"foo": &Attribute{
|
|
Type: cty.String,
|
|
Required: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
1, // both required and computed
|
|
},
|
|
"attribute optional and computed": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"foo": &Attribute{
|
|
Type: cty.String,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
0,
|
|
},
|
|
"attribute with missing type": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"foo": &Attribute{
|
|
Optional: true,
|
|
},
|
|
},
|
|
},
|
|
1, // Type must be set
|
|
},
|
|
"attribute with invalid name": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"fooBar": &Attribute{
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
},
|
|
},
|
|
1, // name may not contain uppercase letters
|
|
},
|
|
"block type with invalid name": {
|
|
&Block{
|
|
BlockTypes: map[string]*NestedBlock{
|
|
"fooBar": &NestedBlock{
|
|
Nesting: NestingSingle,
|
|
},
|
|
},
|
|
},
|
|
1, // name may not contain uppercase letters
|
|
},
|
|
"colliding names": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"foo": &Attribute{
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
},
|
|
BlockTypes: map[string]*NestedBlock{
|
|
"foo": &NestedBlock{
|
|
Nesting: NestingSingle,
|
|
},
|
|
},
|
|
},
|
|
1, // "foo" is defined as both attribute and block type
|
|
},
|
|
"nested block with badness": {
|
|
&Block{
|
|
BlockTypes: map[string]*NestedBlock{
|
|
"bad": &NestedBlock{
|
|
Nesting: NestingSingle,
|
|
Block: Block{
|
|
Attributes: map[string]*Attribute{
|
|
"nested_bad": &Attribute{
|
|
Type: cty.String,
|
|
Required: true,
|
|
Optional: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
1, // nested_bad is both required and optional
|
|
},
|
|
"nil": {
|
|
nil,
|
|
1, // block is nil
|
|
},
|
|
"nil attr": {
|
|
&Block{
|
|
Attributes: map[string]*Attribute{
|
|
"bad": nil,
|
|
},
|
|
},
|
|
1, // attribute schema is nil
|
|
},
|
|
"nil block type": {
|
|
&Block{
|
|
BlockTypes: map[string]*NestedBlock{
|
|
"bad": nil,
|
|
},
|
|
},
|
|
1, // block schema is nil
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
errs := multierrorErrors(test.Block.InternalValidate())
|
|
if got, want := len(errs), test.ErrCount; got != want {
|
|
t.Errorf("wrong number of errors %d; want %d", got, want)
|
|
for _, err := range errs {
|
|
t.Logf("- %s", err.Error())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func multierrorErrors(err error) []error {
|
|
// A function like this should really be part of the multierror package...
|
|
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
switch terr := err.(type) {
|
|
case *multierror.Error:
|
|
return terr.Errors
|
|
default:
|
|
return []error{err}
|
|
}
|
|
}
|