github.com/opentofu/opentofu@v1.7.1/internal/command/jsonformat/computed/renderers/renderer_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package renderers 7 8 import ( 9 "encoding/json" 10 "strings" 11 "testing" 12 13 "github.com/opentofu/opentofu/internal/command/jsonformat/computed" 14 15 "github.com/google/go-cmp/cmp" 16 "github.com/mitchellh/colorstring" 17 "github.com/zclconf/go-cty/cty" 18 19 "github.com/opentofu/opentofu/internal/plans" 20 ) 21 22 func TestRenderers_Human(t *testing.T) { 23 colorize := colorstring.Colorize{ 24 Colors: colorstring.DefaultColors, 25 Disable: true, 26 } 27 28 tcs := map[string]struct { 29 diff computed.Diff 30 expected string 31 opts computed.RenderHumanOpts 32 }{ 33 // We're using the string "null" in these tests to demonstrate the 34 // difference between rendering an actual string and rendering a null 35 // value. 36 "primitive_create_string": { 37 diff: computed.Diff{ 38 Renderer: Primitive(nil, "null", cty.String), 39 Action: plans.Create, 40 }, 41 expected: "\"null\"", 42 }, 43 "primitive_delete_string": { 44 diff: computed.Diff{ 45 Renderer: Primitive("null", nil, cty.String), 46 Action: plans.Delete, 47 }, 48 expected: "\"null\" -> null", 49 }, 50 "primitive_update_string_to_null": { 51 diff: computed.Diff{ 52 Renderer: Primitive("null", nil, cty.String), 53 Action: plans.Update, 54 }, 55 expected: "\"null\" -> null", 56 }, 57 "primitive_update_string_from_null": { 58 diff: computed.Diff{ 59 Renderer: Primitive(nil, "null", cty.String), 60 Action: plans.Update, 61 }, 62 expected: "null -> \"null\"", 63 }, 64 "primitive_update_multiline_string_to_null": { 65 diff: computed.Diff{ 66 Renderer: Primitive("nu\nll", nil, cty.String), 67 Action: plans.Update, 68 }, 69 expected: ` 70 <<-EOT 71 - nu 72 - ll 73 + null 74 EOT 75 `, 76 }, 77 "primitive_update_multiline_string_from_null": { 78 diff: computed.Diff{ 79 Renderer: Primitive(nil, "nu\nll", cty.String), 80 Action: plans.Update, 81 }, 82 expected: ` 83 <<-EOT 84 - null 85 + nu 86 + ll 87 EOT 88 `, 89 }, 90 "primitive_update_json_string_to_null": { 91 diff: computed.Diff{ 92 Renderer: Primitive("[null]", nil, cty.String), 93 Action: plans.Update, 94 }, 95 expected: ` 96 jsonencode( 97 [ 98 - null, 99 ] 100 ) -> null 101 `, 102 }, 103 "primitive_update_json_string_from_null": { 104 diff: computed.Diff{ 105 Renderer: Primitive(nil, "[null]", cty.String), 106 Action: plans.Update, 107 }, 108 expected: ` 109 null -> jsonencode( 110 [ 111 + null, 112 ] 113 ) 114 `, 115 }, 116 "primitive_create_null_string": { 117 diff: computed.Diff{ 118 Renderer: Primitive(nil, nil, cty.String), 119 Action: plans.Create, 120 }, 121 expected: "null", 122 }, 123 "primitive_delete_null_string": { 124 diff: computed.Diff{ 125 Renderer: Primitive(nil, nil, cty.String), 126 Action: plans.Delete, 127 }, 128 expected: "null", 129 }, 130 "primitive_create": { 131 diff: computed.Diff{ 132 Renderer: Primitive(nil, json.Number("1"), cty.Number), 133 Action: plans.Create, 134 }, 135 expected: "1", 136 }, 137 "primitive_delete": { 138 diff: computed.Diff{ 139 Renderer: Primitive(json.Number("1"), nil, cty.Number), 140 Action: plans.Delete, 141 }, 142 expected: "1 -> null", 143 }, 144 "primitive_delete_override": { 145 diff: computed.Diff{ 146 Renderer: Primitive(json.Number("1"), nil, cty.Number), 147 Action: plans.Delete, 148 }, 149 opts: computed.RenderHumanOpts{OverrideNullSuffix: true}, 150 expected: "1", 151 }, 152 "primitive_update_to_null": { 153 diff: computed.Diff{ 154 Renderer: Primitive(json.Number("1"), nil, cty.Number), 155 Action: plans.Update, 156 }, 157 expected: "1 -> null", 158 }, 159 "primitive_update_from_null": { 160 diff: computed.Diff{ 161 Renderer: Primitive(nil, json.Number("1"), cty.Number), 162 Action: plans.Update, 163 }, 164 expected: "null -> 1", 165 }, 166 "primitive_update": { 167 diff: computed.Diff{ 168 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 169 Action: plans.Update, 170 }, 171 expected: "0 -> 1", 172 }, 173 "primitive_update_max_int64_to_min_int64": { 174 diff: computed.Diff{ 175 // 9223372036854775807 == math.MinInt64 176 // -9223372036854775808 == math.MaxInt64 177 Renderer: Primitive(json.Number("9223372036854775807"), json.Number("-9223372036854775808"), cty.Number), 178 Action: plans.Update, 179 }, 180 expected: "9223372036854775807 -> -9223372036854775808", 181 }, 182 "primitive_update_21_digits_number": { 183 diff: computed.Diff{ 184 // 18446744073709551615 == math.MaxUint64 (which has 20-digits number) 185 Renderer: Primitive(json.Number("918446744073709551615"), json.Number("0"), cty.Number), 186 Action: plans.Update, 187 }, 188 expected: "918446744073709551615 -> 0", 189 }, 190 "primitive_update_replace": { 191 diff: computed.Diff{ 192 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 193 Action: plans.Update, 194 Replace: true, 195 }, 196 expected: "0 -> 1 # forces replacement", 197 }, 198 "primitive_multiline_string_create": { 199 diff: computed.Diff{ 200 Renderer: Primitive(nil, "hello\nworld", cty.String), 201 Action: plans.Create, 202 }, 203 expected: ` 204 <<-EOT 205 hello 206 world 207 EOT 208 `, 209 }, 210 "primitive_multiline_string_delete": { 211 diff: computed.Diff{ 212 Renderer: Primitive("hello\nworld", nil, cty.String), 213 Action: plans.Delete, 214 }, 215 expected: ` 216 <<-EOT 217 hello 218 world 219 EOT -> null 220 `, 221 }, 222 "primitive_multiline_string_update": { 223 diff: computed.Diff{ 224 Renderer: Primitive("hello\nold\nworld", "hello\nnew\nworld", cty.String), 225 Action: plans.Update, 226 }, 227 expected: ` 228 <<-EOT 229 hello 230 - old 231 + new 232 world 233 EOT 234 `, 235 }, 236 "primitive_json_string_create": { 237 diff: computed.Diff{ 238 Renderer: Primitive(nil, "{\"key_one\": \"value_one\",\"key_two\":\"value_two\"}", cty.String), 239 Action: plans.Create, 240 }, 241 expected: ` 242 jsonencode( 243 { 244 + key_one = "value_one" 245 + key_two = "value_two" 246 } 247 ) 248 `, 249 }, 250 "primitive_json_string_delete": { 251 diff: computed.Diff{ 252 Renderer: Primitive("{\"key_one\": \"value_one\",\"key_two\":\"value_two\"}", nil, cty.String), 253 Action: plans.Delete, 254 }, 255 expected: ` 256 jsonencode( 257 { 258 - key_one = "value_one" 259 - key_two = "value_two" 260 } 261 ) -> null 262 `, 263 }, 264 "primitive_json_string_update": { 265 diff: computed.Diff{ 266 Renderer: Primitive("{\"key_one\": \"value_one\",\"key_two\":\"value_two\"}", "{\"key_one\": \"value_one\",\"key_two\":\"value_two\",\"key_three\":\"value_three\"}", cty.String), 267 Action: plans.Update, 268 }, 269 expected: ` 270 jsonencode( 271 ~ { 272 + key_three = "value_three" 273 # (2 unchanged attributes hidden) 274 } 275 ) 276 `, 277 }, 278 "primitive_json_explicit_nulls": { 279 diff: computed.Diff{ 280 Renderer: Primitive("{\"key_one\":\"value_one\",\"key_two\":\"value_two\"}", "{\"key_one\":null}", cty.String), 281 Action: plans.Update, 282 }, 283 expected: ` 284 jsonencode( 285 ~ { 286 ~ key_one = "value_one" -> null 287 - key_two = "value_two" 288 } 289 ) 290 `, 291 }, 292 "primitive_fake_json_string_update": { 293 diff: computed.Diff{ 294 // This isn't valid JSON, our renderer should be okay with it. 295 Renderer: Primitive("{\"key_one\": \"value_one\",\"key_two\":\"value_two\"", "{\"key_one\": \"value_one\",\"key_two\":\"value_two\",\"key_three\":\"value_three\"", cty.String), 296 Action: plans.Update, 297 }, 298 expected: "\"{\\\"key_one\\\": \\\"value_one\\\",\\\"key_two\\\":\\\"value_two\\\"\" -> \"{\\\"key_one\\\": \\\"value_one\\\",\\\"key_two\\\":\\\"value_two\\\",\\\"key_three\\\":\\\"value_three\\\"\"", 299 }, 300 "primitive_multiline_to_json_update": { 301 diff: computed.Diff{ 302 Renderer: Primitive("hello\nworld", "{\"key_one\": \"value_one\",\"key_two\":\"value_two\"}", cty.String), 303 Action: plans.Update, 304 }, 305 expected: ` 306 <<-EOT 307 hello 308 world 309 EOT -> jsonencode( 310 { 311 + key_one = "value_one" 312 + key_two = "value_two" 313 } 314 ) 315 `, 316 }, 317 "primitive_json_to_multiline_update": { 318 diff: computed.Diff{ 319 Renderer: Primitive("{\"key_one\": \"value_one\",\"key_two\":\"value_two\"}", "hello\nworld", cty.String), 320 Action: plans.Update, 321 }, 322 expected: ` 323 jsonencode( 324 { 325 - key_one = "value_one" 326 - key_two = "value_two" 327 } 328 ) -> <<-EOT 329 hello 330 world 331 EOT 332 `, 333 }, 334 "primitive_json_to_string_update": { 335 diff: computed.Diff{ 336 Renderer: Primitive("{\"key_one\": \"value_one\",\"key_two\":\"value_two\"}", "hello world", cty.String), 337 Action: plans.Update, 338 }, 339 expected: ` 340 jsonencode( 341 { 342 - key_one = "value_one" 343 - key_two = "value_two" 344 } 345 ) -> "hello world" 346 `, 347 }, 348 "primitive_string_to_json_update": { 349 diff: computed.Diff{ 350 Renderer: Primitive("hello world", "{\"key_one\": \"value_one\",\"key_two\":\"value_two\"}", cty.String), 351 Action: plans.Update, 352 }, 353 expected: ` 354 "hello world" -> jsonencode( 355 { 356 + key_one = "value_one" 357 + key_two = "value_two" 358 } 359 ) 360 `, 361 }, 362 "primitive_multi_to_single_update": { 363 diff: computed.Diff{ 364 Renderer: Primitive("hello\nworld", "hello world", cty.String), 365 Action: plans.Update, 366 }, 367 expected: ` 368 <<-EOT 369 - hello 370 - world 371 + hello world 372 EOT 373 `, 374 }, 375 "primitive_single_to_multi_update": { 376 diff: computed.Diff{ 377 Renderer: Primitive("hello world", "hello\nworld", cty.String), 378 Action: plans.Update, 379 }, 380 expected: ` 381 <<-EOT 382 - hello world 383 + hello 384 + world 385 EOT 386 `, 387 }, 388 "sensitive_update": { 389 diff: computed.Diff{ 390 Renderer: Sensitive(computed.Diff{ 391 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 392 Action: plans.Update, 393 }, true, true), 394 Action: plans.Update, 395 }, 396 expected: "(sensitive value)", 397 }, 398 "sensitive_update_replace": { 399 diff: computed.Diff{ 400 Renderer: Sensitive(computed.Diff{ 401 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 402 Action: plans.Update, 403 Replace: true, 404 }, true, true), 405 Action: plans.Update, 406 Replace: true, 407 }, 408 expected: "(sensitive value) # forces replacement", 409 }, 410 "computed_create": { 411 diff: computed.Diff{ 412 Renderer: Unknown(computed.Diff{}), 413 Action: plans.Create, 414 }, 415 expected: "(known after apply)", 416 }, 417 "computed_update": { 418 diff: computed.Diff{ 419 Renderer: Unknown(computed.Diff{ 420 Renderer: Primitive(json.Number("0"), nil, cty.Number), 421 Action: plans.Delete, 422 }), 423 Action: plans.Update, 424 }, 425 expected: "0 -> (known after apply)", 426 }, 427 "computed_create_forces_replacement": { 428 diff: computed.Diff{ 429 Renderer: Unknown(computed.Diff{}), 430 Action: plans.Create, 431 Replace: true, 432 }, 433 expected: "(known after apply) # forces replacement", 434 }, 435 "computed_update_forces_replacement": { 436 diff: computed.Diff{ 437 Renderer: Unknown(computed.Diff{ 438 Renderer: Primitive(json.Number("0"), nil, cty.Number), 439 Action: plans.Delete, 440 }), 441 Action: plans.Update, 442 Replace: true, 443 }, 444 expected: "0 -> (known after apply) # forces replacement", 445 }, 446 "object_created": { 447 diff: computed.Diff{ 448 Renderer: Object(map[string]computed.Diff{}), 449 Action: plans.Create, 450 }, 451 expected: "{}", 452 }, 453 "object_created_with_attributes": { 454 diff: computed.Diff{ 455 Renderer: Object(map[string]computed.Diff{ 456 "attribute_one": { 457 Renderer: Primitive(nil, json.Number("0"), cty.Number), 458 Action: plans.Create, 459 }, 460 }), 461 Action: plans.Create, 462 }, 463 expected: ` 464 { 465 + attribute_one = 0 466 } 467 `, 468 }, 469 "object_deleted": { 470 diff: computed.Diff{ 471 Renderer: Object(map[string]computed.Diff{}), 472 Action: plans.Delete, 473 }, 474 expected: "{} -> null", 475 }, 476 "object_deleted_with_attributes": { 477 diff: computed.Diff{ 478 Renderer: Object(map[string]computed.Diff{ 479 "attribute_one": { 480 Renderer: Primitive(json.Number("0"), nil, cty.Number), 481 Action: plans.Delete, 482 }, 483 }), 484 Action: plans.Delete, 485 }, 486 expected: ` 487 { 488 - attribute_one = 0 489 } -> null 490 `, 491 }, 492 "nested_object_deleted": { 493 diff: computed.Diff{ 494 Renderer: NestedObject(map[string]computed.Diff{}), 495 Action: plans.Delete, 496 }, 497 expected: "{} -> null", 498 }, 499 "nested_object_deleted_with_attributes": { 500 diff: computed.Diff{ 501 Renderer: NestedObject(map[string]computed.Diff{ 502 "attribute_one": { 503 Renderer: Primitive(json.Number("0"), nil, cty.Number), 504 Action: plans.Delete, 505 }, 506 }), 507 Action: plans.Delete, 508 }, 509 expected: ` 510 { 511 - attribute_one = 0 -> null 512 } -> null 513 `, 514 }, 515 "object_create_attribute": { 516 diff: computed.Diff{ 517 Renderer: Object(map[string]computed.Diff{ 518 "attribute_one": { 519 Renderer: Primitive(nil, json.Number("0"), cty.Number), 520 Action: plans.Create, 521 }, 522 }), 523 Action: plans.Update, 524 }, 525 expected: ` 526 { 527 + attribute_one = 0 528 } 529 `, 530 }, 531 "object_update_attribute": { 532 diff: computed.Diff{ 533 Renderer: Object(map[string]computed.Diff{ 534 "attribute_one": { 535 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 536 Action: plans.Update, 537 }, 538 }), 539 Action: plans.Update, 540 }, 541 expected: ` 542 { 543 ~ attribute_one = 0 -> 1 544 } 545 `, 546 }, 547 "object_update_attribute_forces_replacement": { 548 diff: computed.Diff{ 549 Renderer: Object(map[string]computed.Diff{ 550 "attribute_one": { 551 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 552 Action: plans.Update, 553 }, 554 }), 555 Action: plans.Update, 556 Replace: true, 557 }, 558 expected: ` 559 { # forces replacement 560 ~ attribute_one = 0 -> 1 561 } 562 `, 563 }, 564 "object_delete_attribute": { 565 diff: computed.Diff{ 566 Renderer: Object(map[string]computed.Diff{ 567 "attribute_one": { 568 Renderer: Primitive(json.Number("0"), nil, cty.Number), 569 Action: plans.Delete, 570 }, 571 }), 572 Action: plans.Update, 573 }, 574 expected: ` 575 { 576 - attribute_one = 0 577 } 578 `, 579 }, 580 "object_ignore_unchanged_attributes": { 581 diff: computed.Diff{ 582 Renderer: Object(map[string]computed.Diff{ 583 "attribute_one": { 584 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 585 Action: plans.Update, 586 }, 587 "attribute_two": { 588 Renderer: Primitive(json.Number("0"), json.Number("0"), cty.Number), 589 Action: plans.NoOp, 590 }, 591 "attribute_three": { 592 Renderer: Primitive(nil, json.Number("1"), cty.Number), 593 Action: plans.Create, 594 }, 595 }), 596 Action: plans.Update, 597 }, 598 expected: ` 599 { 600 ~ attribute_one = 0 -> 1 601 + attribute_three = 1 602 # (1 unchanged attribute hidden) 603 } 604 `, 605 }, 606 "object_create_sensitive_attribute": { 607 diff: computed.Diff{ 608 Renderer: Object(map[string]computed.Diff{ 609 "attribute_one": { 610 Renderer: Sensitive(computed.Diff{ 611 Renderer: Primitive(nil, json.Number("1"), cty.Number), 612 Action: plans.Create, 613 }, false, true), 614 Action: plans.Create, 615 }, 616 }), 617 Action: plans.Update, 618 }, 619 expected: ` 620 { 621 + attribute_one = (sensitive value) 622 } 623 `, 624 }, 625 "object_update_sensitive_attribute": { 626 diff: computed.Diff{ 627 Renderer: Object(map[string]computed.Diff{ 628 "attribute_one": { 629 Renderer: Sensitive(computed.Diff{ 630 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 631 Action: plans.Update, 632 }, true, true), 633 Action: plans.Update, 634 }, 635 }), 636 Action: plans.Update, 637 }, 638 expected: ` 639 { 640 ~ attribute_one = (sensitive value) 641 } 642 `, 643 }, 644 "object_delete_sensitive_attribute": { 645 diff: computed.Diff{ 646 Renderer: Object(map[string]computed.Diff{ 647 "attribute_one": { 648 Renderer: Sensitive(computed.Diff{ 649 Renderer: Primitive(json.Number("0"), nil, cty.Number), 650 Action: plans.Delete, 651 }, true, false), 652 Action: plans.Delete, 653 }, 654 }), 655 Action: plans.Update, 656 }, 657 expected: ` 658 { 659 - attribute_one = (sensitive value) 660 } 661 `, 662 }, 663 "object_create_computed_attribute": { 664 diff: computed.Diff{ 665 Renderer: Object(map[string]computed.Diff{ 666 "attribute_one": { 667 Renderer: Unknown(computed.Diff{Renderer: nil}), 668 Action: plans.Create, 669 }, 670 }), 671 Action: plans.Update, 672 }, 673 expected: ` 674 { 675 + attribute_one = (known after apply) 676 } 677 `, 678 }, 679 "object_update_computed_attribute": { 680 diff: computed.Diff{ 681 Renderer: Object(map[string]computed.Diff{ 682 "attribute_one": { 683 Renderer: Unknown(computed.Diff{ 684 Renderer: Primitive(json.Number("1"), nil, cty.Number), 685 Action: plans.Delete, 686 }), 687 Action: plans.Update, 688 }, 689 }), 690 Action: plans.Update, 691 }, 692 expected: ` 693 { 694 ~ attribute_one = 1 -> (known after apply) 695 } 696 `, 697 }, 698 "object_escapes_attribute_keys": { 699 diff: computed.Diff{ 700 Renderer: Object(map[string]computed.Diff{ 701 "attribute_one": { 702 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 703 Action: plans.Update, 704 }, 705 "attribute:two": { 706 Renderer: Primitive(json.Number("2"), json.Number("3"), cty.Number), 707 Action: plans.Update, 708 }, 709 "attribute_six": { 710 Renderer: Primitive(json.Number("3"), json.Number("4"), cty.Number), 711 Action: plans.Update, 712 }, 713 }), 714 Action: plans.Update, 715 }, 716 expected: ` 717 { 718 ~ "attribute:two" = 2 -> 3 719 ~ attribute_one = 1 -> 2 720 ~ attribute_six = 3 -> 4 721 } 722 `, 723 }, 724 "map_create_empty": { 725 diff: computed.Diff{ 726 Renderer: Map(map[string]computed.Diff{}), 727 Action: plans.Create, 728 }, 729 expected: "{}", 730 }, 731 "map_create": { 732 diff: computed.Diff{ 733 Renderer: Map(map[string]computed.Diff{ 734 "element_one": { 735 Renderer: Primitive(nil, "new", cty.String), 736 Action: plans.Create, 737 }, 738 }), 739 Action: plans.Create, 740 }, 741 expected: ` 742 { 743 + "element_one" = "new" 744 } 745 `, 746 }, 747 "map_delete_empty": { 748 diff: computed.Diff{ 749 Renderer: Map(map[string]computed.Diff{}), 750 Action: plans.Delete, 751 }, 752 expected: "{} -> null", 753 }, 754 "map_delete": { 755 diff: computed.Diff{ 756 Renderer: Map(map[string]computed.Diff{ 757 "element_one": { 758 Renderer: Primitive("old", nil, cty.String), 759 Action: plans.Delete, 760 }, 761 }), 762 Action: plans.Delete, 763 }, 764 expected: ` 765 { 766 - "element_one" = "old" 767 } -> null 768 `, 769 }, 770 "map_create_element": { 771 diff: computed.Diff{ 772 Renderer: Map(map[string]computed.Diff{ 773 "element_one": { 774 Renderer: Primitive(nil, "new", cty.String), 775 Action: plans.Create, 776 }, 777 }), 778 Action: plans.Update, 779 }, 780 expected: ` 781 { 782 + "element_one" = "new" 783 } 784 `, 785 }, 786 "map_update_element": { 787 diff: computed.Diff{ 788 Renderer: Map(map[string]computed.Diff{ 789 "element_one": { 790 Renderer: Primitive("old", "new", cty.String), 791 Action: plans.Update, 792 }, 793 }), 794 Action: plans.Update, 795 }, 796 expected: ` 797 { 798 ~ "element_one" = "old" -> "new" 799 } 800 `, 801 }, 802 "map_delete_element": { 803 diff: computed.Diff{ 804 Renderer: Map(map[string]computed.Diff{ 805 "element_one": { 806 Renderer: Primitive("old", nil, cty.String), 807 Action: plans.Delete, 808 }, 809 }), 810 Action: plans.Update, 811 }, 812 expected: ` 813 { 814 - "element_one" = "old" -> null 815 } 816 `, 817 }, 818 "map_update_forces_replacement": { 819 diff: computed.Diff{ 820 Renderer: Map(map[string]computed.Diff{ 821 "element_one": { 822 Renderer: Primitive("old", "new", cty.String), 823 Action: plans.Update, 824 }, 825 }), 826 Action: plans.Update, 827 Replace: true, 828 }, 829 expected: ` 830 { # forces replacement 831 ~ "element_one" = "old" -> "new" 832 } 833 `, 834 }, 835 "map_ignore_unchanged_elements": { 836 diff: computed.Diff{ 837 Renderer: Map(map[string]computed.Diff{ 838 "element_one": { 839 Renderer: Primitive(nil, "new", cty.String), 840 Action: plans.Create, 841 }, 842 "element_two": { 843 Renderer: Primitive("old", "old", cty.String), 844 Action: plans.NoOp, 845 }, 846 "element_three": { 847 Renderer: Primitive("old", "new", cty.String), 848 Action: plans.Update, 849 }, 850 }), 851 Action: plans.Update, 852 }, 853 expected: ` 854 { 855 + "element_one" = "new" 856 ~ "element_three" = "old" -> "new" 857 # (1 unchanged element hidden) 858 } 859 `, 860 }, 861 "map_create_sensitive_element": { 862 diff: computed.Diff{ 863 Renderer: Map(map[string]computed.Diff{ 864 "element_one": { 865 Renderer: Sensitive(computed.Diff{ 866 Renderer: Primitive(nil, json.Number("1"), cty.Number), 867 Action: plans.Create, 868 }, false, true), 869 Action: plans.Create, 870 }, 871 }), 872 Action: plans.Update, 873 }, 874 expected: ` 875 { 876 + "element_one" = (sensitive value) 877 } 878 `, 879 }, 880 "map_update_sensitive_element": { 881 diff: computed.Diff{ 882 Renderer: Map(map[string]computed.Diff{ 883 "element_one": { 884 Renderer: Sensitive(computed.Diff{ 885 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 886 Action: plans.Update, 887 }, true, true), 888 Action: plans.Update, 889 }, 890 }), 891 Action: plans.Update, 892 }, 893 expected: ` 894 { 895 ~ "element_one" = (sensitive value) 896 } 897 `, 898 }, 899 "map_update_sensitive_element_status": { 900 diff: computed.Diff{ 901 Renderer: Map(map[string]computed.Diff{ 902 "element_one": { 903 Renderer: Sensitive(computed.Diff{ 904 Renderer: Primitive(json.Number("0"), json.Number("0"), cty.Number), 905 Action: plans.NoOp, 906 }, true, false), 907 Action: plans.Update, 908 }, 909 }), 910 Action: plans.Update, 911 }, 912 expected: ` 913 { 914 # Warning: this attribute value will no longer be marked as sensitive 915 # after applying this change. The value is unchanged. 916 ~ "element_one" = (sensitive value) 917 } 918 `, 919 }, 920 "map_delete_sensitive_element": { 921 diff: computed.Diff{ 922 Renderer: Map(map[string]computed.Diff{ 923 "element_one": { 924 Renderer: Sensitive(computed.Diff{ 925 Renderer: Primitive(json.Number("0"), nil, cty.Number), 926 Action: plans.Delete, 927 }, true, false), 928 Action: plans.Delete, 929 }, 930 }), 931 Action: plans.Update, 932 }, 933 expected: ` 934 { 935 - "element_one" = (sensitive value) -> null 936 } 937 `, 938 }, 939 "map_create_computed_element": { 940 diff: computed.Diff{ 941 Renderer: Map(map[string]computed.Diff{ 942 "element_one": { 943 Renderer: Unknown(computed.Diff{}), 944 Action: plans.Create, 945 }, 946 }), 947 Action: plans.Update, 948 }, 949 expected: ` 950 { 951 + "element_one" = (known after apply) 952 } 953 `, 954 }, 955 "map_update_computed_element": { 956 diff: computed.Diff{ 957 Renderer: Map(map[string]computed.Diff{ 958 "element_one": { 959 Renderer: Unknown(computed.Diff{ 960 Renderer: Primitive(json.Number("1"), nil, cty.Number), 961 Action: plans.Delete, 962 }), 963 Action: plans.Update, 964 }, 965 }), 966 Action: plans.Update, 967 }, 968 expected: ` 969 { 970 ~ "element_one" = 1 -> (known after apply) 971 } 972 `, 973 }, 974 "map_aligns_key": { 975 diff: computed.Diff{ 976 Renderer: Map(map[string]computed.Diff{ 977 "element_one": { 978 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 979 Action: plans.Update, 980 }, 981 "element_two": { 982 Renderer: Primitive(json.Number("1"), json.Number("3"), cty.Number), 983 Action: plans.Update, 984 }, 985 "element_three": { 986 Renderer: Primitive(json.Number("1"), json.Number("4"), cty.Number), 987 Action: plans.Update, 988 }, 989 }), 990 Action: plans.Update, 991 }, 992 expected: ` 993 { 994 ~ "element_one" = 1 -> 2 995 ~ "element_three" = 1 -> 4 996 ~ "element_two" = 1 -> 3 997 } 998 `, 999 }, 1000 "nested_map_does_not_align_keys": { 1001 diff: computed.Diff{ 1002 Renderer: NestedMap(map[string]computed.Diff{ 1003 "element_one": { 1004 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 1005 Action: plans.Update, 1006 }, 1007 "element_two": { 1008 Renderer: Primitive(json.Number("1"), json.Number("3"), cty.Number), 1009 Action: plans.Update, 1010 }, 1011 "element_three": { 1012 Renderer: Primitive(json.Number("1"), json.Number("4"), cty.Number), 1013 Action: plans.Update, 1014 }, 1015 }), 1016 Action: plans.Update, 1017 }, 1018 expected: ` 1019 { 1020 ~ "element_one" = 1 -> 2 1021 ~ "element_three" = 1 -> 4 1022 ~ "element_two" = 1 -> 3 1023 } 1024 `, 1025 }, 1026 "list_create_empty": { 1027 diff: computed.Diff{ 1028 Renderer: List([]computed.Diff{}), 1029 Action: plans.Create, 1030 }, 1031 expected: "[]", 1032 }, 1033 "list_create": { 1034 diff: computed.Diff{ 1035 Renderer: List([]computed.Diff{ 1036 { 1037 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1038 Action: plans.Create, 1039 }, 1040 }), 1041 Action: plans.Create, 1042 }, 1043 expected: ` 1044 [ 1045 + 1, 1046 ] 1047 `, 1048 }, 1049 "list_delete_empty": { 1050 diff: computed.Diff{ 1051 Renderer: List([]computed.Diff{}), 1052 Action: plans.Delete, 1053 }, 1054 expected: "[] -> null", 1055 }, 1056 "list_delete": { 1057 diff: computed.Diff{ 1058 Renderer: List([]computed.Diff{ 1059 { 1060 Renderer: Primitive(json.Number("1"), nil, cty.Number), 1061 Action: plans.Delete, 1062 }, 1063 }), 1064 Action: plans.Delete, 1065 }, 1066 expected: ` 1067 [ 1068 - 1, 1069 ] -> null 1070 `, 1071 }, 1072 "list_create_element": { 1073 diff: computed.Diff{ 1074 Renderer: List([]computed.Diff{ 1075 { 1076 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1077 Action: plans.Create, 1078 }, 1079 }), 1080 Action: plans.Update, 1081 }, 1082 expected: ` 1083 [ 1084 + 1, 1085 ] 1086 `, 1087 }, 1088 "list_update_element": { 1089 diff: computed.Diff{ 1090 Renderer: List([]computed.Diff{ 1091 { 1092 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 1093 Action: plans.Update, 1094 }, 1095 }), 1096 Action: plans.Update, 1097 }, 1098 expected: ` 1099 [ 1100 ~ 0 -> 1, 1101 ] 1102 `, 1103 }, 1104 "list_replace_element": { 1105 diff: computed.Diff{ 1106 Renderer: List([]computed.Diff{ 1107 { 1108 Renderer: Primitive(json.Number("0"), nil, cty.Number), 1109 Action: plans.Delete, 1110 }, 1111 { 1112 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1113 Action: plans.Create, 1114 }, 1115 }), 1116 Action: plans.Update, 1117 }, 1118 expected: ` 1119 [ 1120 - 0, 1121 + 1, 1122 ] 1123 `, 1124 }, 1125 "list_delete_element": { 1126 diff: computed.Diff{ 1127 Renderer: List([]computed.Diff{ 1128 { 1129 Renderer: Primitive(json.Number("0"), nil, cty.Number), 1130 Action: plans.Delete, 1131 }, 1132 }), 1133 Action: plans.Update, 1134 }, 1135 expected: ` 1136 [ 1137 - 0, 1138 ] 1139 `, 1140 }, 1141 "list_update_forces_replacement": { 1142 diff: computed.Diff{ 1143 Renderer: List([]computed.Diff{ 1144 { 1145 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 1146 Action: plans.Update, 1147 }, 1148 }), 1149 Action: plans.Update, 1150 Replace: true, 1151 }, 1152 expected: ` 1153 [ # forces replacement 1154 ~ 0 -> 1, 1155 ] 1156 `, 1157 }, 1158 "list_update_ignores_unchanged": { 1159 diff: computed.Diff{ 1160 Renderer: NestedList([]computed.Diff{ 1161 { 1162 Renderer: Primitive(json.Number("0"), json.Number("0"), cty.Number), 1163 Action: plans.NoOp, 1164 }, 1165 { 1166 Renderer: Primitive(json.Number("1"), json.Number("1"), cty.Number), 1167 Action: plans.NoOp, 1168 }, 1169 { 1170 Renderer: Primitive(json.Number("2"), json.Number("5"), cty.Number), 1171 Action: plans.Update, 1172 }, 1173 { 1174 Renderer: Primitive(json.Number("3"), json.Number("3"), cty.Number), 1175 Action: plans.NoOp, 1176 }, 1177 { 1178 Renderer: Primitive(json.Number("4"), json.Number("4"), cty.Number), 1179 Action: plans.NoOp, 1180 }, 1181 }), 1182 Action: plans.Update, 1183 }, 1184 expected: ` 1185 [ 1186 ~ 2 -> 5, 1187 # (4 unchanged elements hidden) 1188 ] 1189 `, 1190 }, 1191 "list_update_ignored_unchanged_with_context": { 1192 diff: computed.Diff{ 1193 Renderer: List([]computed.Diff{ 1194 { 1195 Renderer: Primitive(json.Number("0"), json.Number("0"), cty.Number), 1196 Action: plans.NoOp, 1197 }, 1198 { 1199 Renderer: Primitive(json.Number("1"), json.Number("1"), cty.Number), 1200 Action: plans.NoOp, 1201 }, 1202 { 1203 Renderer: Primitive(json.Number("2"), json.Number("5"), cty.Number), 1204 Action: plans.Update, 1205 }, 1206 { 1207 Renderer: Primitive(json.Number("3"), json.Number("3"), cty.Number), 1208 Action: plans.NoOp, 1209 }, 1210 { 1211 Renderer: Primitive(json.Number("4"), json.Number("4"), cty.Number), 1212 Action: plans.NoOp, 1213 }, 1214 }), 1215 Action: plans.Update, 1216 }, 1217 expected: ` 1218 [ 1219 # (1 unchanged element hidden) 1220 1, 1221 ~ 2 -> 5, 1222 3, 1223 # (1 unchanged element hidden) 1224 ] 1225 `, 1226 }, 1227 "list_create_sensitive_element": { 1228 diff: computed.Diff{ 1229 Renderer: List([]computed.Diff{ 1230 { 1231 Renderer: Sensitive(computed.Diff{ 1232 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1233 Action: plans.Create, 1234 }, false, true), 1235 Action: plans.Create, 1236 }, 1237 }), 1238 Action: plans.Update, 1239 }, 1240 expected: ` 1241 [ 1242 + (sensitive value), 1243 ] 1244 `, 1245 }, 1246 "list_delete_sensitive_element": { 1247 diff: computed.Diff{ 1248 Renderer: List([]computed.Diff{ 1249 { 1250 Renderer: Sensitive(computed.Diff{ 1251 Renderer: Primitive(json.Number("1"), nil, cty.Number), 1252 Action: plans.Delete, 1253 }, true, false), 1254 Action: plans.Delete, 1255 }, 1256 }), 1257 Action: plans.Update, 1258 }, 1259 expected: ` 1260 [ 1261 - (sensitive value), 1262 ] 1263 `, 1264 }, 1265 "list_update_sensitive_element": { 1266 diff: computed.Diff{ 1267 Renderer: List([]computed.Diff{ 1268 { 1269 Renderer: Sensitive(computed.Diff{ 1270 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 1271 Action: plans.Update, 1272 }, true, true), 1273 Action: plans.Update, 1274 }, 1275 }), 1276 Action: plans.Update, 1277 }, 1278 expected: ` 1279 [ 1280 ~ (sensitive value), 1281 ] 1282 `, 1283 }, 1284 "list_update_sensitive_element_status": { 1285 diff: computed.Diff{ 1286 Renderer: List([]computed.Diff{ 1287 { 1288 Renderer: Sensitive(computed.Diff{ 1289 Renderer: Primitive(json.Number("1"), json.Number("1"), cty.Number), 1290 Action: plans.NoOp, 1291 }, false, true), 1292 Action: plans.Update, 1293 }, 1294 }), 1295 Action: plans.Update, 1296 }, 1297 expected: ` 1298 [ 1299 # Warning: this attribute value will be marked as sensitive and will not 1300 # display in UI output after applying this change. The value is unchanged. 1301 ~ (sensitive value), 1302 ] 1303 `, 1304 }, 1305 "list_create_computed_element": { 1306 diff: computed.Diff{ 1307 Renderer: List([]computed.Diff{ 1308 { 1309 Renderer: Unknown(computed.Diff{}), 1310 Action: plans.Create, 1311 }, 1312 }), 1313 Action: plans.Update, 1314 }, 1315 expected: ` 1316 [ 1317 + (known after apply), 1318 ] 1319 `, 1320 }, 1321 "list_update_computed_element": { 1322 diff: computed.Diff{ 1323 Renderer: List([]computed.Diff{ 1324 { 1325 Renderer: Unknown(computed.Diff{ 1326 Renderer: Primitive(json.Number("0"), nil, cty.Number), 1327 Action: plans.Delete, 1328 }), 1329 Action: plans.Update, 1330 }, 1331 }), 1332 Action: plans.Update, 1333 }, 1334 expected: ` 1335 [ 1336 ~ 0 -> (known after apply), 1337 ] 1338 `, 1339 }, 1340 "set_create_empty": { 1341 diff: computed.Diff{ 1342 Renderer: Set([]computed.Diff{}), 1343 Action: plans.Create, 1344 }, 1345 expected: "[]", 1346 }, 1347 "set_create": { 1348 diff: computed.Diff{ 1349 Renderer: Set([]computed.Diff{ 1350 { 1351 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1352 Action: plans.Create, 1353 }, 1354 }), 1355 Action: plans.Create, 1356 }, 1357 expected: ` 1358 [ 1359 + 1, 1360 ] 1361 `, 1362 }, 1363 "set_delete_empty": { 1364 diff: computed.Diff{ 1365 Renderer: Set([]computed.Diff{}), 1366 Action: plans.Delete, 1367 }, 1368 expected: "[] -> null", 1369 }, 1370 "set_delete": { 1371 diff: computed.Diff{ 1372 Renderer: Set([]computed.Diff{ 1373 { 1374 Renderer: Primitive(json.Number("1"), nil, cty.Number), 1375 Action: plans.Delete, 1376 }, 1377 }), 1378 Action: plans.Delete, 1379 }, 1380 expected: ` 1381 [ 1382 - 1, 1383 ] -> null 1384 `, 1385 }, 1386 "set_create_element": { 1387 diff: computed.Diff{ 1388 Renderer: Set([]computed.Diff{ 1389 { 1390 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1391 Action: plans.Create, 1392 }, 1393 }), 1394 Action: plans.Update, 1395 }, 1396 expected: ` 1397 [ 1398 + 1, 1399 ] 1400 `, 1401 }, 1402 "set_update_element": { 1403 diff: computed.Diff{ 1404 Renderer: Set([]computed.Diff{ 1405 { 1406 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 1407 Action: plans.Update, 1408 }, 1409 }), 1410 Action: plans.Update, 1411 }, 1412 expected: ` 1413 [ 1414 ~ 0 -> 1, 1415 ] 1416 `, 1417 }, 1418 "set_replace_element": { 1419 diff: computed.Diff{ 1420 Renderer: Set([]computed.Diff{ 1421 { 1422 Renderer: Primitive(json.Number("0"), nil, cty.Number), 1423 Action: plans.Delete, 1424 }, 1425 { 1426 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1427 Action: plans.Create, 1428 }, 1429 }), 1430 Action: plans.Update, 1431 }, 1432 expected: ` 1433 [ 1434 - 0, 1435 + 1, 1436 ] 1437 `, 1438 }, 1439 "set_delete_element": { 1440 diff: computed.Diff{ 1441 Renderer: Set([]computed.Diff{ 1442 { 1443 Renderer: Primitive(json.Number("0"), nil, cty.Number), 1444 Action: plans.Delete, 1445 }, 1446 }), 1447 Action: plans.Update, 1448 }, 1449 expected: ` 1450 [ 1451 - 0, 1452 ] 1453 `, 1454 }, 1455 "set_update_forces_replacement": { 1456 diff: computed.Diff{ 1457 Renderer: Set([]computed.Diff{ 1458 { 1459 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 1460 Action: plans.Update, 1461 }, 1462 }), 1463 Action: plans.Update, 1464 Replace: true, 1465 }, 1466 expected: ` 1467 [ # forces replacement 1468 ~ 0 -> 1, 1469 ] 1470 `, 1471 }, 1472 "nested_set_update_forces_replacement": { 1473 diff: computed.Diff{ 1474 Renderer: NestedSet([]computed.Diff{ 1475 { 1476 Renderer: Object(map[string]computed.Diff{ 1477 "attribute_one": { 1478 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 1479 Action: plans.Update, 1480 }, 1481 }), 1482 Action: plans.Update, 1483 }, 1484 }), 1485 Action: plans.Update, 1486 Replace: true, 1487 }, 1488 expected: ` 1489 [ 1490 ~ { # forces replacement 1491 ~ attribute_one = 0 -> 1 1492 }, 1493 ] 1494 `, 1495 }, 1496 "set_update_ignores_unchanged": { 1497 diff: computed.Diff{ 1498 Renderer: Set([]computed.Diff{ 1499 { 1500 Renderer: Primitive(json.Number("0"), json.Number("0"), cty.Number), 1501 Action: plans.NoOp, 1502 }, 1503 { 1504 Renderer: Primitive(json.Number("1"), json.Number("1"), cty.Number), 1505 Action: plans.NoOp, 1506 }, 1507 { 1508 Renderer: Primitive(json.Number("2"), json.Number("5"), cty.Number), 1509 Action: plans.Update, 1510 }, 1511 { 1512 Renderer: Primitive(json.Number("3"), json.Number("3"), cty.Number), 1513 Action: plans.NoOp, 1514 }, 1515 { 1516 Renderer: Primitive(json.Number("4"), json.Number("4"), cty.Number), 1517 Action: plans.NoOp, 1518 }, 1519 }), 1520 Action: plans.Update, 1521 }, 1522 expected: ` 1523 [ 1524 ~ 2 -> 5, 1525 # (4 unchanged elements hidden) 1526 ] 1527 `, 1528 }, 1529 "set_create_sensitive_element": { 1530 diff: computed.Diff{ 1531 Renderer: Set([]computed.Diff{ 1532 { 1533 Renderer: Sensitive(computed.Diff{ 1534 Renderer: Primitive(nil, json.Number("1"), cty.Number), 1535 Action: plans.Create, 1536 }, false, true), 1537 Action: plans.Create, 1538 }, 1539 }), 1540 Action: plans.Update, 1541 }, 1542 expected: ` 1543 [ 1544 + (sensitive value), 1545 ] 1546 `, 1547 }, 1548 "set_delete_sensitive_element": { 1549 diff: computed.Diff{ 1550 Renderer: Set([]computed.Diff{ 1551 { 1552 Renderer: Sensitive(computed.Diff{ 1553 Renderer: Primitive(json.Number("1"), nil, cty.Number), 1554 Action: plans.Delete, 1555 }, false, true), 1556 Action: plans.Delete, 1557 }, 1558 }), 1559 Action: plans.Update, 1560 }, 1561 expected: ` 1562 [ 1563 - (sensitive value), 1564 ] 1565 `, 1566 }, 1567 "set_update_sensitive_element": { 1568 diff: computed.Diff{ 1569 Renderer: Set([]computed.Diff{ 1570 { 1571 Renderer: Sensitive(computed.Diff{ 1572 Renderer: Primitive(json.Number("0"), json.Number("1"), cty.Number), 1573 Action: plans.Update, 1574 }, true, true), 1575 Action: plans.Update, 1576 }, 1577 }), 1578 Action: plans.Update, 1579 }, 1580 expected: ` 1581 [ 1582 ~ (sensitive value), 1583 ] 1584 `, 1585 }, 1586 "set_update_sensitive_element_status": { 1587 diff: computed.Diff{ 1588 Renderer: Set([]computed.Diff{ 1589 { 1590 Renderer: Sensitive(computed.Diff{ 1591 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 1592 Action: plans.Update, 1593 }, false, true), 1594 Action: plans.Update, 1595 }, 1596 }), 1597 Action: plans.Update, 1598 }, 1599 expected: ` 1600 [ 1601 # Warning: this attribute value will be marked as sensitive and will not 1602 # display in UI output after applying this change. 1603 ~ (sensitive value), 1604 ] 1605 `, 1606 }, 1607 "set_create_computed_element": { 1608 diff: computed.Diff{ 1609 Renderer: Set([]computed.Diff{ 1610 { 1611 Renderer: Unknown(computed.Diff{}), 1612 Action: plans.Create, 1613 }, 1614 }), 1615 Action: plans.Update, 1616 }, 1617 expected: ` 1618 [ 1619 + (known after apply), 1620 ] 1621 `, 1622 }, 1623 "set_update_computed_element": { 1624 diff: computed.Diff{ 1625 Renderer: Set([]computed.Diff{ 1626 { 1627 Renderer: Unknown(computed.Diff{ 1628 Renderer: Primitive(json.Number("0"), nil, cty.Number), 1629 Action: plans.Delete, 1630 }), 1631 Action: plans.Update, 1632 }, 1633 }), 1634 Action: plans.Update, 1635 }, 1636 expected: ` 1637 [ 1638 ~ 0 -> (known after apply), 1639 ] 1640 `, 1641 }, 1642 "create_empty_block": { 1643 diff: computed.Diff{ 1644 Renderer: Block(nil, Blocks{}), 1645 Action: plans.Create, 1646 }, 1647 expected: "{}", 1648 }, 1649 "create_populated_block": { 1650 diff: computed.Diff{ 1651 Renderer: Block(map[string]computed.Diff{ 1652 "string": { 1653 Renderer: Primitive(nil, "root", cty.String), 1654 Action: plans.Create, 1655 }, 1656 "boolean": { 1657 Renderer: Primitive(nil, true, cty.Bool), 1658 Action: plans.Create, 1659 }, 1660 }, Blocks{ 1661 SingleBlocks: map[string]computed.Diff{ 1662 "nested_block": { 1663 Renderer: Block(map[string]computed.Diff{ 1664 "string": { 1665 Renderer: Primitive(nil, "one", cty.String), 1666 Action: plans.Create, 1667 }, 1668 }, Blocks{}), 1669 Action: plans.Create, 1670 }, 1671 "nested_block_two": { 1672 Renderer: Block(map[string]computed.Diff{ 1673 "string": { 1674 Renderer: Primitive(nil, "two", cty.String), 1675 Action: plans.Create, 1676 }, 1677 }, Blocks{}), 1678 Action: plans.Create, 1679 }, 1680 }, 1681 }), 1682 Action: plans.Create, 1683 }, 1684 expected: ` 1685 { 1686 + boolean = true 1687 + string = "root" 1688 1689 + nested_block { 1690 + string = "one" 1691 } 1692 1693 + nested_block_two { 1694 + string = "two" 1695 } 1696 }`, 1697 }, 1698 "update_empty_block": { 1699 diff: computed.Diff{ 1700 Renderer: Block(map[string]computed.Diff{ 1701 "string": { 1702 Renderer: Primitive(nil, "root", cty.String), 1703 Action: plans.Create, 1704 }, 1705 "boolean": { 1706 Renderer: Primitive(nil, true, cty.Bool), 1707 Action: plans.Create, 1708 }, 1709 }, Blocks{ 1710 SingleBlocks: map[string]computed.Diff{ 1711 "nested_block": { 1712 1713 Renderer: Block(map[string]computed.Diff{ 1714 "string": { 1715 Renderer: Primitive(nil, "one", cty.String), 1716 Action: plans.Create, 1717 }, 1718 }, Blocks{}), 1719 Action: plans.Create, 1720 }, 1721 "nested_block_two": { 1722 1723 Renderer: Block(map[string]computed.Diff{ 1724 "string": { 1725 Renderer: Primitive(nil, "two", cty.String), 1726 Action: plans.Create, 1727 }, 1728 }, Blocks{}), 1729 Action: plans.Create, 1730 }, 1731 }, 1732 }), 1733 Action: plans.Update, 1734 }, 1735 expected: ` 1736 { 1737 + boolean = true 1738 + string = "root" 1739 1740 + nested_block { 1741 + string = "one" 1742 } 1743 1744 + nested_block_two { 1745 + string = "two" 1746 } 1747 }`, 1748 }, 1749 "update_populated_block": { 1750 diff: computed.Diff{ 1751 Renderer: Block(map[string]computed.Diff{ 1752 "string": { 1753 Renderer: Primitive(nil, "root", cty.String), 1754 Action: plans.Create, 1755 }, 1756 "boolean": { 1757 Renderer: Primitive(false, true, cty.Bool), 1758 Action: plans.Update, 1759 }, 1760 }, Blocks{ 1761 SingleBlocks: map[string]computed.Diff{ 1762 "nested_block": { 1763 Renderer: Block(map[string]computed.Diff{ 1764 "string": { 1765 Renderer: Primitive(nil, "one", cty.String), 1766 Action: plans.NoOp, 1767 }, 1768 }, Blocks{}), 1769 Action: plans.NoOp, 1770 }, 1771 "nested_block_two": { 1772 Renderer: Block(map[string]computed.Diff{ 1773 "string": { 1774 Renderer: Primitive(nil, "two", cty.String), 1775 Action: plans.Create, 1776 }, 1777 }, Blocks{}), 1778 Action: plans.Create, 1779 }, 1780 }, 1781 }), 1782 Action: plans.Update, 1783 }, 1784 expected: ` 1785 { 1786 ~ boolean = false -> true 1787 + string = "root" 1788 1789 + nested_block_two { 1790 + string = "two" 1791 } 1792 1793 # (1 unchanged block hidden) 1794 }`, 1795 }, 1796 "clear_populated_block": { 1797 diff: computed.Diff{ 1798 Renderer: Block(map[string]computed.Diff{ 1799 "string": { 1800 Renderer: Primitive("root", nil, cty.String), 1801 Action: plans.Delete, 1802 }, 1803 "boolean": { 1804 Renderer: Primitive(true, nil, cty.Bool), 1805 Action: plans.Delete, 1806 }, 1807 }, Blocks{ 1808 SingleBlocks: map[string]computed.Diff{ 1809 "nested_block": { 1810 Renderer: Block(map[string]computed.Diff{ 1811 "string": { 1812 Renderer: Primitive("one", nil, cty.String), 1813 Action: plans.Delete, 1814 }, 1815 }, Blocks{}), 1816 Action: plans.Delete, 1817 }, 1818 "nested_block_two": { 1819 Renderer: Block(map[string]computed.Diff{ 1820 "string": { 1821 Renderer: Primitive("two", nil, cty.String), 1822 Action: plans.Delete, 1823 }, 1824 }, Blocks{}), 1825 Action: plans.Delete, 1826 }, 1827 }, 1828 }), 1829 Action: plans.Update, 1830 }, 1831 expected: ` 1832 { 1833 - boolean = true -> null 1834 - string = "root" -> null 1835 1836 - nested_block { 1837 - string = "one" -> null 1838 } 1839 1840 - nested_block_two { 1841 - string = "two" -> null 1842 } 1843 }`, 1844 }, 1845 "delete_populated_block": { 1846 diff: computed.Diff{ 1847 Renderer: Block(map[string]computed.Diff{ 1848 "string": { 1849 Renderer: Primitive("root", nil, cty.String), 1850 Action: plans.Delete, 1851 }, 1852 "boolean": { 1853 Renderer: Primitive(true, nil, cty.Bool), 1854 Action: plans.Delete, 1855 }, 1856 }, Blocks{ 1857 SingleBlocks: map[string]computed.Diff{ 1858 "nested_block": { 1859 Renderer: Block(map[string]computed.Diff{ 1860 "string": { 1861 Renderer: Primitive("one", nil, cty.String), 1862 Action: plans.Delete, 1863 }, 1864 }, Blocks{}), 1865 Action: plans.Delete, 1866 }, 1867 "nested_block_two": { 1868 Renderer: Block(map[string]computed.Diff{ 1869 "string": { 1870 Renderer: Primitive("two", nil, cty.String), 1871 Action: plans.Delete, 1872 }, 1873 }, Blocks{}), 1874 Action: plans.Delete, 1875 }, 1876 }, 1877 }), 1878 Action: plans.Delete, 1879 }, 1880 expected: ` 1881 { 1882 - boolean = true -> null 1883 - string = "root" -> null 1884 1885 - nested_block { 1886 - string = "one" -> null 1887 } 1888 1889 - nested_block_two { 1890 - string = "two" -> null 1891 } 1892 }`, 1893 }, 1894 "list_block_update": { 1895 diff: computed.Diff{ 1896 Renderer: Block( 1897 nil, 1898 Blocks{ 1899 ListBlocks: map[string][]computed.Diff{ 1900 "list_blocks": { 1901 { 1902 Renderer: Block(map[string]computed.Diff{ 1903 "number": { 1904 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 1905 Action: plans.Update, 1906 }, 1907 "string": { 1908 Renderer: Primitive(nil, "new", cty.String), 1909 Action: plans.Create, 1910 }, 1911 }, Blocks{}), 1912 Action: plans.Update, 1913 }, 1914 { 1915 Renderer: Block(map[string]computed.Diff{ 1916 "number": { 1917 Renderer: Primitive(json.Number("1"), nil, cty.Number), 1918 Action: plans.Delete, 1919 }, 1920 "string": { 1921 Renderer: Primitive("old", "new", cty.String), 1922 Action: plans.Update, 1923 }, 1924 }, Blocks{}), 1925 Action: plans.Update, 1926 }, 1927 }, 1928 }, 1929 }), 1930 }, 1931 expected: ` 1932 { 1933 ~ list_blocks { 1934 ~ number = 1 -> 2 1935 + string = "new" 1936 } 1937 ~ list_blocks { 1938 - number = 1 -> null 1939 ~ string = "old" -> "new" 1940 } 1941 }`, 1942 }, 1943 "set_block_update": { 1944 diff: computed.Diff{ 1945 Renderer: Block( 1946 nil, 1947 Blocks{ 1948 SetBlocks: map[string][]computed.Diff{ 1949 "set_blocks": { 1950 { 1951 Renderer: Block(map[string]computed.Diff{ 1952 "number": { 1953 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 1954 Action: plans.Update, 1955 }, 1956 "string": { 1957 Renderer: Primitive(nil, "new", cty.String), 1958 Action: plans.Create, 1959 }, 1960 }, Blocks{}), 1961 Action: plans.Update, 1962 }, 1963 { 1964 Renderer: Block(map[string]computed.Diff{ 1965 "number": { 1966 Renderer: Primitive(json.Number("1"), nil, cty.Number), 1967 Action: plans.Delete, 1968 }, 1969 "string": { 1970 Renderer: Primitive("old", "new", cty.String), 1971 Action: plans.Update, 1972 }, 1973 }, Blocks{}), 1974 Action: plans.Update, 1975 }, 1976 }, 1977 }, 1978 }), 1979 }, 1980 expected: ` 1981 { 1982 ~ set_blocks { 1983 ~ number = 1 -> 2 1984 + string = "new" 1985 } 1986 ~ set_blocks { 1987 - number = 1 -> null 1988 ~ string = "old" -> "new" 1989 } 1990 }`, 1991 }, 1992 "map_block_update": { 1993 diff: computed.Diff{ 1994 Renderer: Block( 1995 nil, 1996 Blocks{ 1997 MapBlocks: map[string]map[string]computed.Diff{ 1998 "list_blocks": { 1999 "key_one": { 2000 Renderer: Block(map[string]computed.Diff{ 2001 "number": { 2002 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 2003 Action: plans.Update, 2004 }, 2005 "string": { 2006 Renderer: Primitive(nil, "new", cty.String), 2007 Action: plans.Create, 2008 }, 2009 }, Blocks{}), 2010 Action: plans.Update, 2011 }, 2012 "key:two": { 2013 Renderer: Block(map[string]computed.Diff{ 2014 "number": { 2015 Renderer: Primitive(json.Number("1"), nil, cty.Number), 2016 Action: plans.Delete, 2017 }, 2018 "string": { 2019 Renderer: Primitive("old", "new", cty.String), 2020 Action: plans.Update, 2021 }, 2022 }, Blocks{}), 2023 Action: plans.Update, 2024 }, 2025 }, 2026 }, 2027 }), 2028 }, 2029 expected: ` 2030 { 2031 ~ list_blocks "key:two" { 2032 - number = 1 -> null 2033 ~ string = "old" -> "new" 2034 } 2035 ~ list_blocks "key_one" { 2036 ~ number = 1 -> 2 2037 + string = "new" 2038 } 2039 } 2040 `, 2041 }, 2042 "sensitive_block": { 2043 diff: computed.Diff{ 2044 Renderer: SensitiveBlock(computed.Diff{ 2045 Renderer: Block(nil, Blocks{}), 2046 Action: plans.NoOp, 2047 }, true, true), 2048 Action: plans.Update, 2049 }, 2050 expected: ` 2051 { 2052 # At least one attribute in this block is (or was) sensitive, 2053 # so its contents will not be displayed. 2054 } 2055 `, 2056 }, 2057 "delete_empty_block": { 2058 diff: computed.Diff{ 2059 Renderer: Block(nil, Blocks{}), 2060 Action: plans.Delete, 2061 }, 2062 expected: "{}", 2063 }, 2064 "block_escapes_keys": { 2065 diff: computed.Diff{ 2066 Renderer: Block(map[string]computed.Diff{ 2067 "attribute_one": { 2068 Renderer: Primitive(json.Number("1"), json.Number("2"), cty.Number), 2069 Action: plans.Update, 2070 }, 2071 "attribute:two": { 2072 Renderer: Primitive(json.Number("2"), json.Number("3"), cty.Number), 2073 Action: plans.Update, 2074 }, 2075 "attribute_six": { 2076 Renderer: Primitive(json.Number("3"), json.Number("4"), cty.Number), 2077 Action: plans.Update, 2078 }, 2079 }, Blocks{ 2080 SingleBlocks: map[string]computed.Diff{ 2081 "nested_block:one": { 2082 Renderer: Block(map[string]computed.Diff{ 2083 "string": { 2084 Renderer: Primitive("one", "four", cty.String), 2085 Action: plans.Update, 2086 }, 2087 }, Blocks{}), 2088 Action: plans.Update, 2089 }, 2090 "nested_block_two": { 2091 Renderer: Block(map[string]computed.Diff{ 2092 "string": { 2093 Renderer: Primitive("two", "three", cty.String), 2094 Action: plans.Update, 2095 }, 2096 }, Blocks{}), 2097 Action: plans.Update, 2098 }, 2099 }, 2100 }), 2101 Action: plans.Update, 2102 }, 2103 expected: ` 2104 { 2105 ~ "attribute:two" = 2 -> 3 2106 ~ attribute_one = 1 -> 2 2107 ~ attribute_six = 3 -> 4 2108 2109 ~ "nested_block:one" { 2110 ~ string = "one" -> "four" 2111 } 2112 2113 ~ nested_block_two { 2114 ~ string = "two" -> "three" 2115 } 2116 }`, 2117 }, 2118 "block_always_includes_important_attributes": { 2119 diff: computed.Diff{ 2120 Renderer: Block(map[string]computed.Diff{ 2121 "id": { 2122 Renderer: Primitive("root", "root", cty.String), 2123 Action: plans.NoOp, 2124 }, 2125 "boolean": { 2126 Renderer: Primitive(false, false, cty.Bool), 2127 Action: plans.NoOp, 2128 }, 2129 }, Blocks{ 2130 SingleBlocks: map[string]computed.Diff{ 2131 "nested_block": { 2132 Renderer: Block(map[string]computed.Diff{ 2133 "string": { 2134 Renderer: Primitive("one", "one", cty.String), 2135 Action: plans.NoOp, 2136 }, 2137 }, Blocks{}), 2138 Action: plans.NoOp, 2139 }, 2140 "nested_block_two": { 2141 Renderer: Block(map[string]computed.Diff{ 2142 "string": { 2143 Renderer: Primitive("two", "two", cty.String), 2144 Action: plans.NoOp, 2145 }, 2146 }, Blocks{}), 2147 Action: plans.NoOp, 2148 }, 2149 }, 2150 }), 2151 Action: plans.NoOp, 2152 }, 2153 expected: ` 2154 { 2155 id = "root" 2156 # (1 unchanged attribute hidden) 2157 2158 # (2 unchanged blocks hidden) 2159 }`, 2160 }, 2161 "output_map_to_list": { 2162 diff: computed.Diff{ 2163 Renderer: TypeChange(computed.Diff{ 2164 Renderer: Map(map[string]computed.Diff{ 2165 "element_one": { 2166 Renderer: Primitive(json.Number("0"), nil, cty.Number), 2167 Action: plans.Delete, 2168 }, 2169 "element_two": { 2170 Renderer: Primitive(json.Number("1"), nil, cty.Number), 2171 Action: plans.Delete, 2172 }, 2173 }), 2174 Action: plans.Delete, 2175 }, computed.Diff{ 2176 Renderer: List([]computed.Diff{ 2177 { 2178 Renderer: Primitive(nil, json.Number("0"), cty.Number), 2179 Action: plans.Create, 2180 }, 2181 { 2182 Renderer: Primitive(nil, json.Number("1"), cty.Number), 2183 Action: plans.Create, 2184 }, 2185 }), 2186 Action: plans.Create, 2187 }), 2188 }, 2189 expected: ` 2190 { 2191 - "element_one" = 0 2192 - "element_two" = 1 2193 } -> [ 2194 + 0, 2195 + 1, 2196 ] 2197 `, 2198 }, 2199 "json_string_no_symbols": { 2200 diff: computed.Diff{ 2201 Renderer: Primitive("{\"key\":\"value\"}", "{\"key\":\"value\"}", cty.String), 2202 Action: plans.NoOp, 2203 }, 2204 opts: computed.RenderHumanOpts{ 2205 HideDiffActionSymbols: true, 2206 ShowUnchangedChildren: true, 2207 }, 2208 expected: ` 2209 jsonencode( 2210 { 2211 key = "value" 2212 } 2213 ) 2214 `, 2215 }, 2216 } 2217 for name, tc := range tcs { 2218 t.Run(name, func(t *testing.T) { 2219 2220 opts := tc.opts.Clone() 2221 opts.Colorize = &colorize 2222 2223 expected := strings.TrimSpace(tc.expected) 2224 actual := tc.diff.RenderHuman(0, opts) 2225 if diff := cmp.Diff(expected, actual); len(diff) > 0 { 2226 t.Fatalf("\nexpected:\n%s\nactual:\n%s\ndiff:\n%s\n", expected, actual, diff) 2227 } 2228 }) 2229 } 2230 }