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 }