github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/configs/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  		Errs  []string
    15  	}{
    16  		"empty": {
    17  			&Block{},
    18  			[]string{},
    19  		},
    20  		"valid": {
    21  			&Block{
    22  				Attributes: map[string]*Attribute{
    23  					"foo": {
    24  						Type:     cty.String,
    25  						Required: true,
    26  					},
    27  					"bar": {
    28  						Type:     cty.String,
    29  						Optional: true,
    30  					},
    31  					"baz": {
    32  						Type:     cty.String,
    33  						Computed: true,
    34  					},
    35  					"baz_maybe": {
    36  						Type:     cty.String,
    37  						Optional: true,
    38  						Computed: true,
    39  					},
    40  				},
    41  				BlockTypes: map[string]*NestedBlock{
    42  					"single": {
    43  						Nesting: NestingSingle,
    44  						Block:   Block{},
    45  					},
    46  					"single_required": {
    47  						Nesting:  NestingSingle,
    48  						Block:    Block{},
    49  						MinItems: 1,
    50  						MaxItems: 1,
    51  					},
    52  					"list": {
    53  						Nesting: NestingList,
    54  						Block:   Block{},
    55  					},
    56  					"list_required": {
    57  						Nesting:  NestingList,
    58  						Block:    Block{},
    59  						MinItems: 1,
    60  					},
    61  					"set": {
    62  						Nesting: NestingSet,
    63  						Block:   Block{},
    64  					},
    65  					"set_required": {
    66  						Nesting:  NestingSet,
    67  						Block:    Block{},
    68  						MinItems: 1,
    69  					},
    70  					"map": {
    71  						Nesting: NestingMap,
    72  						Block:   Block{},
    73  					},
    74  				},
    75  			},
    76  			[]string{},
    77  		},
    78  		"attribute with no flags set": {
    79  			&Block{
    80  				Attributes: map[string]*Attribute{
    81  					"foo": {
    82  						Type: cty.String,
    83  					},
    84  				},
    85  			},
    86  			[]string{"foo: must set Optional, Required or Computed"},
    87  		},
    88  		"attribute required and optional": {
    89  			&Block{
    90  				Attributes: map[string]*Attribute{
    91  					"foo": {
    92  						Type:     cty.String,
    93  						Required: true,
    94  						Optional: true,
    95  					},
    96  				},
    97  			},
    98  			[]string{"foo: cannot set both Optional and Required"},
    99  		},
   100  		"attribute required and computed": {
   101  			&Block{
   102  				Attributes: map[string]*Attribute{
   103  					"foo": {
   104  						Type:     cty.String,
   105  						Required: true,
   106  						Computed: true,
   107  					},
   108  				},
   109  			},
   110  			[]string{"foo: cannot set both Computed and Required"},
   111  		},
   112  		"attribute optional and computed": {
   113  			&Block{
   114  				Attributes: map[string]*Attribute{
   115  					"foo": {
   116  						Type:     cty.String,
   117  						Optional: true,
   118  						Computed: true,
   119  					},
   120  				},
   121  			},
   122  			[]string{},
   123  		},
   124  		"attribute with missing type": {
   125  			&Block{
   126  				Attributes: map[string]*Attribute{
   127  					"foo": {
   128  						Optional: true,
   129  					},
   130  				},
   131  			},
   132  			[]string{"foo: either Type or NestedType must be defined"},
   133  		},
   134  		/* FIXME: This caused errors when applied to existing providers (oci)
   135  		and cannot be enforced without coordination.
   136  
   137  		"attribute with invalid name": {&Block{Attributes:
   138  		    map[string]*Attribute{"fooBar": {Type:     cty.String, Optional:
   139  		    true,
   140  		            },
   141  		        },
   142  		    },
   143  		    []string{"fooBar: name may contain only lowercase letters, digits and underscores"},
   144  		},
   145  		*/
   146  		"attribute with invalid NestedType attribute": {
   147  			&Block{
   148  				Attributes: map[string]*Attribute{
   149  					"foo": {
   150  						NestedType: &Object{
   151  							Nesting: NestingSingle,
   152  							Attributes: map[string]*Attribute{
   153  								"foo": {
   154  									Type:     cty.String,
   155  									Required: true,
   156  									Optional: true,
   157  								},
   158  							},
   159  						},
   160  						Optional: true,
   161  					},
   162  				},
   163  			},
   164  			[]string{"foo: cannot set both Optional and Required"},
   165  		},
   166  		"block type with invalid name": {
   167  			&Block{
   168  				BlockTypes: map[string]*NestedBlock{
   169  					"fooBar": {
   170  						Nesting: NestingSingle,
   171  					},
   172  				},
   173  			},
   174  			[]string{"fooBar: name may contain only lowercase letters, digits and underscores"},
   175  		},
   176  		"colliding names": {
   177  			&Block{
   178  				Attributes: map[string]*Attribute{
   179  					"foo": {
   180  						Type:     cty.String,
   181  						Optional: true,
   182  					},
   183  				},
   184  				BlockTypes: map[string]*NestedBlock{
   185  					"foo": {
   186  						Nesting: NestingSingle,
   187  					},
   188  				},
   189  			},
   190  			[]string{"foo: name defined as both attribute and child block type"},
   191  		},
   192  		"nested block with badness": {
   193  			&Block{
   194  				BlockTypes: map[string]*NestedBlock{
   195  					"bad": {
   196  						Nesting: NestingSingle,
   197  						Block: Block{
   198  							Attributes: map[string]*Attribute{
   199  								"nested_bad": {
   200  									Type:     cty.String,
   201  									Required: true,
   202  									Optional: true,
   203  								},
   204  							},
   205  						},
   206  					},
   207  				},
   208  			},
   209  			[]string{"bad.nested_bad: cannot set both Optional and Required"},
   210  		},
   211  		"nested list block with dynamically-typed attribute": {
   212  			&Block{
   213  				BlockTypes: map[string]*NestedBlock{
   214  					"bad": {
   215  						Nesting: NestingList,
   216  						Block: Block{
   217  							Attributes: map[string]*Attribute{
   218  								"nested_bad": {
   219  									Type:     cty.DynamicPseudoType,
   220  									Optional: true,
   221  								},
   222  							},
   223  						},
   224  					},
   225  				},
   226  			},
   227  			[]string{},
   228  		},
   229  		"nested set block with dynamically-typed attribute": {
   230  			&Block{
   231  				BlockTypes: map[string]*NestedBlock{
   232  					"bad": {
   233  						Nesting: NestingSet,
   234  						Block: Block{
   235  							Attributes: map[string]*Attribute{
   236  								"nested_bad": {
   237  									Type:     cty.DynamicPseudoType,
   238  									Optional: true,
   239  								},
   240  							},
   241  						},
   242  					},
   243  				},
   244  			},
   245  			[]string{"bad: NestingSet blocks may not contain attributes of cty.DynamicPseudoType"},
   246  		},
   247  		"nil": {
   248  			nil,
   249  			[]string{"top-level block schema is nil"},
   250  		},
   251  		"nil attr": {
   252  			&Block{
   253  				Attributes: map[string]*Attribute{
   254  					"bad": nil,
   255  				},
   256  			},
   257  			[]string{"bad: attribute schema is nil"},
   258  		},
   259  		"nil block type": {
   260  			&Block{
   261  				BlockTypes: map[string]*NestedBlock{
   262  					"bad": nil,
   263  				},
   264  			},
   265  			[]string{"bad: block schema is nil"},
   266  		},
   267  	}
   268  
   269  	for name, test := range tests {
   270  		t.Run(name, func(t *testing.T) {
   271  			errs := multierrorErrors(test.Block.InternalValidate())
   272  			if got, want := len(errs), len(test.Errs); got != want {
   273  				t.Errorf("wrong number of errors %d; want %d", got, want)
   274  				for _, err := range errs {
   275  					t.Logf("- %s", err.Error())
   276  				}
   277  			} else {
   278  				if len(errs) > 0 {
   279  					for i := range errs {
   280  						if errs[i].Error() != test.Errs[i] {
   281  							t.Errorf("wrong error: got %s, want %s", errs[i].Error(), test.Errs[i])
   282  						}
   283  					}
   284  				}
   285  			}
   286  		})
   287  	}
   288  }
   289  
   290  func multierrorErrors(err error) []error {
   291  	// A function like this should really be part of the multierror package...
   292  
   293  	if err == nil {
   294  		return nil
   295  	}
   296  
   297  	switch terr := err.(type) {
   298  	case *multierror.Error:
   299  		return terr.Errors
   300  	default:
   301  		return []error{err}
   302  	}
   303  }