github.com/KevinKlinger/open_terraform@v0.11.12-beta1/config/configschema/internal_validate_test.go (about)

     1  package configschema
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/zclconf/go-cty/cty"
     7  
     8  	multierror "github.com/hashicorp/go-multierror"
     9  )
    10  
    11  func TestBlockInternalValidate(t *testing.T) {
    12  	tests := map[string]struct {
    13  		Block    *Block
    14  		ErrCount int
    15  	}{
    16  		"empty": {
    17  			&Block{},
    18  			0,
    19  		},
    20  		"valid": {
    21  			&Block{
    22  				Attributes: map[string]*Attribute{
    23  					"foo": &Attribute{
    24  						Type:     cty.String,
    25  						Required: true,
    26  					},
    27  					"bar": &Attribute{
    28  						Type:     cty.String,
    29  						Optional: true,
    30  					},
    31  					"baz": &Attribute{
    32  						Type:     cty.String,
    33  						Computed: true,
    34  					},
    35  					"baz_maybe": &Attribute{
    36  						Type:     cty.String,
    37  						Optional: true,
    38  						Computed: true,
    39  					},
    40  				},
    41  				BlockTypes: map[string]*NestedBlock{
    42  					"single": &NestedBlock{
    43  						Nesting: NestingSingle,
    44  						Block:   Block{},
    45  					},
    46  					"single_required": &NestedBlock{
    47  						Nesting:  NestingSingle,
    48  						Block:    Block{},
    49  						MinItems: 1,
    50  						MaxItems: 1,
    51  					},
    52  					"list": &NestedBlock{
    53  						Nesting: NestingList,
    54  						Block:   Block{},
    55  					},
    56  					"list_required": &NestedBlock{
    57  						Nesting:  NestingList,
    58  						Block:    Block{},
    59  						MinItems: 1,
    60  					},
    61  					"set": &NestedBlock{
    62  						Nesting: NestingSet,
    63  						Block:   Block{},
    64  					},
    65  					"set_required": &NestedBlock{
    66  						Nesting:  NestingSet,
    67  						Block:    Block{},
    68  						MinItems: 1,
    69  					},
    70  					"map": &NestedBlock{
    71  						Nesting: NestingMap,
    72  						Block:   Block{},
    73  					},
    74  				},
    75  			},
    76  			0,
    77  		},
    78  		"attribute with no flags set": {
    79  			&Block{
    80  				Attributes: map[string]*Attribute{
    81  					"foo": &Attribute{
    82  						Type: cty.String,
    83  					},
    84  				},
    85  			},
    86  			1, // must set one of the flags
    87  		},
    88  		"attribute required and optional": {
    89  			&Block{
    90  				Attributes: map[string]*Attribute{
    91  					"foo": &Attribute{
    92  						Type:     cty.String,
    93  						Required: true,
    94  						Optional: true,
    95  					},
    96  				},
    97  			},
    98  			1, // both required and optional
    99  		},
   100  		"attribute required and computed": {
   101  			&Block{
   102  				Attributes: map[string]*Attribute{
   103  					"foo": &Attribute{
   104  						Type:     cty.String,
   105  						Required: true,
   106  						Computed: true,
   107  					},
   108  				},
   109  			},
   110  			1, // both required and computed
   111  		},
   112  		"attribute optional and computed": {
   113  			&Block{
   114  				Attributes: map[string]*Attribute{
   115  					"foo": &Attribute{
   116  						Type:     cty.String,
   117  						Optional: true,
   118  						Computed: true,
   119  					},
   120  				},
   121  			},
   122  			0,
   123  		},
   124  		"attribute with missing type": {
   125  			&Block{
   126  				Attributes: map[string]*Attribute{
   127  					"foo": &Attribute{
   128  						Optional: true,
   129  					},
   130  				},
   131  			},
   132  			1, // Type must be set
   133  		},
   134  		"attribute with invalid name": {
   135  			&Block{
   136  				Attributes: map[string]*Attribute{
   137  					"fooBar": &Attribute{
   138  						Type:     cty.String,
   139  						Optional: true,
   140  					},
   141  				},
   142  			},
   143  			1, // name may not contain uppercase letters
   144  		},
   145  		"block type with invalid name": {
   146  			&Block{
   147  				BlockTypes: map[string]*NestedBlock{
   148  					"fooBar": &NestedBlock{
   149  						Nesting: NestingSingle,
   150  					},
   151  				},
   152  			},
   153  			1, // name may not contain uppercase letters
   154  		},
   155  		"colliding names": {
   156  			&Block{
   157  				Attributes: map[string]*Attribute{
   158  					"foo": &Attribute{
   159  						Type:     cty.String,
   160  						Optional: true,
   161  					},
   162  				},
   163  				BlockTypes: map[string]*NestedBlock{
   164  					"foo": &NestedBlock{
   165  						Nesting: NestingSingle,
   166  					},
   167  				},
   168  			},
   169  			1, // "foo" is defined as both attribute and block type
   170  		},
   171  		"nested block with badness": {
   172  			&Block{
   173  				BlockTypes: map[string]*NestedBlock{
   174  					"bad": &NestedBlock{
   175  						Nesting: NestingSingle,
   176  						Block: Block{
   177  							Attributes: map[string]*Attribute{
   178  								"nested_bad": &Attribute{
   179  									Type:     cty.String,
   180  									Required: true,
   181  									Optional: true,
   182  								},
   183  							},
   184  						},
   185  					},
   186  				},
   187  			},
   188  			1, // nested_bad is both required and optional
   189  		},
   190  		"nil": {
   191  			nil,
   192  			1, // block is nil
   193  		},
   194  		"nil attr": {
   195  			&Block{
   196  				Attributes: map[string]*Attribute{
   197  					"bad": nil,
   198  				},
   199  			},
   200  			1, // attribute schema is nil
   201  		},
   202  		"nil block type": {
   203  			&Block{
   204  				BlockTypes: map[string]*NestedBlock{
   205  					"bad": nil,
   206  				},
   207  			},
   208  			1, // block schema is nil
   209  		},
   210  	}
   211  
   212  	for name, test := range tests {
   213  		t.Run(name, func(t *testing.T) {
   214  			errs := multierrorErrors(test.Block.InternalValidate())
   215  			if got, want := len(errs), test.ErrCount; got != want {
   216  				t.Errorf("wrong number of errors %d; want %d", got, want)
   217  				for _, err := range errs {
   218  					t.Logf("- %s", err.Error())
   219  				}
   220  			}
   221  		})
   222  	}
   223  }
   224  
   225  func multierrorErrors(err error) []error {
   226  	// A function like this should really be part of the multierror package...
   227  
   228  	if err == nil {
   229  		return nil
   230  	}
   231  
   232  	switch terr := err.(type) {
   233  	case *multierror.Error:
   234  		return terr.Errors
   235  	default:
   236  		return []error{err}
   237  	}
   238  }