github.com/kevinklinger/open_terraform@v1.3.6/noninternal/configs/configschema/implied_type_test.go (about) 1 package configschema 2 3 import ( 4 "testing" 5 6 "github.com/zclconf/go-cty/cty" 7 ) 8 9 func TestBlockImpliedType(t *testing.T) { 10 tests := map[string]struct { 11 Schema *Block 12 Want cty.Type 13 }{ 14 "nil": { 15 nil, 16 cty.EmptyObject, 17 }, 18 "empty": { 19 &Block{}, 20 cty.EmptyObject, 21 }, 22 "attributes": { 23 &Block{ 24 Attributes: map[string]*Attribute{ 25 "optional": { 26 Type: cty.String, 27 Optional: true, 28 }, 29 "required": { 30 Type: cty.Number, 31 Required: true, 32 }, 33 "computed": { 34 Type: cty.List(cty.Bool), 35 Computed: true, 36 }, 37 "optional_computed": { 38 Type: cty.Map(cty.Bool), 39 Optional: true, 40 Computed: true, 41 }, 42 }, 43 }, 44 cty.Object(map[string]cty.Type{ 45 "optional": cty.String, 46 "required": cty.Number, 47 "computed": cty.List(cty.Bool), 48 "optional_computed": cty.Map(cty.Bool), 49 }), 50 }, 51 "blocks": { 52 &Block{ 53 BlockTypes: map[string]*NestedBlock{ 54 "single": &NestedBlock{ 55 Nesting: NestingSingle, 56 Block: Block{ 57 Attributes: map[string]*Attribute{ 58 "foo": { 59 Type: cty.DynamicPseudoType, 60 Required: true, 61 }, 62 }, 63 }, 64 }, 65 "list": &NestedBlock{ 66 Nesting: NestingList, 67 }, 68 "set": &NestedBlock{ 69 Nesting: NestingSet, 70 }, 71 "map": &NestedBlock{ 72 Nesting: NestingMap, 73 }, 74 }, 75 }, 76 cty.Object(map[string]cty.Type{ 77 "single": cty.Object(map[string]cty.Type{ 78 "foo": cty.DynamicPseudoType, 79 }), 80 "list": cty.List(cty.EmptyObject), 81 "set": cty.Set(cty.EmptyObject), 82 "map": cty.Map(cty.EmptyObject), 83 }), 84 }, 85 "deep block nesting": { 86 &Block{ 87 BlockTypes: map[string]*NestedBlock{ 88 "single": &NestedBlock{ 89 Nesting: NestingSingle, 90 Block: Block{ 91 BlockTypes: map[string]*NestedBlock{ 92 "list": &NestedBlock{ 93 Nesting: NestingList, 94 Block: Block{ 95 BlockTypes: map[string]*NestedBlock{ 96 "set": &NestedBlock{ 97 Nesting: NestingSet, 98 }, 99 }, 100 }, 101 }, 102 }, 103 }, 104 }, 105 }, 106 }, 107 cty.Object(map[string]cty.Type{ 108 "single": cty.Object(map[string]cty.Type{ 109 "list": cty.List(cty.Object(map[string]cty.Type{ 110 "set": cty.Set(cty.EmptyObject), 111 })), 112 }), 113 }), 114 }, 115 "nested objects with optional attrs": { 116 &Block{ 117 Attributes: map[string]*Attribute{ 118 "map": { 119 Optional: true, 120 NestedType: &Object{ 121 Nesting: NestingMap, 122 Attributes: map[string]*Attribute{ 123 "optional": {Type: cty.String, Optional: true}, 124 "required": {Type: cty.Number, Required: true}, 125 "computed": {Type: cty.List(cty.Bool), Computed: true}, 126 "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, 127 }, 128 }, 129 }, 130 }, 131 }, 132 // The ImpliedType from the type-level block should not contain any 133 // optional attributes. 134 cty.Object(map[string]cty.Type{ 135 "map": cty.Map(cty.Object( 136 map[string]cty.Type{ 137 "optional": cty.String, 138 "required": cty.Number, 139 "computed": cty.List(cty.Bool), 140 "optional_computed": cty.Map(cty.Bool), 141 }, 142 )), 143 }), 144 }, 145 } 146 147 for name, test := range tests { 148 t.Run(name, func(t *testing.T) { 149 got := test.Schema.ImpliedType() 150 if !got.Equals(test.Want) { 151 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 152 } 153 }) 154 } 155 } 156 157 func TestBlockContainsSensitive(t *testing.T) { 158 tests := map[string]struct { 159 Schema *Block 160 Want bool 161 }{ 162 "object contains sensitive": { 163 &Block{ 164 Attributes: map[string]*Attribute{ 165 "sensitive": {Sensitive: true}, 166 }, 167 }, 168 true, 169 }, 170 "no sensitive attrs": { 171 &Block{ 172 Attributes: map[string]*Attribute{ 173 "insensitive": {}, 174 }, 175 }, 176 false, 177 }, 178 "nested object contains sensitive": { 179 &Block{ 180 Attributes: map[string]*Attribute{ 181 "nested": { 182 NestedType: &Object{ 183 Nesting: NestingSingle, 184 Attributes: map[string]*Attribute{ 185 "sensitive": {Sensitive: true}, 186 }, 187 }, 188 }, 189 }, 190 }, 191 true, 192 }, 193 "nested obj, no sensitive attrs": { 194 &Block{ 195 Attributes: map[string]*Attribute{ 196 "nested": { 197 NestedType: &Object{ 198 Nesting: NestingSingle, 199 Attributes: map[string]*Attribute{ 200 "public": {}, 201 }, 202 }, 203 }, 204 }, 205 }, 206 false, 207 }, 208 } 209 210 for name, test := range tests { 211 t.Run(name, func(t *testing.T) { 212 got := test.Schema.ContainsSensitive() 213 if got != test.Want { 214 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 215 } 216 }) 217 } 218 219 } 220 221 func TestObjectImpliedType(t *testing.T) { 222 tests := map[string]struct { 223 Schema *Object 224 Want cty.Type 225 }{ 226 "nil": { 227 nil, 228 cty.EmptyObject, 229 }, 230 "empty": { 231 &Object{}, 232 cty.EmptyObject, 233 }, 234 "attributes": { 235 &Object{ 236 Nesting: NestingSingle, 237 Attributes: map[string]*Attribute{ 238 "optional": {Type: cty.String, Optional: true}, 239 "required": {Type: cty.Number, Required: true}, 240 "computed": {Type: cty.List(cty.Bool), Computed: true}, 241 "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, 242 }, 243 }, 244 cty.Object( 245 map[string]cty.Type{ 246 "optional": cty.String, 247 "required": cty.Number, 248 "computed": cty.List(cty.Bool), 249 "optional_computed": cty.Map(cty.Bool), 250 }, 251 ), 252 }, 253 "nested attributes": { 254 &Object{ 255 Nesting: NestingSingle, 256 Attributes: map[string]*Attribute{ 257 "nested_type": { 258 NestedType: &Object{ 259 Nesting: NestingSingle, 260 Attributes: map[string]*Attribute{ 261 "optional": {Type: cty.String, Optional: true}, 262 "required": {Type: cty.Number, Required: true}, 263 "computed": {Type: cty.List(cty.Bool), Computed: true}, 264 "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, 265 }, 266 }, 267 Optional: true, 268 }, 269 }, 270 }, 271 cty.Object(map[string]cty.Type{ 272 "nested_type": cty.Object(map[string]cty.Type{ 273 "optional": cty.String, 274 "required": cty.Number, 275 "computed": cty.List(cty.Bool), 276 "optional_computed": cty.Map(cty.Bool), 277 }), 278 }), 279 }, 280 "nested object-type attributes": { 281 &Object{ 282 Nesting: NestingSingle, 283 Attributes: map[string]*Attribute{ 284 "nested_type": { 285 NestedType: &Object{ 286 Nesting: NestingSingle, 287 Attributes: map[string]*Attribute{ 288 "optional": {Type: cty.String, Optional: true}, 289 "required": {Type: cty.Number, Required: true}, 290 "computed": {Type: cty.List(cty.Bool), Computed: true}, 291 "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, 292 "object": { 293 Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ 294 "optional": cty.String, 295 "required": cty.Number, 296 }, []string{"optional"}), 297 }, 298 }, 299 }, 300 Optional: true, 301 }, 302 }, 303 }, 304 cty.Object(map[string]cty.Type{ 305 "nested_type": cty.Object(map[string]cty.Type{ 306 "optional": cty.String, 307 "required": cty.Number, 308 "computed": cty.List(cty.Bool), 309 "optional_computed": cty.Map(cty.Bool), 310 "object": cty.Object(map[string]cty.Type{"optional": cty.String, "required": cty.Number}), 311 }), 312 }), 313 }, 314 "NestingList": { 315 &Object{ 316 Nesting: NestingList, 317 Attributes: map[string]*Attribute{ 318 "foo": {Type: cty.String, Optional: true}, 319 }, 320 }, 321 cty.List(cty.Object(map[string]cty.Type{"foo": cty.String})), 322 }, 323 "NestingMap": { 324 &Object{ 325 Nesting: NestingMap, 326 Attributes: map[string]*Attribute{ 327 "foo": {Type: cty.String}, 328 }, 329 }, 330 cty.Map(cty.Object(map[string]cty.Type{"foo": cty.String})), 331 }, 332 "NestingSet": { 333 &Object{ 334 Nesting: NestingSet, 335 Attributes: map[string]*Attribute{ 336 "foo": {Type: cty.String}, 337 }, 338 }, 339 cty.Set(cty.Object(map[string]cty.Type{"foo": cty.String})), 340 }, 341 "deeply nested NestingList": { 342 &Object{ 343 Nesting: NestingList, 344 Attributes: map[string]*Attribute{ 345 "foo": { 346 NestedType: &Object{ 347 Nesting: NestingList, 348 Attributes: map[string]*Attribute{ 349 "bar": {Type: cty.String}, 350 }, 351 }, 352 }, 353 }, 354 }, 355 cty.List(cty.Object(map[string]cty.Type{"foo": cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))})), 356 }, 357 } 358 359 for name, test := range tests { 360 t.Run(name, func(t *testing.T) { 361 got := test.Schema.ImpliedType() 362 if !got.Equals(test.Want) { 363 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 364 } 365 }) 366 } 367 } 368 369 func TestObjectContainsSensitive(t *testing.T) { 370 tests := map[string]struct { 371 Schema *Object 372 Want bool 373 }{ 374 "object contains sensitive": { 375 &Object{ 376 Attributes: map[string]*Attribute{ 377 "sensitive": {Sensitive: true}, 378 }, 379 }, 380 true, 381 }, 382 "no sensitive attrs": { 383 &Object{ 384 Attributes: map[string]*Attribute{ 385 "insensitive": {}, 386 }, 387 }, 388 false, 389 }, 390 "nested object contains sensitive": { 391 &Object{ 392 Attributes: map[string]*Attribute{ 393 "nested": { 394 NestedType: &Object{ 395 Nesting: NestingSingle, 396 Attributes: map[string]*Attribute{ 397 "sensitive": {Sensitive: true}, 398 }, 399 }, 400 }, 401 }, 402 }, 403 true, 404 }, 405 "nested obj, no sensitive attrs": { 406 &Object{ 407 Attributes: map[string]*Attribute{ 408 "nested": { 409 NestedType: &Object{ 410 Nesting: NestingSingle, 411 Attributes: map[string]*Attribute{ 412 "public": {}, 413 }, 414 }, 415 }, 416 }, 417 }, 418 false, 419 }, 420 "several nested objects, one contains sensitive": { 421 &Object{ 422 Attributes: map[string]*Attribute{ 423 "alpha": { 424 NestedType: &Object{ 425 Nesting: NestingSingle, 426 Attributes: map[string]*Attribute{ 427 "nonsensitive": {}, 428 }, 429 }, 430 }, 431 "beta": { 432 NestedType: &Object{ 433 Nesting: NestingSingle, 434 Attributes: map[string]*Attribute{ 435 "sensitive": {Sensitive: true}, 436 }, 437 }, 438 }, 439 "gamma": { 440 NestedType: &Object{ 441 Nesting: NestingSingle, 442 Attributes: map[string]*Attribute{ 443 "nonsensitive": {}, 444 }, 445 }, 446 }, 447 }, 448 }, 449 true, 450 }, 451 } 452 453 for name, test := range tests { 454 t.Run(name, func(t *testing.T) { 455 got := test.Schema.ContainsSensitive() 456 if got != test.Want { 457 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 458 } 459 }) 460 } 461 462 } 463 464 // Nested attribute should return optional object attributes for decoding. 465 func TestObjectSpecType(t *testing.T) { 466 tests := map[string]struct { 467 Schema *Object 468 Want cty.Type 469 }{ 470 "attributes": { 471 &Object{ 472 Nesting: NestingSingle, 473 Attributes: map[string]*Attribute{ 474 "optional": {Type: cty.String, Optional: true}, 475 "required": {Type: cty.Number, Required: true}, 476 "computed": {Type: cty.List(cty.Bool), Computed: true}, 477 "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, 478 }, 479 }, 480 cty.ObjectWithOptionalAttrs( 481 map[string]cty.Type{ 482 "optional": cty.String, 483 "required": cty.Number, 484 "computed": cty.List(cty.Bool), 485 "optional_computed": cty.Map(cty.Bool), 486 }, 487 []string{"optional", "computed", "optional_computed"}, 488 ), 489 }, 490 "nested attributes": { 491 &Object{ 492 Nesting: NestingSingle, 493 Attributes: map[string]*Attribute{ 494 "nested_type": { 495 NestedType: &Object{ 496 Nesting: NestingSingle, 497 Attributes: map[string]*Attribute{ 498 "optional": {Type: cty.String, Optional: true}, 499 "required": {Type: cty.Number, Required: true}, 500 "computed": {Type: cty.List(cty.Bool), Computed: true}, 501 "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, 502 }, 503 }, 504 Optional: true, 505 }, 506 }, 507 }, 508 cty.ObjectWithOptionalAttrs(map[string]cty.Type{ 509 "nested_type": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ 510 "optional": cty.String, 511 "required": cty.Number, 512 "computed": cty.List(cty.Bool), 513 "optional_computed": cty.Map(cty.Bool), 514 }, []string{"optional", "computed", "optional_computed"}), 515 }, []string{"nested_type"}), 516 }, 517 "nested object-type attributes": { 518 &Object{ 519 Nesting: NestingSingle, 520 Attributes: map[string]*Attribute{ 521 "nested_type": { 522 NestedType: &Object{ 523 Nesting: NestingSingle, 524 Attributes: map[string]*Attribute{ 525 "optional": {Type: cty.String, Optional: true}, 526 "required": {Type: cty.Number, Required: true}, 527 "computed": {Type: cty.List(cty.Bool), Computed: true}, 528 "optional_computed": {Type: cty.Map(cty.Bool), Optional: true, Computed: true}, 529 "object": { 530 Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ 531 "optional": cty.String, 532 "required": cty.Number, 533 }, []string{"optional"}), 534 }, 535 }, 536 }, 537 Optional: true, 538 }, 539 }, 540 }, 541 cty.ObjectWithOptionalAttrs(map[string]cty.Type{ 542 "nested_type": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ 543 "optional": cty.String, 544 "required": cty.Number, 545 "computed": cty.List(cty.Bool), 546 "optional_computed": cty.Map(cty.Bool), 547 "object": cty.ObjectWithOptionalAttrs(map[string]cty.Type{"optional": cty.String, "required": cty.Number}, []string{"optional"}), 548 }, []string{"optional", "computed", "optional_computed"}), 549 }, []string{"nested_type"}), 550 }, 551 "NestingList": { 552 &Object{ 553 Nesting: NestingList, 554 Attributes: map[string]*Attribute{ 555 "foo": {Type: cty.String, Optional: true}, 556 }, 557 }, 558 cty.List(cty.ObjectWithOptionalAttrs(map[string]cty.Type{"foo": cty.String}, []string{"foo"})), 559 }, 560 "NestingMap": { 561 &Object{ 562 Nesting: NestingMap, 563 Attributes: map[string]*Attribute{ 564 "foo": {Type: cty.String}, 565 }, 566 }, 567 cty.Map(cty.Object(map[string]cty.Type{"foo": cty.String})), 568 }, 569 "NestingSet": { 570 &Object{ 571 Nesting: NestingSet, 572 Attributes: map[string]*Attribute{ 573 "foo": {Type: cty.String}, 574 }, 575 }, 576 cty.Set(cty.Object(map[string]cty.Type{"foo": cty.String})), 577 }, 578 "deeply nested NestingList": { 579 &Object{ 580 Nesting: NestingList, 581 Attributes: map[string]*Attribute{ 582 "foo": { 583 NestedType: &Object{ 584 Nesting: NestingList, 585 Attributes: map[string]*Attribute{ 586 "bar": {Type: cty.String}, 587 }, 588 }, 589 }, 590 }, 591 }, 592 cty.List(cty.Object(map[string]cty.Type{"foo": cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))})), 593 }, 594 } 595 596 for name, test := range tests { 597 t.Run(name, func(t *testing.T) { 598 got := test.Schema.specType() 599 if !got.Equals(test.Want) { 600 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 601 } 602 }) 603 } 604 }