github.com/kevinklinger/open_terraform@v1.3.6/noninternal/plans/objchange/objchange_test.go (about) 1 package objchange 2 3 import ( 4 "testing" 5 6 "github.com/apparentlymart/go-dump/dump" 7 "github.com/zclconf/go-cty/cty" 8 9 "github.com/kevinklinger/open_terraform/noninternal/configs/configschema" 10 ) 11 12 func TestProposedNew(t *testing.T) { 13 tests := map[string]struct { 14 Schema *configschema.Block 15 Prior cty.Value 16 Config cty.Value 17 Want cty.Value 18 }{ 19 "empty": { 20 &configschema.Block{}, 21 cty.EmptyObjectVal, 22 cty.EmptyObjectVal, 23 cty.EmptyObjectVal, 24 }, 25 "no prior": { 26 &configschema.Block{ 27 Attributes: map[string]*configschema.Attribute{ 28 "foo": { 29 Type: cty.String, 30 Optional: true, 31 }, 32 "bar": { 33 Type: cty.String, 34 Computed: true, 35 }, 36 "bloop": { 37 NestedType: &configschema.Object{ 38 Nesting: configschema.NestingSingle, 39 Attributes: map[string]*configschema.Attribute{ 40 "blop": { 41 Type: cty.String, 42 Required: true, 43 }, 44 }, 45 }, 46 Computed: true, 47 }, 48 }, 49 BlockTypes: map[string]*configschema.NestedBlock{ 50 "baz": { 51 Nesting: configschema.NestingSingle, 52 Block: configschema.Block{ 53 Attributes: map[string]*configschema.Attribute{ 54 "boz": { 55 Type: cty.String, 56 Optional: true, 57 Computed: true, 58 }, 59 "biz": { 60 Type: cty.String, 61 Optional: true, 62 Computed: true, 63 }, 64 }, 65 }, 66 }, 67 }, 68 }, 69 cty.NullVal(cty.DynamicPseudoType), 70 cty.ObjectVal(map[string]cty.Value{ 71 "foo": cty.StringVal("hello"), 72 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 73 "blop": cty.String, 74 })), 75 "bar": cty.NullVal(cty.String), 76 "baz": cty.ObjectVal(map[string]cty.Value{ 77 "boz": cty.StringVal("world"), 78 79 // An unknown in the config represents a situation where 80 // an argument is explicitly set to an expression result 81 // that is derived from an unknown value. This is distinct 82 // from leaving it null, which allows the provider itself 83 // to decide the value during PlanResourceChange. 84 "biz": cty.UnknownVal(cty.String), 85 }), 86 }), 87 cty.ObjectVal(map[string]cty.Value{ 88 "foo": cty.StringVal("hello"), 89 90 // unset computed attributes are null in the proposal; provider 91 // usually changes them to "unknown" during PlanResourceChange, 92 // to indicate that the value will be decided during apply. 93 "bar": cty.NullVal(cty.String), 94 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 95 "blop": cty.String, 96 })), 97 98 "baz": cty.ObjectVal(map[string]cty.Value{ 99 "boz": cty.StringVal("world"), 100 "biz": cty.UnknownVal(cty.String), // explicit unknown preserved from config 101 }), 102 }), 103 }, 104 "null block remains null": { 105 &configschema.Block{ 106 Attributes: map[string]*configschema.Attribute{ 107 "foo": { 108 Type: cty.String, 109 Optional: true, 110 }, 111 "bloop": { 112 NestedType: &configschema.Object{ 113 Nesting: configschema.NestingSingle, 114 Attributes: map[string]*configschema.Attribute{ 115 "blop": { 116 Type: cty.String, 117 Required: true, 118 }, 119 }, 120 }, 121 Computed: true, 122 }, 123 }, 124 BlockTypes: map[string]*configschema.NestedBlock{ 125 "baz": { 126 Nesting: configschema.NestingSingle, 127 Block: configschema.Block{ 128 Attributes: map[string]*configschema.Attribute{ 129 "boz": { 130 Type: cty.String, 131 Optional: true, 132 Computed: true, 133 }, 134 }, 135 }, 136 }, 137 }, 138 }, 139 cty.NullVal(cty.DynamicPseudoType), 140 cty.ObjectVal(map[string]cty.Value{ 141 "foo": cty.StringVal("bar"), 142 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 143 "blop": cty.String, 144 })), 145 "baz": cty.NullVal(cty.Object(map[string]cty.Type{ 146 "boz": cty.String, 147 })), 148 }), 149 // The bloop attribue and baz block does not exist in the config, 150 // and therefore shouldn't be planned. 151 cty.ObjectVal(map[string]cty.Value{ 152 "foo": cty.StringVal("bar"), 153 "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ 154 "blop": cty.String, 155 })), 156 "baz": cty.NullVal(cty.Object(map[string]cty.Type{ 157 "boz": cty.String, 158 })), 159 }), 160 }, 161 "no prior with set": { 162 // This one is here because our handling of sets is more complex 163 // than others (due to the fuzzy correlation heuristic) and 164 // historically that caused us some panic-related grief. 165 &configschema.Block{ 166 BlockTypes: map[string]*configschema.NestedBlock{ 167 "baz": { 168 Nesting: configschema.NestingSet, 169 Block: configschema.Block{ 170 Attributes: map[string]*configschema.Attribute{ 171 "boz": { 172 Type: cty.String, 173 Optional: true, 174 Computed: true, 175 }, 176 }, 177 }, 178 }, 179 }, 180 Attributes: map[string]*configschema.Attribute{ 181 "bloop": { 182 NestedType: &configschema.Object{ 183 Nesting: configschema.NestingSet, 184 Attributes: map[string]*configschema.Attribute{ 185 "blop": { 186 Type: cty.String, 187 Required: true, 188 }, 189 }, 190 }, 191 Computed: true, 192 Optional: true, 193 }, 194 }, 195 }, 196 cty.NullVal(cty.DynamicPseudoType), 197 cty.ObjectVal(map[string]cty.Value{ 198 "baz": cty.SetVal([]cty.Value{ 199 cty.ObjectVal(map[string]cty.Value{ 200 "boz": cty.StringVal("world"), 201 }), 202 }), 203 "bloop": cty.SetVal([]cty.Value{ 204 cty.ObjectVal(map[string]cty.Value{ 205 "blop": cty.StringVal("blub"), 206 }), 207 }), 208 }), 209 cty.ObjectVal(map[string]cty.Value{ 210 "baz": cty.SetVal([]cty.Value{ 211 cty.ObjectVal(map[string]cty.Value{ 212 "boz": cty.StringVal("world"), 213 }), 214 }), 215 "bloop": cty.SetVal([]cty.Value{ 216 cty.ObjectVal(map[string]cty.Value{ 217 "blop": cty.StringVal("blub"), 218 }), 219 }), 220 }), 221 }, 222 "prior attributes": { 223 &configschema.Block{ 224 Attributes: map[string]*configschema.Attribute{ 225 "foo": { 226 Type: cty.String, 227 Optional: true, 228 }, 229 "bar": { 230 Type: cty.String, 231 Computed: true, 232 }, 233 "baz": { 234 Type: cty.String, 235 Optional: true, 236 Computed: true, 237 }, 238 "boz": { 239 Type: cty.String, 240 Optional: true, 241 Computed: true, 242 }, 243 "bloop": { 244 NestedType: &configschema.Object{ 245 Nesting: configschema.NestingSingle, 246 Attributes: map[string]*configschema.Attribute{ 247 "blop": { 248 Type: cty.String, 249 Required: true, 250 }, 251 }, 252 }, 253 Optional: true, 254 }, 255 }, 256 }, 257 cty.ObjectVal(map[string]cty.Value{ 258 "foo": cty.StringVal("bonjour"), 259 "bar": cty.StringVal("petit dejeuner"), 260 "baz": cty.StringVal("grande dejeuner"), 261 "boz": cty.StringVal("a la monde"), 262 "bloop": cty.ObjectVal(map[string]cty.Value{ 263 "blop": cty.StringVal("glub"), 264 }), 265 }), 266 cty.ObjectVal(map[string]cty.Value{ 267 "foo": cty.StringVal("hello"), 268 "bar": cty.NullVal(cty.String), 269 "baz": cty.NullVal(cty.String), 270 "boz": cty.StringVal("world"), 271 "bloop": cty.ObjectVal(map[string]cty.Value{ 272 "blop": cty.StringVal("bleep"), 273 }), 274 }), 275 cty.ObjectVal(map[string]cty.Value{ 276 "foo": cty.StringVal("hello"), 277 "bar": cty.StringVal("petit dejeuner"), 278 "baz": cty.StringVal("grande dejeuner"), 279 "boz": cty.StringVal("world"), 280 "bloop": cty.ObjectVal(map[string]cty.Value{ 281 "blop": cty.StringVal("bleep"), 282 }), 283 }), 284 }, 285 "prior nested single": { 286 &configschema.Block{ 287 BlockTypes: map[string]*configschema.NestedBlock{ 288 "foo": { 289 Nesting: configschema.NestingSingle, 290 Block: configschema.Block{ 291 Attributes: map[string]*configschema.Attribute{ 292 "bar": { 293 Type: cty.String, 294 Optional: true, 295 Computed: true, 296 }, 297 "baz": { 298 Type: cty.String, 299 Optional: true, 300 Computed: true, 301 }, 302 }, 303 }, 304 }, 305 }, 306 Attributes: map[string]*configschema.Attribute{ 307 "bloop": { 308 NestedType: &configschema.Object{ 309 Nesting: configschema.NestingSingle, 310 Attributes: map[string]*configschema.Attribute{ 311 "blop": { 312 Type: cty.String, 313 Required: true, 314 }, 315 "bleep": { 316 Type: cty.String, 317 Optional: true, 318 }, 319 }, 320 }, 321 Optional: true, 322 }, 323 }, 324 }, 325 cty.ObjectVal(map[string]cty.Value{ 326 "foo": cty.ObjectVal(map[string]cty.Value{ 327 "bar": cty.StringVal("beep"), 328 "baz": cty.StringVal("boop"), 329 }), 330 "bloop": cty.ObjectVal(map[string]cty.Value{ 331 "blop": cty.StringVal("glub"), 332 "bleep": cty.NullVal(cty.String), 333 }), 334 }), 335 cty.ObjectVal(map[string]cty.Value{ 336 "foo": cty.ObjectVal(map[string]cty.Value{ 337 "bar": cty.StringVal("bap"), 338 "baz": cty.NullVal(cty.String), 339 }), 340 "bloop": cty.ObjectVal(map[string]cty.Value{ 341 "blop": cty.StringVal("glub"), 342 "bleep": cty.StringVal("beep"), 343 }), 344 }), 345 cty.ObjectVal(map[string]cty.Value{ 346 "foo": cty.ObjectVal(map[string]cty.Value{ 347 "bar": cty.StringVal("bap"), 348 "baz": cty.StringVal("boop"), 349 }), 350 "bloop": cty.ObjectVal(map[string]cty.Value{ 351 "blop": cty.StringVal("glub"), 352 "bleep": cty.StringVal("beep"), 353 }), 354 }), 355 }, 356 "prior nested list": { 357 &configschema.Block{ 358 BlockTypes: map[string]*configschema.NestedBlock{ 359 "foo": { 360 Nesting: configschema.NestingList, 361 Block: configschema.Block{ 362 Attributes: map[string]*configschema.Attribute{ 363 "bar": { 364 Type: cty.String, 365 Optional: true, 366 Computed: true, 367 }, 368 "baz": { 369 Type: cty.String, 370 Optional: true, 371 Computed: true, 372 }, 373 }, 374 }, 375 }, 376 }, 377 Attributes: map[string]*configschema.Attribute{ 378 "bloop": { 379 NestedType: &configschema.Object{ 380 Nesting: configschema.NestingList, 381 Attributes: map[string]*configschema.Attribute{ 382 "blop": { 383 Type: cty.String, 384 Required: true, 385 }, 386 }, 387 }, 388 Optional: true, 389 }, 390 }, 391 }, 392 cty.ObjectVal(map[string]cty.Value{ 393 "foo": cty.ListVal([]cty.Value{ 394 cty.ObjectVal(map[string]cty.Value{ 395 "bar": cty.StringVal("beep"), 396 "baz": cty.StringVal("boop"), 397 }), 398 }), 399 "bloop": cty.ListVal([]cty.Value{ 400 cty.ObjectVal(map[string]cty.Value{ 401 "blop": cty.StringVal("bar"), 402 }), 403 cty.ObjectVal(map[string]cty.Value{ 404 "blop": cty.StringVal("baz"), 405 }), 406 }), 407 }), 408 cty.ObjectVal(map[string]cty.Value{ 409 "foo": cty.ListVal([]cty.Value{ 410 cty.ObjectVal(map[string]cty.Value{ 411 "bar": cty.StringVal("bap"), 412 "baz": cty.NullVal(cty.String), 413 }), 414 cty.ObjectVal(map[string]cty.Value{ 415 "bar": cty.StringVal("blep"), 416 "baz": cty.NullVal(cty.String), 417 }), 418 }), 419 "bloop": cty.ListVal([]cty.Value{ 420 cty.ObjectVal(map[string]cty.Value{ 421 "blop": cty.StringVal("bar"), 422 }), 423 cty.ObjectVal(map[string]cty.Value{ 424 "blop": cty.StringVal("baz"), 425 }), 426 }), 427 }), 428 cty.ObjectVal(map[string]cty.Value{ 429 "foo": cty.ListVal([]cty.Value{ 430 cty.ObjectVal(map[string]cty.Value{ 431 "bar": cty.StringVal("bap"), 432 "baz": cty.StringVal("boop"), 433 }), 434 cty.ObjectVal(map[string]cty.Value{ 435 "bar": cty.StringVal("blep"), 436 "baz": cty.NullVal(cty.String), 437 }), 438 }), 439 "bloop": cty.ListVal([]cty.Value{ 440 cty.ObjectVal(map[string]cty.Value{ 441 "blop": cty.StringVal("bar"), 442 }), 443 cty.ObjectVal(map[string]cty.Value{ 444 "blop": cty.StringVal("baz"), 445 }), 446 }), 447 }), 448 }, 449 "prior nested list with dynamic": { 450 &configschema.Block{ 451 BlockTypes: map[string]*configschema.NestedBlock{ 452 "foo": { 453 Nesting: configschema.NestingList, 454 Block: configschema.Block{ 455 Attributes: map[string]*configschema.Attribute{ 456 "bar": { 457 Type: cty.String, 458 Optional: true, 459 Computed: true, 460 }, 461 "baz": { 462 Type: cty.DynamicPseudoType, 463 Optional: true, 464 Computed: true, 465 }, 466 }, 467 }, 468 }, 469 }, 470 Attributes: map[string]*configschema.Attribute{ 471 "bloop": { 472 NestedType: &configschema.Object{ 473 Nesting: configschema.NestingList, 474 Attributes: map[string]*configschema.Attribute{ 475 "blop": { 476 Type: cty.DynamicPseudoType, 477 Required: true, 478 }, 479 "blub": { 480 Type: cty.DynamicPseudoType, 481 Optional: true, 482 }, 483 }, 484 }, 485 Optional: true, 486 }, 487 }, 488 }, 489 cty.ObjectVal(map[string]cty.Value{ 490 "foo": cty.TupleVal([]cty.Value{ 491 cty.ObjectVal(map[string]cty.Value{ 492 "bar": cty.StringVal("beep"), 493 "baz": cty.StringVal("boop"), 494 }), 495 }), 496 "bloop": cty.ListVal([]cty.Value{ 497 cty.ObjectVal(map[string]cty.Value{ 498 "blop": cty.StringVal("bar"), 499 "blub": cty.StringVal("glub"), 500 }), 501 cty.ObjectVal(map[string]cty.Value{ 502 "blop": cty.StringVal("baz"), 503 "blub": cty.NullVal(cty.String), 504 }), 505 }), 506 }), 507 cty.ObjectVal(map[string]cty.Value{ 508 "foo": cty.TupleVal([]cty.Value{ 509 cty.ObjectVal(map[string]cty.Value{ 510 "bar": cty.StringVal("bap"), 511 "baz": cty.NullVal(cty.String), 512 }), 513 cty.ObjectVal(map[string]cty.Value{ 514 "bar": cty.StringVal("blep"), 515 "baz": cty.NullVal(cty.String), 516 }), 517 }), 518 "bloop": cty.ListVal([]cty.Value{ 519 cty.ObjectVal(map[string]cty.Value{ 520 "blop": cty.StringVal("bar"), 521 "blub": cty.NullVal(cty.String), 522 }), 523 }), 524 }), 525 cty.ObjectVal(map[string]cty.Value{ 526 "foo": cty.TupleVal([]cty.Value{ 527 cty.ObjectVal(map[string]cty.Value{ 528 "bar": cty.StringVal("bap"), 529 "baz": cty.StringVal("boop"), 530 }), 531 cty.ObjectVal(map[string]cty.Value{ 532 "bar": cty.StringVal("blep"), 533 "baz": cty.NullVal(cty.String), 534 }), 535 }), 536 "bloop": cty.ListVal([]cty.Value{ 537 cty.ObjectVal(map[string]cty.Value{ 538 "blop": cty.StringVal("bar"), 539 "blub": cty.NullVal(cty.String), 540 }), 541 }), 542 }), 543 }, 544 "prior nested map": { 545 &configschema.Block{ 546 BlockTypes: map[string]*configschema.NestedBlock{ 547 "foo": { 548 Nesting: configschema.NestingMap, 549 Block: configschema.Block{ 550 Attributes: map[string]*configschema.Attribute{ 551 "bar": { 552 Type: cty.String, 553 Optional: true, 554 Computed: true, 555 }, 556 "baz": { 557 Type: cty.String, 558 Optional: true, 559 Computed: true, 560 }, 561 }, 562 }, 563 }, 564 }, 565 Attributes: map[string]*configschema.Attribute{ 566 "bloop": { 567 NestedType: &configschema.Object{ 568 Nesting: configschema.NestingMap, 569 Attributes: map[string]*configschema.Attribute{ 570 "blop": { 571 Type: cty.String, 572 Required: true, 573 }, 574 }, 575 }, 576 Optional: true, 577 }, 578 }, 579 }, 580 cty.ObjectVal(map[string]cty.Value{ 581 "foo": cty.MapVal(map[string]cty.Value{ 582 "a": cty.ObjectVal(map[string]cty.Value{ 583 "bar": cty.StringVal("beep"), 584 "baz": cty.StringVal("boop"), 585 }), 586 "b": cty.ObjectVal(map[string]cty.Value{ 587 "bar": cty.StringVal("blep"), 588 "baz": cty.StringVal("boot"), 589 }), 590 }), 591 "bloop": cty.MapVal(map[string]cty.Value{ 592 "a": cty.ObjectVal(map[string]cty.Value{ 593 "blop": cty.StringVal("glub"), 594 }), 595 "b": cty.ObjectVal(map[string]cty.Value{ 596 "blop": cty.StringVal("blub"), 597 }), 598 }), 599 }), 600 cty.ObjectVal(map[string]cty.Value{ 601 "foo": cty.MapVal(map[string]cty.Value{ 602 "a": cty.ObjectVal(map[string]cty.Value{ 603 "bar": cty.StringVal("bap"), 604 "baz": cty.NullVal(cty.String), 605 }), 606 "c": cty.ObjectVal(map[string]cty.Value{ 607 "bar": cty.StringVal("bosh"), 608 "baz": cty.NullVal(cty.String), 609 }), 610 }), 611 "bloop": cty.MapVal(map[string]cty.Value{ 612 "a": cty.ObjectVal(map[string]cty.Value{ 613 "blop": cty.StringVal("glub"), 614 }), 615 "c": cty.ObjectVal(map[string]cty.Value{ 616 "blop": cty.StringVal("blub"), 617 }), 618 }), 619 }), 620 cty.ObjectVal(map[string]cty.Value{ 621 "foo": cty.MapVal(map[string]cty.Value{ 622 "a": cty.ObjectVal(map[string]cty.Value{ 623 "bar": cty.StringVal("bap"), 624 "baz": cty.StringVal("boop"), 625 }), 626 "c": cty.ObjectVal(map[string]cty.Value{ 627 "bar": cty.StringVal("bosh"), 628 "baz": cty.NullVal(cty.String), 629 }), 630 }), 631 "bloop": cty.MapVal(map[string]cty.Value{ 632 "a": cty.ObjectVal(map[string]cty.Value{ 633 "blop": cty.StringVal("glub"), 634 }), 635 "c": cty.ObjectVal(map[string]cty.Value{ 636 "blop": cty.StringVal("blub"), 637 }), 638 }), 639 }), 640 }, 641 "prior nested map with dynamic": { 642 &configschema.Block{ 643 BlockTypes: map[string]*configschema.NestedBlock{ 644 "foo": { 645 Nesting: configschema.NestingMap, 646 Block: configschema.Block{ 647 Attributes: map[string]*configschema.Attribute{ 648 "bar": { 649 Type: cty.String, 650 Optional: true, 651 Computed: true, 652 }, 653 "baz": { 654 Type: cty.DynamicPseudoType, 655 Optional: true, 656 Computed: true, 657 }, 658 }, 659 }, 660 }, 661 }, 662 Attributes: map[string]*configschema.Attribute{ 663 "bloop": { 664 NestedType: &configschema.Object{ 665 Nesting: configschema.NestingMap, 666 Attributes: map[string]*configschema.Attribute{ 667 "blop": { 668 Type: cty.DynamicPseudoType, 669 Required: true, 670 }, 671 }, 672 }, 673 Optional: true, 674 }, 675 }, 676 }, 677 cty.ObjectVal(map[string]cty.Value{ 678 "foo": cty.ObjectVal(map[string]cty.Value{ 679 "a": cty.ObjectVal(map[string]cty.Value{ 680 "bar": cty.StringVal("beep"), 681 "baz": cty.StringVal("boop"), 682 }), 683 "b": cty.ObjectVal(map[string]cty.Value{ 684 "bar": cty.StringVal("blep"), 685 "baz": cty.ListVal([]cty.Value{cty.StringVal("boot")}), 686 }), 687 }), 688 "bloop": cty.ObjectVal(map[string]cty.Value{ 689 "a": cty.ObjectVal(map[string]cty.Value{ 690 "blop": cty.StringVal("glub"), 691 }), 692 "b": cty.ObjectVal(map[string]cty.Value{ 693 "blop": cty.NumberIntVal(13), 694 }), 695 }), 696 }), 697 cty.ObjectVal(map[string]cty.Value{ 698 "foo": cty.ObjectVal(map[string]cty.Value{ 699 "a": cty.ObjectVal(map[string]cty.Value{ 700 "bar": cty.StringVal("bap"), 701 "baz": cty.NullVal(cty.String), 702 }), 703 "c": cty.ObjectVal(map[string]cty.Value{ 704 "bar": cty.StringVal("bosh"), 705 "baz": cty.NullVal(cty.List(cty.String)), 706 }), 707 }), 708 "bloop": cty.ObjectVal(map[string]cty.Value{ 709 "a": cty.ObjectVal(map[string]cty.Value{ 710 "blop": cty.StringVal("blep"), 711 }), 712 "c": cty.ObjectVal(map[string]cty.Value{ 713 "blop": cty.NumberIntVal(13), 714 }), 715 }), 716 }), 717 cty.ObjectVal(map[string]cty.Value{ 718 "foo": cty.ObjectVal(map[string]cty.Value{ 719 "a": cty.ObjectVal(map[string]cty.Value{ 720 "bar": cty.StringVal("bap"), 721 "baz": cty.StringVal("boop"), 722 }), 723 "c": cty.ObjectVal(map[string]cty.Value{ 724 "bar": cty.StringVal("bosh"), 725 "baz": cty.NullVal(cty.List(cty.String)), 726 }), 727 }), 728 "bloop": cty.ObjectVal(map[string]cty.Value{ 729 "a": cty.ObjectVal(map[string]cty.Value{ 730 "blop": cty.StringVal("blep"), 731 }), 732 "c": cty.ObjectVal(map[string]cty.Value{ 733 "blop": cty.NumberIntVal(13), 734 }), 735 }), 736 }), 737 }, 738 "prior nested set": { 739 &configschema.Block{ 740 BlockTypes: map[string]*configschema.NestedBlock{ 741 "foo": { 742 Nesting: configschema.NestingSet, 743 Block: configschema.Block{ 744 Attributes: map[string]*configschema.Attribute{ 745 "bar": { 746 // This non-computed attribute will serve 747 // as our matching key for propagating 748 // "baz" from elements in the prior value. 749 Type: cty.String, 750 Optional: true, 751 }, 752 "baz": { 753 Type: cty.String, 754 Optional: true, 755 Computed: true, 756 }, 757 }, 758 }, 759 }, 760 }, 761 Attributes: map[string]*configschema.Attribute{ 762 "bloop": { 763 NestedType: &configschema.Object{ 764 Nesting: configschema.NestingSet, 765 Attributes: map[string]*configschema.Attribute{ 766 "blop": { 767 Type: cty.String, 768 Required: true, 769 }, 770 "bleep": { 771 Type: cty.String, 772 Optional: true, 773 }, 774 }, 775 }, 776 Optional: true, 777 }, 778 }, 779 }, 780 cty.ObjectVal(map[string]cty.Value{ 781 "foo": cty.SetVal([]cty.Value{ 782 cty.ObjectVal(map[string]cty.Value{ 783 "bar": cty.StringVal("beep"), 784 "baz": cty.StringVal("boop"), 785 }), 786 cty.ObjectVal(map[string]cty.Value{ 787 "bar": cty.StringVal("blep"), 788 "baz": cty.StringVal("boot"), 789 }), 790 }), 791 "bloop": cty.SetVal([]cty.Value{ 792 cty.ObjectVal(map[string]cty.Value{ 793 "blop": cty.StringVal("glubglub"), 794 "bleep": cty.NullVal(cty.String), 795 }), 796 cty.ObjectVal(map[string]cty.Value{ 797 "blop": cty.StringVal("glubglub"), 798 "bleep": cty.StringVal("beep"), 799 }), 800 }), 801 }), 802 cty.ObjectVal(map[string]cty.Value{ 803 "foo": cty.SetVal([]cty.Value{ 804 cty.ObjectVal(map[string]cty.Value{ 805 "bar": cty.StringVal("beep"), 806 "baz": cty.NullVal(cty.String), 807 }), 808 cty.ObjectVal(map[string]cty.Value{ 809 "bar": cty.StringVal("bosh"), 810 "baz": cty.NullVal(cty.String), 811 }), 812 }), 813 "bloop": cty.SetVal([]cty.Value{ 814 cty.ObjectVal(map[string]cty.Value{ 815 "blop": cty.StringVal("glubglub"), 816 "bleep": cty.NullVal(cty.String), 817 }), 818 cty.ObjectVal(map[string]cty.Value{ 819 "blop": cty.StringVal("glub"), 820 "bleep": cty.NullVal(cty.String), 821 }), 822 }), 823 }), 824 cty.ObjectVal(map[string]cty.Value{ 825 "foo": cty.SetVal([]cty.Value{ 826 cty.ObjectVal(map[string]cty.Value{ 827 "bar": cty.StringVal("beep"), 828 "baz": cty.StringVal("boop"), 829 }), 830 cty.ObjectVal(map[string]cty.Value{ 831 "bar": cty.StringVal("bosh"), 832 "baz": cty.NullVal(cty.String), 833 }), 834 }), 835 "bloop": cty.SetVal([]cty.Value{ 836 cty.ObjectVal(map[string]cty.Value{ 837 "blop": cty.StringVal("glubglub"), 838 "bleep": cty.NullVal(cty.String), 839 }), 840 cty.ObjectVal(map[string]cty.Value{ 841 "blop": cty.StringVal("glub"), 842 "bleep": cty.NullVal(cty.String), 843 }), 844 }), 845 }), 846 }, 847 "sets differing only by unknown": { 848 &configschema.Block{ 849 BlockTypes: map[string]*configschema.NestedBlock{ 850 "multi": { 851 Nesting: configschema.NestingSet, 852 Block: configschema.Block{ 853 Attributes: map[string]*configschema.Attribute{ 854 "optional": { 855 Type: cty.String, 856 Optional: true, 857 Computed: true, 858 }, 859 }, 860 }, 861 }, 862 }, 863 Attributes: map[string]*configschema.Attribute{ 864 "bloop": { 865 NestedType: &configschema.Object{ 866 Nesting: configschema.NestingSet, 867 Attributes: map[string]*configschema.Attribute{ 868 "blop": { 869 Type: cty.String, 870 Required: true, 871 }, 872 }, 873 }, 874 Optional: true, 875 }, 876 }, 877 }, 878 cty.NullVal(cty.DynamicPseudoType), 879 cty.ObjectVal(map[string]cty.Value{ 880 "multi": cty.SetVal([]cty.Value{ 881 cty.ObjectVal(map[string]cty.Value{ 882 "optional": cty.UnknownVal(cty.String), 883 }), 884 cty.ObjectVal(map[string]cty.Value{ 885 "optional": cty.UnknownVal(cty.String), 886 }), 887 }), 888 "bloop": cty.SetVal([]cty.Value{ 889 cty.ObjectVal(map[string]cty.Value{ 890 "blop": cty.UnknownVal(cty.String), 891 }), 892 cty.ObjectVal(map[string]cty.Value{ 893 "blop": cty.UnknownVal(cty.String), 894 }), 895 }), 896 }), 897 cty.ObjectVal(map[string]cty.Value{ 898 "multi": cty.SetVal([]cty.Value{ 899 // These remain distinct because unknown values never 900 // compare equal. They may be consolidated together once 901 // the values become known, though. 902 cty.ObjectVal(map[string]cty.Value{ 903 "optional": cty.UnknownVal(cty.String), 904 }), 905 cty.ObjectVal(map[string]cty.Value{ 906 "optional": cty.UnknownVal(cty.String), 907 }), 908 }), 909 "bloop": cty.SetVal([]cty.Value{ 910 cty.ObjectVal(map[string]cty.Value{ 911 "blop": cty.UnknownVal(cty.String), 912 }), 913 cty.ObjectVal(map[string]cty.Value{ 914 "blop": cty.UnknownVal(cty.String), 915 }), 916 }), 917 }), 918 }, 919 "nested list in set": { 920 &configschema.Block{ 921 BlockTypes: map[string]*configschema.NestedBlock{ 922 "foo": { 923 Nesting: configschema.NestingSet, 924 Block: configschema.Block{ 925 BlockTypes: map[string]*configschema.NestedBlock{ 926 "bar": { 927 Nesting: configschema.NestingList, 928 Block: configschema.Block{ 929 Attributes: map[string]*configschema.Attribute{ 930 "baz": { 931 Type: cty.String, 932 }, 933 "qux": { 934 Type: cty.String, 935 Computed: true, 936 Optional: true, 937 }, 938 }, 939 }, 940 }, 941 }, 942 }, 943 }, 944 }, 945 }, 946 cty.ObjectVal(map[string]cty.Value{ 947 "foo": cty.SetVal([]cty.Value{ 948 cty.ObjectVal(map[string]cty.Value{ 949 "bar": cty.ListVal([]cty.Value{ 950 cty.ObjectVal(map[string]cty.Value{ 951 "baz": cty.StringVal("beep"), 952 "qux": cty.StringVal("boop"), 953 }), 954 }), 955 }), 956 }), 957 }), 958 cty.ObjectVal(map[string]cty.Value{ 959 "foo": cty.SetVal([]cty.Value{ 960 cty.ObjectVal(map[string]cty.Value{ 961 "bar": cty.ListVal([]cty.Value{ 962 cty.ObjectVal(map[string]cty.Value{ 963 "baz": cty.StringVal("beep"), 964 "qux": cty.NullVal(cty.String), 965 }), 966 }), 967 }), 968 }), 969 }), 970 cty.ObjectVal(map[string]cty.Value{ 971 "foo": cty.SetVal([]cty.Value{ 972 cty.ObjectVal(map[string]cty.Value{ 973 "bar": cty.ListVal([]cty.Value{ 974 cty.ObjectVal(map[string]cty.Value{ 975 "baz": cty.StringVal("beep"), 976 "qux": cty.StringVal("boop"), 977 }), 978 }), 979 }), 980 }), 981 }), 982 }, 983 "empty nested list in set": { 984 &configschema.Block{ 985 BlockTypes: map[string]*configschema.NestedBlock{ 986 "foo": { 987 Nesting: configschema.NestingSet, 988 Block: configschema.Block{ 989 BlockTypes: map[string]*configschema.NestedBlock{ 990 "bar": { 991 Nesting: configschema.NestingList, 992 Block: configschema.Block{}, 993 }, 994 }, 995 }, 996 }, 997 }, 998 }, 999 cty.ObjectVal(map[string]cty.Value{ 1000 "foo": cty.SetVal([]cty.Value{ 1001 cty.ObjectVal(map[string]cty.Value{ 1002 "bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()), 1003 }), 1004 }), 1005 }), 1006 cty.ObjectVal(map[string]cty.Value{ 1007 "foo": cty.SetVal([]cty.Value{ 1008 cty.ObjectVal(map[string]cty.Value{ 1009 "bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()), 1010 }), 1011 }), 1012 }), 1013 cty.ObjectVal(map[string]cty.Value{ 1014 "foo": cty.SetVal([]cty.Value{ 1015 cty.ObjectVal(map[string]cty.Value{ 1016 "bar": cty.ListValEmpty((&configschema.Block{}).ImpliedType()), 1017 }), 1018 }), 1019 }), 1020 }, 1021 "nested list with dynamic in set": { 1022 &configschema.Block{ 1023 BlockTypes: map[string]*configschema.NestedBlock{ 1024 "foo": { 1025 Nesting: configschema.NestingSet, 1026 Block: configschema.Block{ 1027 BlockTypes: map[string]*configschema.NestedBlock{ 1028 "bar": { 1029 Nesting: configschema.NestingList, 1030 Block: configschema.Block{ 1031 Attributes: map[string]*configschema.Attribute{ 1032 "baz": { 1033 Type: cty.DynamicPseudoType, 1034 }, 1035 }, 1036 }, 1037 }, 1038 }, 1039 }, 1040 }, 1041 }, 1042 }, 1043 cty.ObjectVal(map[string]cty.Value{ 1044 "foo": cty.SetVal([]cty.Value{ 1045 cty.ObjectVal(map[string]cty.Value{ 1046 "bar": cty.TupleVal([]cty.Value{ 1047 cty.ObjectVal(map[string]cty.Value{ 1048 "baz": cty.StringVal("true"), 1049 }), 1050 cty.ObjectVal(map[string]cty.Value{ 1051 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1052 }), 1053 }), 1054 }), 1055 }), 1056 }), 1057 cty.ObjectVal(map[string]cty.Value{ 1058 "foo": cty.SetVal([]cty.Value{ 1059 cty.ObjectVal(map[string]cty.Value{ 1060 "bar": cty.TupleVal([]cty.Value{ 1061 cty.ObjectVal(map[string]cty.Value{ 1062 "baz": cty.StringVal("true"), 1063 }), 1064 cty.ObjectVal(map[string]cty.Value{ 1065 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1066 }), 1067 }), 1068 }), 1069 }), 1070 }), 1071 cty.ObjectVal(map[string]cty.Value{ 1072 "foo": cty.SetVal([]cty.Value{ 1073 cty.ObjectVal(map[string]cty.Value{ 1074 "bar": cty.TupleVal([]cty.Value{ 1075 cty.ObjectVal(map[string]cty.Value{ 1076 "baz": cty.StringVal("true"), 1077 }), 1078 cty.ObjectVal(map[string]cty.Value{ 1079 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1080 }), 1081 }), 1082 }), 1083 }), 1084 }), 1085 }, 1086 "nested map with dynamic in set": { 1087 &configschema.Block{ 1088 BlockTypes: map[string]*configschema.NestedBlock{ 1089 "foo": { 1090 Nesting: configschema.NestingSet, 1091 Block: configschema.Block{ 1092 BlockTypes: map[string]*configschema.NestedBlock{ 1093 "bar": { 1094 Nesting: configschema.NestingMap, 1095 Block: configschema.Block{ 1096 Attributes: map[string]*configschema.Attribute{ 1097 "baz": { 1098 Type: cty.DynamicPseudoType, 1099 Optional: true, 1100 }, 1101 }, 1102 }, 1103 }, 1104 }, 1105 }, 1106 }, 1107 }, 1108 }, 1109 cty.ObjectVal(map[string]cty.Value{ 1110 "foo": cty.SetVal([]cty.Value{ 1111 cty.ObjectVal(map[string]cty.Value{ 1112 "bar": cty.ObjectVal(map[string]cty.Value{ 1113 "bing": cty.ObjectVal(map[string]cty.Value{ 1114 "baz": cty.StringVal("true"), 1115 }), 1116 "bang": cty.ObjectVal(map[string]cty.Value{ 1117 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1118 }), 1119 }), 1120 }), 1121 }), 1122 }), 1123 cty.ObjectVal(map[string]cty.Value{ 1124 "foo": cty.SetVal([]cty.Value{ 1125 cty.ObjectVal(map[string]cty.Value{ 1126 "bar": cty.ObjectVal(map[string]cty.Value{ 1127 "bing": cty.ObjectVal(map[string]cty.Value{ 1128 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1129 }), 1130 }), 1131 }), 1132 }), 1133 }), 1134 cty.ObjectVal(map[string]cty.Value{ 1135 "foo": cty.SetVal([]cty.Value{ 1136 cty.ObjectVal(map[string]cty.Value{ 1137 "bar": cty.ObjectVal(map[string]cty.Value{ 1138 "bing": cty.ObjectVal(map[string]cty.Value{ 1139 "baz": cty.ListVal([]cty.Value{cty.StringVal("true")}), 1140 }), 1141 }), 1142 }), 1143 }), 1144 }), 1145 }, 1146 "empty nested map in set": { 1147 &configschema.Block{ 1148 BlockTypes: map[string]*configschema.NestedBlock{ 1149 "foo": { 1150 Nesting: configschema.NestingSet, 1151 Block: configschema.Block{ 1152 BlockTypes: map[string]*configschema.NestedBlock{ 1153 "bar": { 1154 Nesting: configschema.NestingMap, 1155 Block: configschema.Block{ 1156 Attributes: map[string]*configschema.Attribute{ 1157 "baz": { 1158 Type: cty.String, 1159 Optional: true, 1160 }, 1161 }, 1162 }, 1163 }, 1164 }, 1165 }, 1166 }, 1167 }, 1168 }, 1169 cty.ObjectVal(map[string]cty.Value{ 1170 "foo": cty.SetVal([]cty.Value{ 1171 cty.ObjectVal(map[string]cty.Value{ 1172 "bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 1173 "baz": cty.String, 1174 })), 1175 }), 1176 }), 1177 }), 1178 cty.ObjectVal(map[string]cty.Value{ 1179 "foo": cty.SetVal([]cty.Value{ 1180 cty.ObjectVal(map[string]cty.Value{ 1181 "bar": cty.MapVal(map[string]cty.Value{ 1182 "bing": cty.ObjectVal(map[string]cty.Value{ 1183 "baz": cty.StringVal("true"), 1184 }), 1185 }), 1186 }), 1187 }), 1188 }), 1189 cty.ObjectVal(map[string]cty.Value{ 1190 "foo": cty.SetVal([]cty.Value{ 1191 cty.ObjectVal(map[string]cty.Value{ 1192 "bar": cty.MapVal(map[string]cty.Value{ 1193 "bing": cty.ObjectVal(map[string]cty.Value{ 1194 "baz": cty.StringVal("true"), 1195 }), 1196 }), 1197 }), 1198 }), 1199 }), 1200 }, 1201 // This example has a mixture of optional, computed and required in a deeply-nested NestedType attribute 1202 "deeply NestedType": { 1203 &configschema.Block{ 1204 Attributes: map[string]*configschema.Attribute{ 1205 "foo": { 1206 NestedType: &configschema.Object{ 1207 Nesting: configschema.NestingSingle, 1208 Attributes: map[string]*configschema.Attribute{ 1209 "bar": { 1210 NestedType: &configschema.Object{ 1211 Nesting: configschema.NestingSingle, 1212 Attributes: testAttributes, 1213 }, 1214 Required: true, 1215 }, 1216 "baz": { 1217 NestedType: &configschema.Object{ 1218 Nesting: configschema.NestingSingle, 1219 Attributes: testAttributes, 1220 }, 1221 Optional: true, 1222 }, 1223 }, 1224 }, 1225 Optional: true, 1226 }, 1227 }, 1228 }, 1229 // prior 1230 cty.ObjectVal(map[string]cty.Value{ 1231 "foo": cty.ObjectVal(map[string]cty.Value{ 1232 "bar": cty.NullVal(cty.DynamicPseudoType), 1233 "baz": cty.ObjectVal(map[string]cty.Value{ 1234 "optional": cty.NullVal(cty.String), 1235 "computed": cty.StringVal("hello"), 1236 "optional_computed": cty.StringVal("prior"), 1237 "required": cty.StringVal("present"), 1238 }), 1239 }), 1240 }), 1241 // config 1242 cty.ObjectVal(map[string]cty.Value{ 1243 "foo": cty.ObjectVal(map[string]cty.Value{ 1244 "bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown from the config 1245 "optional": cty.String, 1246 "computed": cty.String, 1247 "optional_computed": cty.String, 1248 "required": cty.String, 1249 })), 1250 "baz": cty.ObjectVal(map[string]cty.Value{ 1251 "optional": cty.NullVal(cty.String), 1252 "computed": cty.NullVal(cty.String), 1253 "optional_computed": cty.StringVal("hello"), 1254 "required": cty.StringVal("present"), 1255 }), 1256 }), 1257 }), 1258 // want 1259 cty.ObjectVal(map[string]cty.Value{ 1260 "foo": cty.ObjectVal(map[string]cty.Value{ 1261 "bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown preserved from the config 1262 "optional": cty.String, 1263 "computed": cty.String, 1264 "optional_computed": cty.String, 1265 "required": cty.String, 1266 })), 1267 "baz": cty.ObjectVal(map[string]cty.Value{ 1268 "optional": cty.NullVal(cty.String), // config is null 1269 "computed": cty.StringVal("hello"), // computed values come from prior 1270 "optional_computed": cty.StringVal("hello"), // config takes precedent over prior in opt+computed 1271 "required": cty.StringVal("present"), // value from config 1272 }), 1273 }), 1274 }), 1275 }, 1276 "deeply nested set": { 1277 &configschema.Block{ 1278 Attributes: map[string]*configschema.Attribute{ 1279 "foo": { 1280 NestedType: &configschema.Object{ 1281 Nesting: configschema.NestingSet, 1282 Attributes: map[string]*configschema.Attribute{ 1283 "bar": { 1284 NestedType: &configschema.Object{ 1285 Nesting: configschema.NestingSet, 1286 Attributes: testAttributes, 1287 }, 1288 Required: true, 1289 }, 1290 }, 1291 }, 1292 Optional: true, 1293 }, 1294 }, 1295 }, 1296 // prior values 1297 cty.ObjectVal(map[string]cty.Value{ 1298 "foo": cty.SetVal([]cty.Value{ 1299 cty.ObjectVal(map[string]cty.Value{ 1300 "bar": cty.SetVal([]cty.Value{ 1301 cty.ObjectVal(map[string]cty.Value{ 1302 "optional": cty.StringVal("prior"), 1303 "computed": cty.StringVal("prior"), 1304 "optional_computed": cty.StringVal("prior"), 1305 "required": cty.StringVal("prior"), 1306 }), 1307 }), 1308 }), 1309 cty.ObjectVal(map[string]cty.Value{ 1310 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1311 "optional": cty.StringVal("other_prior"), 1312 "computed": cty.StringVal("other_prior"), 1313 "optional_computed": cty.StringVal("other_prior"), 1314 "required": cty.StringVal("other_prior"), 1315 })}), 1316 }), 1317 }), 1318 }), 1319 // config differs from prior 1320 cty.ObjectVal(map[string]cty.Value{ 1321 "foo": cty.SetVal([]cty.Value{ 1322 cty.ObjectVal(map[string]cty.Value{ 1323 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1324 "optional": cty.StringVal("configured"), 1325 "computed": cty.NullVal(cty.String), // computed attrs are null in config 1326 "optional_computed": cty.StringVal("configured"), 1327 "required": cty.StringVal("configured"), 1328 })}), 1329 }), 1330 cty.ObjectVal(map[string]cty.Value{ 1331 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1332 "optional": cty.NullVal(cty.String), // explicit null in config 1333 "computed": cty.NullVal(cty.String), // computed attrs are null in config 1334 "optional_computed": cty.StringVal("other_configured"), 1335 "required": cty.StringVal("other_configured"), 1336 })}), 1337 }), 1338 }), 1339 }), 1340 // want: 1341 cty.ObjectVal(map[string]cty.Value{ 1342 "foo": cty.SetVal([]cty.Value{ 1343 cty.ObjectVal(map[string]cty.Value{ 1344 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1345 "optional": cty.StringVal("configured"), 1346 "computed": cty.NullVal(cty.String), 1347 "optional_computed": cty.StringVal("configured"), 1348 "required": cty.StringVal("configured"), 1349 })}), 1350 }), 1351 cty.ObjectVal(map[string]cty.Value{ 1352 "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 1353 "optional": cty.NullVal(cty.String), // explicit null in config is preserved 1354 "computed": cty.NullVal(cty.String), 1355 "optional_computed": cty.StringVal("other_configured"), 1356 "required": cty.StringVal("other_configured"), 1357 })}), 1358 }), 1359 }), 1360 }), 1361 }, 1362 "expected null NestedTypes": { 1363 &configschema.Block{ 1364 Attributes: map[string]*configschema.Attribute{ 1365 "single": { 1366 NestedType: &configschema.Object{ 1367 Nesting: configschema.NestingSingle, 1368 Attributes: map[string]*configschema.Attribute{ 1369 "bar": {Type: cty.String}, 1370 }, 1371 }, 1372 Optional: true, 1373 }, 1374 "list": { 1375 NestedType: &configschema.Object{ 1376 Nesting: configschema.NestingList, 1377 Attributes: map[string]*configschema.Attribute{ 1378 "bar": {Type: cty.String}, 1379 }, 1380 }, 1381 Optional: true, 1382 }, 1383 "set": { 1384 NestedType: &configschema.Object{ 1385 Nesting: configschema.NestingSet, 1386 Attributes: map[string]*configschema.Attribute{ 1387 "bar": {Type: cty.String}, 1388 }, 1389 }, 1390 Optional: true, 1391 }, 1392 "map": { 1393 NestedType: &configschema.Object{ 1394 Nesting: configschema.NestingMap, 1395 Attributes: map[string]*configschema.Attribute{ 1396 "bar": {Type: cty.String}, 1397 }, 1398 }, 1399 Optional: true, 1400 }, 1401 "nested_map": { 1402 NestedType: &configschema.Object{ 1403 Nesting: configschema.NestingMap, 1404 Attributes: map[string]*configschema.Attribute{ 1405 "inner": { 1406 NestedType: &configschema.Object{ 1407 Nesting: configschema.NestingSingle, 1408 Attributes: testAttributes, 1409 }, 1410 }, 1411 }, 1412 }, 1413 Optional: true, 1414 }, 1415 }, 1416 }, 1417 cty.ObjectVal(map[string]cty.Value{ 1418 "single": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), 1419 "list": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}), 1420 "map": cty.MapVal(map[string]cty.Value{ 1421 "map_entry": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), 1422 }), 1423 "set": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}), 1424 "nested_map": cty.MapVal(map[string]cty.Value{ 1425 "a": cty.ObjectVal(map[string]cty.Value{ 1426 "inner": cty.ObjectVal(map[string]cty.Value{ 1427 "optional": cty.StringVal("foo"), 1428 "computed": cty.StringVal("foo"), 1429 "optional_computed": cty.StringVal("foo"), 1430 "required": cty.StringVal("foo"), 1431 }), 1432 }), 1433 }), 1434 }), 1435 cty.ObjectVal(map[string]cty.Value{ 1436 "single": cty.NullVal(cty.Object(map[string]cty.Type{"bar": cty.String})), 1437 "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1438 "map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1439 "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1440 "nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ 1441 "inner": cty.Object(map[string]cty.Type{ 1442 "optional": cty.String, 1443 "computed": cty.String, 1444 "optional_computed": cty.String, 1445 "required": cty.String, 1446 }), 1447 }))), 1448 }), 1449 cty.ObjectVal(map[string]cty.Value{ 1450 "single": cty.NullVal(cty.Object(map[string]cty.Type{"bar": cty.String})), 1451 "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1452 "map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1453 "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))), 1454 "nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ 1455 "inner": cty.Object(map[string]cty.Type{ 1456 "optional": cty.String, 1457 "computed": cty.String, 1458 "optional_computed": cty.String, 1459 "required": cty.String, 1460 }), 1461 }))), 1462 }), 1463 }, 1464 "expected empty NestedTypes": { 1465 &configschema.Block{ 1466 Attributes: map[string]*configschema.Attribute{ 1467 "set": { 1468 NestedType: &configschema.Object{ 1469 Nesting: configschema.NestingSet, 1470 Attributes: map[string]*configschema.Attribute{ 1471 "bar": {Type: cty.String}, 1472 }, 1473 }, 1474 Optional: true, 1475 }, 1476 "map": { 1477 NestedType: &configschema.Object{ 1478 Nesting: configschema.NestingMap, 1479 Attributes: map[string]*configschema.Attribute{ 1480 "bar": {Type: cty.String}, 1481 }, 1482 }, 1483 Optional: true, 1484 }, 1485 }, 1486 }, 1487 cty.ObjectVal(map[string]cty.Value{ 1488 "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1489 "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1490 }), 1491 cty.ObjectVal(map[string]cty.Value{ 1492 "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1493 "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1494 }), 1495 cty.ObjectVal(map[string]cty.Value{ 1496 "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1497 "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{"bar": cty.String})), 1498 }), 1499 }, 1500 "optional types set replacement": { 1501 &configschema.Block{ 1502 Attributes: map[string]*configschema.Attribute{ 1503 "set": { 1504 NestedType: &configschema.Object{ 1505 Nesting: configschema.NestingSet, 1506 Attributes: map[string]*configschema.Attribute{ 1507 "bar": { 1508 Type: cty.String, 1509 Required: true, 1510 }, 1511 }, 1512 }, 1513 Optional: true, 1514 }, 1515 }, 1516 }, 1517 cty.ObjectVal(map[string]cty.Value{ 1518 "set": cty.SetVal([]cty.Value{ 1519 cty.ObjectVal(map[string]cty.Value{ 1520 "bar": cty.StringVal("old"), 1521 }), 1522 }), 1523 }), 1524 cty.ObjectVal(map[string]cty.Value{ 1525 "set": cty.SetVal([]cty.Value{ 1526 cty.ObjectVal(map[string]cty.Value{ 1527 "bar": cty.StringVal("new"), 1528 }), 1529 }), 1530 }), 1531 cty.ObjectVal(map[string]cty.Value{ 1532 "set": cty.SetVal([]cty.Value{ 1533 cty.ObjectVal(map[string]cty.Value{ 1534 "bar": cty.StringVal("new"), 1535 }), 1536 }), 1537 }), 1538 }, 1539 "prior null nested objects": { 1540 &configschema.Block{ 1541 Attributes: map[string]*configschema.Attribute{ 1542 "single": { 1543 NestedType: &configschema.Object{ 1544 Nesting: configschema.NestingSingle, 1545 Attributes: map[string]*configschema.Attribute{ 1546 "list": { 1547 NestedType: &configschema.Object{ 1548 Nesting: configschema.NestingList, 1549 Attributes: map[string]*configschema.Attribute{ 1550 "foo": { 1551 Type: cty.String, 1552 }, 1553 }, 1554 }, 1555 Optional: true, 1556 }, 1557 }, 1558 }, 1559 Optional: true, 1560 }, 1561 "map": { 1562 NestedType: &configschema.Object{ 1563 Nesting: configschema.NestingMap, 1564 Attributes: map[string]*configschema.Attribute{ 1565 "map": { 1566 NestedType: &configschema.Object{ 1567 Nesting: configschema.NestingList, 1568 Attributes: map[string]*configschema.Attribute{ 1569 "foo": { 1570 Type: cty.String, 1571 }, 1572 }, 1573 }, 1574 Optional: true, 1575 }, 1576 }, 1577 }, 1578 Optional: true, 1579 }, 1580 }, 1581 }, 1582 cty.NullVal(cty.Object(map[string]cty.Type{ 1583 "single": cty.Object(map[string]cty.Type{ 1584 "list": cty.List(cty.Object(map[string]cty.Type{ 1585 "foo": cty.String, 1586 })), 1587 }), 1588 "map": cty.Map(cty.Object(map[string]cty.Type{ 1589 "list": cty.List(cty.Object(map[string]cty.Type{ 1590 "foo": cty.String, 1591 })), 1592 })), 1593 })), 1594 cty.ObjectVal(map[string]cty.Value{ 1595 "single": cty.ObjectVal(map[string]cty.Value{ 1596 "list": cty.ListVal([]cty.Value{ 1597 cty.ObjectVal(map[string]cty.Value{ 1598 "foo": cty.StringVal("a"), 1599 }), 1600 cty.ObjectVal(map[string]cty.Value{ 1601 "foo": cty.StringVal("b"), 1602 }), 1603 }), 1604 }), 1605 "map": cty.MapVal(map[string]cty.Value{ 1606 "one": cty.ObjectVal(map[string]cty.Value{ 1607 "list": cty.ListVal([]cty.Value{ 1608 cty.ObjectVal(map[string]cty.Value{ 1609 "foo": cty.StringVal("a"), 1610 }), 1611 cty.ObjectVal(map[string]cty.Value{ 1612 "foo": cty.StringVal("b"), 1613 }), 1614 }), 1615 }), 1616 }), 1617 }), 1618 cty.ObjectVal(map[string]cty.Value{ 1619 "single": cty.ObjectVal(map[string]cty.Value{ 1620 "list": cty.ListVal([]cty.Value{ 1621 cty.ObjectVal(map[string]cty.Value{ 1622 "foo": cty.StringVal("a"), 1623 }), 1624 cty.ObjectVal(map[string]cty.Value{ 1625 "foo": cty.StringVal("b"), 1626 }), 1627 }), 1628 }), 1629 "map": cty.MapVal(map[string]cty.Value{ 1630 "one": cty.ObjectVal(map[string]cty.Value{ 1631 "list": cty.ListVal([]cty.Value{ 1632 cty.ObjectVal(map[string]cty.Value{ 1633 "foo": cty.StringVal("a"), 1634 }), 1635 cty.ObjectVal(map[string]cty.Value{ 1636 "foo": cty.StringVal("b"), 1637 }), 1638 }), 1639 }), 1640 }), 1641 }), 1642 }, 1643 1644 // data sources are planned with an unknown value 1645 "unknown prior nested objects": { 1646 &configschema.Block{ 1647 Attributes: map[string]*configschema.Attribute{ 1648 "list": { 1649 NestedType: &configschema.Object{ 1650 Nesting: configschema.NestingList, 1651 Attributes: map[string]*configschema.Attribute{ 1652 "list": { 1653 NestedType: &configschema.Object{ 1654 Nesting: configschema.NestingList, 1655 Attributes: map[string]*configschema.Attribute{ 1656 "foo": { 1657 Type: cty.String, 1658 }, 1659 }, 1660 }, 1661 Computed: true, 1662 }, 1663 }, 1664 }, 1665 Computed: true, 1666 }, 1667 }, 1668 }, 1669 cty.UnknownVal(cty.Object(map[string]cty.Type{ 1670 "List": cty.List(cty.Object(map[string]cty.Type{ 1671 "list": cty.List(cty.Object(map[string]cty.Type{ 1672 "foo": cty.String, 1673 })), 1674 })), 1675 })), 1676 cty.NullVal(cty.Object(map[string]cty.Type{ 1677 "List": cty.List(cty.Object(map[string]cty.Type{ 1678 "list": cty.List(cty.Object(map[string]cty.Type{ 1679 "foo": cty.String, 1680 })), 1681 })), 1682 })), 1683 cty.UnknownVal(cty.Object(map[string]cty.Type{ 1684 "List": cty.List(cty.Object(map[string]cty.Type{ 1685 "list": cty.List(cty.Object(map[string]cty.Type{ 1686 "foo": cty.String, 1687 })), 1688 })), 1689 })), 1690 }, 1691 } 1692 1693 for name, test := range tests { 1694 t.Run(name, func(t *testing.T) { 1695 got := ProposedNew(test.Schema, test.Prior, test.Config) 1696 if !got.RawEquals(test.Want) { 1697 t.Errorf("wrong result\ngot: %swant: %s", dump.Value(got), dump.Value(test.Want)) 1698 } 1699 }) 1700 } 1701 } 1702 1703 var testAttributes = map[string]*configschema.Attribute{ 1704 "optional": { 1705 Type: cty.String, 1706 Optional: true, 1707 }, 1708 "computed": { 1709 Type: cty.String, 1710 Computed: true, 1711 }, 1712 "optional_computed": { 1713 Type: cty.String, 1714 Computed: true, 1715 Optional: true, 1716 }, 1717 "required": { 1718 Type: cty.String, 1719 Required: true, 1720 }, 1721 }