github.com/scottwinkler/terraform@v0.11.6-0.20180329211809-05143987aea8/helper/schema/resource_diff_test.go (about) 1 package schema 2 3 import ( 4 "reflect" 5 "sort" 6 "testing" 7 8 "github.com/davecgh/go-spew/spew" 9 "github.com/hashicorp/terraform/terraform" 10 ) 11 12 // testSetFunc is a very simple function we use to test a foo/bar complex set. 13 // Both "foo" and "bar" are int values. 14 // 15 // This is not foolproof as since it performs sums, you can run into 16 // collisions. Spec tests accordingly. :P 17 func testSetFunc(v interface{}) int { 18 m := v.(map[string]interface{}) 19 return m["foo"].(int) + m["bar"].(int) 20 } 21 22 // resourceDiffTestCase provides a test case struct for SetNew and SetDiff. 23 type resourceDiffTestCase struct { 24 Name string 25 Schema map[string]*Schema 26 State *terraform.InstanceState 27 Config *terraform.ResourceConfig 28 Diff *terraform.InstanceDiff 29 Key string 30 OldValue interface{} 31 NewValue interface{} 32 Expected *terraform.InstanceDiff 33 ExpectedKeys []string 34 ExpectedError bool 35 } 36 37 // testDiffCases produces a list of test cases for use with SetNew and SetDiff. 38 func testDiffCases(t *testing.T, oldPrefix string, oldOffset int, computed bool) []resourceDiffTestCase { 39 return []resourceDiffTestCase{ 40 resourceDiffTestCase{ 41 Name: "basic primitive diff", 42 Schema: map[string]*Schema{ 43 "foo": &Schema{ 44 Type: TypeString, 45 Optional: true, 46 Computed: true, 47 }, 48 }, 49 State: &terraform.InstanceState{ 50 Attributes: map[string]string{ 51 "foo": "bar", 52 }, 53 }, 54 Config: testConfig(t, map[string]interface{}{ 55 "foo": "baz", 56 }), 57 Diff: &terraform.InstanceDiff{ 58 Attributes: map[string]*terraform.ResourceAttrDiff{ 59 "foo": &terraform.ResourceAttrDiff{ 60 Old: "bar", 61 New: "baz", 62 }, 63 }, 64 }, 65 Key: "foo", 66 NewValue: "qux", 67 Expected: &terraform.InstanceDiff{ 68 Attributes: map[string]*terraform.ResourceAttrDiff{ 69 "foo": &terraform.ResourceAttrDiff{ 70 Old: "bar", 71 New: func() string { 72 if computed { 73 return "" 74 } 75 return "qux" 76 }(), 77 NewComputed: computed, 78 }, 79 }, 80 }, 81 }, 82 resourceDiffTestCase{ 83 Name: "basic set diff", 84 Schema: map[string]*Schema{ 85 "foo": &Schema{ 86 Type: TypeSet, 87 Optional: true, 88 Computed: true, 89 Elem: &Schema{Type: TypeString}, 90 Set: HashString, 91 }, 92 }, 93 State: &terraform.InstanceState{ 94 Attributes: map[string]string{ 95 "foo.#": "1", 96 "foo.1996459178": "bar", 97 }, 98 }, 99 Config: testConfig(t, map[string]interface{}{ 100 "foo": []interface{}{"baz"}, 101 }), 102 Diff: &terraform.InstanceDiff{ 103 Attributes: map[string]*terraform.ResourceAttrDiff{ 104 "foo.1996459178": &terraform.ResourceAttrDiff{ 105 Old: "bar", 106 New: "", 107 NewRemoved: true, 108 }, 109 "foo.2015626392": &terraform.ResourceAttrDiff{ 110 Old: "", 111 New: "baz", 112 }, 113 }, 114 }, 115 Key: "foo", 116 NewValue: []interface{}{"qux"}, 117 Expected: &terraform.InstanceDiff{ 118 Attributes: func() map[string]*terraform.ResourceAttrDiff { 119 result := map[string]*terraform.ResourceAttrDiff{} 120 if computed { 121 result["foo.#"] = &terraform.ResourceAttrDiff{ 122 Old: "1", 123 New: "", 124 NewComputed: true, 125 } 126 } else { 127 result["foo.2800005064"] = &terraform.ResourceAttrDiff{ 128 Old: "", 129 New: "qux", 130 } 131 result["foo.1996459178"] = &terraform.ResourceAttrDiff{ 132 Old: "bar", 133 New: "", 134 NewRemoved: true, 135 } 136 } 137 return result 138 }(), 139 }, 140 }, 141 resourceDiffTestCase{ 142 Name: "basic list diff", 143 Schema: map[string]*Schema{ 144 "foo": &Schema{ 145 Type: TypeList, 146 Optional: true, 147 Computed: true, 148 Elem: &Schema{Type: TypeString}, 149 }, 150 }, 151 State: &terraform.InstanceState{ 152 Attributes: map[string]string{ 153 "foo.#": "1", 154 "foo.0": "bar", 155 }, 156 }, 157 Config: testConfig(t, map[string]interface{}{ 158 "foo": []interface{}{"baz"}, 159 }), 160 Diff: &terraform.InstanceDiff{ 161 Attributes: map[string]*terraform.ResourceAttrDiff{ 162 "foo.0": &terraform.ResourceAttrDiff{ 163 Old: "bar", 164 New: "baz", 165 }, 166 }, 167 }, 168 Key: "foo", 169 NewValue: []interface{}{"qux"}, 170 Expected: &terraform.InstanceDiff{ 171 Attributes: func() map[string]*terraform.ResourceAttrDiff { 172 result := make(map[string]*terraform.ResourceAttrDiff) 173 if computed { 174 result["foo.#"] = &terraform.ResourceAttrDiff{ 175 Old: "1", 176 New: "", 177 NewComputed: true, 178 } 179 } else { 180 result["foo.0"] = &terraform.ResourceAttrDiff{ 181 Old: "bar", 182 New: "qux", 183 } 184 } 185 return result 186 }(), 187 }, 188 }, 189 resourceDiffTestCase{ 190 Name: "basic map diff", 191 Schema: map[string]*Schema{ 192 "foo": &Schema{ 193 Type: TypeMap, 194 Optional: true, 195 Computed: true, 196 }, 197 }, 198 State: &terraform.InstanceState{ 199 Attributes: map[string]string{ 200 "foo.%": "1", 201 "foo.bar": "baz", 202 }, 203 }, 204 Config: testConfig(t, map[string]interface{}{ 205 "foo": map[string]interface{}{"bar": "qux"}, 206 }), 207 Diff: &terraform.InstanceDiff{ 208 Attributes: map[string]*terraform.ResourceAttrDiff{ 209 "foo.bar": &terraform.ResourceAttrDiff{ 210 Old: "baz", 211 New: "qux", 212 }, 213 }, 214 }, 215 Key: "foo", 216 NewValue: map[string]interface{}{"bar": "quux"}, 217 Expected: &terraform.InstanceDiff{ 218 Attributes: func() map[string]*terraform.ResourceAttrDiff { 219 result := make(map[string]*terraform.ResourceAttrDiff) 220 if computed { 221 result["foo.%"] = &terraform.ResourceAttrDiff{ 222 Old: "", 223 New: "", 224 NewComputed: true, 225 } 226 result["foo.bar"] = &terraform.ResourceAttrDiff{ 227 Old: "baz", 228 New: "", 229 NewRemoved: true, 230 } 231 } else { 232 result["foo.bar"] = &terraform.ResourceAttrDiff{ 233 Old: "baz", 234 New: "quux", 235 } 236 } 237 return result 238 }(), 239 }, 240 }, 241 resourceDiffTestCase{ 242 Name: "additional diff with primitive", 243 Schema: map[string]*Schema{ 244 "foo": &Schema{ 245 Type: TypeString, 246 Optional: true, 247 }, 248 "one": &Schema{ 249 Type: TypeString, 250 Optional: true, 251 Computed: true, 252 }, 253 }, 254 State: &terraform.InstanceState{ 255 Attributes: map[string]string{ 256 "foo": "bar", 257 "one": "two", 258 }, 259 }, 260 Config: testConfig(t, map[string]interface{}{ 261 "foo": "baz", 262 }), 263 Diff: &terraform.InstanceDiff{ 264 Attributes: map[string]*terraform.ResourceAttrDiff{ 265 "foo": &terraform.ResourceAttrDiff{ 266 Old: "bar", 267 New: "baz", 268 }, 269 }, 270 }, 271 Key: "one", 272 NewValue: "four", 273 Expected: &terraform.InstanceDiff{ 274 Attributes: map[string]*terraform.ResourceAttrDiff{ 275 "foo": &terraform.ResourceAttrDiff{ 276 Old: "bar", 277 New: "baz", 278 }, 279 "one": &terraform.ResourceAttrDiff{ 280 Old: "two", 281 New: func() string { 282 if computed { 283 return "" 284 } 285 return "four" 286 }(), 287 NewComputed: computed, 288 }, 289 }, 290 }, 291 }, 292 resourceDiffTestCase{ 293 Name: "additional diff with primitive computed only", 294 Schema: map[string]*Schema{ 295 "foo": &Schema{ 296 Type: TypeString, 297 Optional: true, 298 }, 299 "one": &Schema{ 300 Type: TypeString, 301 Computed: true, 302 }, 303 }, 304 State: &terraform.InstanceState{ 305 Attributes: map[string]string{ 306 "foo": "bar", 307 "one": "two", 308 }, 309 }, 310 Config: testConfig(t, map[string]interface{}{ 311 "foo": "baz", 312 }), 313 Diff: &terraform.InstanceDiff{ 314 Attributes: map[string]*terraform.ResourceAttrDiff{ 315 "foo": &terraform.ResourceAttrDiff{ 316 Old: "bar", 317 New: "baz", 318 }, 319 }, 320 }, 321 Key: "one", 322 NewValue: "three", 323 Expected: &terraform.InstanceDiff{ 324 Attributes: map[string]*terraform.ResourceAttrDiff{ 325 "foo": &terraform.ResourceAttrDiff{ 326 Old: "bar", 327 New: "baz", 328 }, 329 "one": &terraform.ResourceAttrDiff{ 330 Old: "two", 331 New: func() string { 332 if computed { 333 return "" 334 } 335 return "three" 336 }(), 337 NewComputed: computed, 338 }, 339 }, 340 }, 341 }, 342 resourceDiffTestCase{ 343 Name: "complex-ish set diff", 344 Schema: map[string]*Schema{ 345 "top": &Schema{ 346 Type: TypeSet, 347 Optional: true, 348 Computed: true, 349 Elem: &Resource{ 350 Schema: map[string]*Schema{ 351 "foo": &Schema{ 352 Type: TypeInt, 353 Optional: true, 354 Computed: true, 355 }, 356 "bar": &Schema{ 357 Type: TypeInt, 358 Optional: true, 359 Computed: true, 360 }, 361 }, 362 }, 363 Set: testSetFunc, 364 }, 365 }, 366 State: &terraform.InstanceState{ 367 Attributes: map[string]string{ 368 "top.#": "2", 369 "top.3.foo": "1", 370 "top.3.bar": "2", 371 "top.23.foo": "11", 372 "top.23.bar": "12", 373 }, 374 }, 375 Config: testConfig(t, map[string]interface{}{ 376 "top": []interface{}{ 377 map[string]interface{}{ 378 "foo": 1, 379 "bar": 3, 380 }, 381 map[string]interface{}{ 382 "foo": 12, 383 "bar": 12, 384 }, 385 }, 386 }), 387 Diff: &terraform.InstanceDiff{ 388 Attributes: map[string]*terraform.ResourceAttrDiff{ 389 "top.4.foo": &terraform.ResourceAttrDiff{ 390 Old: "", 391 New: "1", 392 }, 393 "top.4.bar": &terraform.ResourceAttrDiff{ 394 Old: "", 395 New: "3", 396 }, 397 "top.24.foo": &terraform.ResourceAttrDiff{ 398 Old: "", 399 New: "12", 400 }, 401 "top.24.bar": &terraform.ResourceAttrDiff{ 402 Old: "", 403 New: "12", 404 }, 405 }, 406 }, 407 Key: "top", 408 NewValue: NewSet(testSetFunc, []interface{}{ 409 map[string]interface{}{ 410 "foo": 1, 411 "bar": 4, 412 }, 413 map[string]interface{}{ 414 "foo": 13, 415 "bar": 12, 416 }, 417 map[string]interface{}{ 418 "foo": 21, 419 "bar": 22, 420 }, 421 }), 422 Expected: &terraform.InstanceDiff{ 423 Attributes: func() map[string]*terraform.ResourceAttrDiff { 424 result := make(map[string]*terraform.ResourceAttrDiff) 425 if computed { 426 result["top.#"] = &terraform.ResourceAttrDiff{ 427 Old: "2", 428 New: "", 429 NewComputed: true, 430 } 431 } else { 432 result["top.#"] = &terraform.ResourceAttrDiff{ 433 Old: "2", 434 New: "3", 435 } 436 result["top.5.foo"] = &terraform.ResourceAttrDiff{ 437 Old: "", 438 New: "1", 439 } 440 result["top.5.bar"] = &terraform.ResourceAttrDiff{ 441 Old: "", 442 New: "4", 443 } 444 result["top.25.foo"] = &terraform.ResourceAttrDiff{ 445 Old: "", 446 New: "13", 447 } 448 result["top.25.bar"] = &terraform.ResourceAttrDiff{ 449 Old: "", 450 New: "12", 451 } 452 result["top.43.foo"] = &terraform.ResourceAttrDiff{ 453 Old: "", 454 New: "21", 455 } 456 result["top.43.bar"] = &terraform.ResourceAttrDiff{ 457 Old: "", 458 New: "22", 459 } 460 } 461 return result 462 }(), 463 }, 464 }, 465 resourceDiffTestCase{ 466 Name: "primitive, no diff, no refresh", 467 Schema: map[string]*Schema{ 468 "foo": &Schema{ 469 Type: TypeString, 470 Computed: true, 471 }, 472 }, 473 State: &terraform.InstanceState{ 474 Attributes: map[string]string{ 475 "foo": "bar", 476 }, 477 }, 478 Config: testConfig(t, map[string]interface{}{}), 479 Diff: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, 480 Key: "foo", 481 NewValue: "baz", 482 Expected: &terraform.InstanceDiff{ 483 Attributes: map[string]*terraform.ResourceAttrDiff{ 484 "foo": &terraform.ResourceAttrDiff{ 485 Old: "bar", 486 New: func() string { 487 if computed { 488 return "" 489 } 490 return "baz" 491 }(), 492 NewComputed: computed, 493 }, 494 }, 495 }, 496 }, 497 resourceDiffTestCase{ 498 Name: "non-computed key, should error", 499 Schema: map[string]*Schema{ 500 "foo": &Schema{ 501 Type: TypeString, 502 Required: true, 503 }, 504 }, 505 State: &terraform.InstanceState{ 506 Attributes: map[string]string{ 507 "foo": "bar", 508 }, 509 }, 510 Config: testConfig(t, map[string]interface{}{ 511 "foo": "baz", 512 }), 513 Diff: &terraform.InstanceDiff{ 514 Attributes: map[string]*terraform.ResourceAttrDiff{ 515 "foo": &terraform.ResourceAttrDiff{ 516 Old: "bar", 517 New: "baz", 518 }, 519 }, 520 }, 521 Key: "foo", 522 NewValue: "qux", 523 ExpectedError: true, 524 }, 525 resourceDiffTestCase{ 526 Name: "bad key, should error", 527 Schema: map[string]*Schema{ 528 "foo": &Schema{ 529 Type: TypeString, 530 Required: true, 531 }, 532 }, 533 State: &terraform.InstanceState{ 534 Attributes: map[string]string{ 535 "foo": "bar", 536 }, 537 }, 538 Config: testConfig(t, map[string]interface{}{ 539 "foo": "baz", 540 }), 541 Diff: &terraform.InstanceDiff{ 542 Attributes: map[string]*terraform.ResourceAttrDiff{ 543 "foo": &terraform.ResourceAttrDiff{ 544 Old: "bar", 545 New: "baz", 546 }, 547 }, 548 }, 549 Key: "bad", 550 NewValue: "qux", 551 ExpectedError: true, 552 }, 553 } 554 } 555 556 func TestSetNew(t *testing.T) { 557 testCases := testDiffCases(t, "", 0, false) 558 for _, tc := range testCases { 559 t.Run(tc.Name, func(t *testing.T) { 560 m := schemaMap(tc.Schema) 561 d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) 562 err := d.SetNew(tc.Key, tc.NewValue) 563 switch { 564 case err != nil && !tc.ExpectedError: 565 t.Fatalf("bad: %s", err) 566 case err == nil && tc.ExpectedError: 567 t.Fatalf("Expected error, got none") 568 case err != nil && tc.ExpectedError: 569 return 570 } 571 for _, k := range d.UpdatedKeys() { 572 if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { 573 t.Fatalf("bad: %s", err) 574 } 575 } 576 if !reflect.DeepEqual(tc.Expected, tc.Diff) { 577 t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) 578 } 579 }) 580 } 581 } 582 583 func TestSetNewComputed(t *testing.T) { 584 testCases := testDiffCases(t, "", 0, true) 585 for _, tc := range testCases { 586 t.Run(tc.Name, func(t *testing.T) { 587 m := schemaMap(tc.Schema) 588 d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) 589 err := d.SetNewComputed(tc.Key) 590 switch { 591 case err != nil && !tc.ExpectedError: 592 t.Fatalf("bad: %s", err) 593 case err == nil && tc.ExpectedError: 594 t.Fatalf("Expected error, got none") 595 case err != nil && tc.ExpectedError: 596 return 597 } 598 for _, k := range d.UpdatedKeys() { 599 if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { 600 t.Fatalf("bad: %s", err) 601 } 602 } 603 if !reflect.DeepEqual(tc.Expected, tc.Diff) { 604 t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) 605 } 606 }) 607 } 608 } 609 610 func TestForceNew(t *testing.T) { 611 cases := []resourceDiffTestCase{ 612 resourceDiffTestCase{ 613 Name: "basic primitive diff", 614 Schema: map[string]*Schema{ 615 "foo": &Schema{ 616 Type: TypeString, 617 Optional: true, 618 Computed: true, 619 }, 620 }, 621 State: &terraform.InstanceState{ 622 Attributes: map[string]string{ 623 "foo": "bar", 624 }, 625 }, 626 Config: testConfig(t, map[string]interface{}{ 627 "foo": "baz", 628 }), 629 Diff: &terraform.InstanceDiff{ 630 Attributes: map[string]*terraform.ResourceAttrDiff{ 631 "foo": &terraform.ResourceAttrDiff{ 632 Old: "bar", 633 New: "baz", 634 }, 635 }, 636 }, 637 Key: "foo", 638 Expected: &terraform.InstanceDiff{ 639 Attributes: map[string]*terraform.ResourceAttrDiff{ 640 "foo": &terraform.ResourceAttrDiff{ 641 Old: "bar", 642 New: "baz", 643 RequiresNew: true, 644 }, 645 }, 646 }, 647 }, 648 resourceDiffTestCase{ 649 Name: "no change, should error", 650 Schema: map[string]*Schema{ 651 "foo": &Schema{ 652 Type: TypeString, 653 Optional: true, 654 Computed: true, 655 }, 656 }, 657 State: &terraform.InstanceState{ 658 Attributes: map[string]string{ 659 "foo": "bar", 660 }, 661 }, 662 Config: testConfig(t, map[string]interface{}{ 663 "foo": "bar", 664 }), 665 ExpectedError: true, 666 }, 667 resourceDiffTestCase{ 668 Name: "basic primitive, non-computed key", 669 Schema: map[string]*Schema{ 670 "foo": &Schema{ 671 Type: TypeString, 672 Required: true, 673 }, 674 }, 675 State: &terraform.InstanceState{ 676 Attributes: map[string]string{ 677 "foo": "bar", 678 }, 679 }, 680 Config: testConfig(t, map[string]interface{}{ 681 "foo": "baz", 682 }), 683 Diff: &terraform.InstanceDiff{ 684 Attributes: map[string]*terraform.ResourceAttrDiff{ 685 "foo": &terraform.ResourceAttrDiff{ 686 Old: "bar", 687 New: "baz", 688 }, 689 }, 690 }, 691 Key: "foo", 692 Expected: &terraform.InstanceDiff{ 693 Attributes: map[string]*terraform.ResourceAttrDiff{ 694 "foo": &terraform.ResourceAttrDiff{ 695 Old: "bar", 696 New: "baz", 697 RequiresNew: true, 698 }, 699 }, 700 }, 701 }, 702 resourceDiffTestCase{ 703 Name: "nested field", 704 Schema: map[string]*Schema{ 705 "foo": &Schema{ 706 Type: TypeList, 707 Required: true, 708 MaxItems: 1, 709 Elem: &Resource{ 710 Schema: map[string]*Schema{ 711 "bar": { 712 Type: TypeString, 713 Optional: true, 714 }, 715 "baz": { 716 Type: TypeString, 717 Optional: true, 718 }, 719 }, 720 }, 721 }, 722 }, 723 State: &terraform.InstanceState{ 724 Attributes: map[string]string{ 725 "foo.#": "1", 726 "foo.0.bar": "abc", 727 "foo.0.baz": "xyz", 728 }, 729 }, 730 Config: testConfig(t, map[string]interface{}{ 731 "foo": []map[string]interface{}{ 732 map[string]interface{}{ 733 "bar": "abcdefg", 734 "baz": "changed", 735 }, 736 }, 737 }), 738 Diff: &terraform.InstanceDiff{ 739 Attributes: map[string]*terraform.ResourceAttrDiff{ 740 "foo.0.bar": &terraform.ResourceAttrDiff{ 741 Old: "abc", 742 New: "abcdefg", 743 }, 744 "foo.0.baz": &terraform.ResourceAttrDiff{ 745 Old: "xyz", 746 New: "changed", 747 }, 748 }, 749 }, 750 Key: "foo.0.baz", 751 Expected: &terraform.InstanceDiff{ 752 Attributes: map[string]*terraform.ResourceAttrDiff{ 753 "foo.0.bar": &terraform.ResourceAttrDiff{ 754 Old: "abc", 755 New: "abcdefg", 756 }, 757 "foo.0.baz": &terraform.ResourceAttrDiff{ 758 Old: "xyz", 759 New: "changed", 760 RequiresNew: true, 761 }, 762 }, 763 }, 764 }, 765 } 766 for _, tc := range cases { 767 t.Run(tc.Name, func(t *testing.T) { 768 m := schemaMap(tc.Schema) 769 d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) 770 err := d.ForceNew(tc.Key) 771 switch { 772 case err != nil && !tc.ExpectedError: 773 t.Fatalf("bad: %s", err) 774 case err == nil && tc.ExpectedError: 775 t.Fatalf("Expected error, got none") 776 case err != nil && tc.ExpectedError: 777 return 778 } 779 for _, k := range d.UpdatedKeys() { 780 if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { 781 t.Fatalf("bad: %s", err) 782 } 783 } 784 if !reflect.DeepEqual(tc.Expected, tc.Diff) { 785 t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) 786 } 787 }) 788 } 789 } 790 791 func TestClear(t *testing.T) { 792 cases := []resourceDiffTestCase{ 793 resourceDiffTestCase{ 794 Name: "basic primitive diff", 795 Schema: map[string]*Schema{ 796 "foo": &Schema{ 797 Type: TypeString, 798 Optional: true, 799 Computed: true, 800 }, 801 }, 802 State: &terraform.InstanceState{ 803 Attributes: map[string]string{ 804 "foo": "bar", 805 }, 806 }, 807 Config: testConfig(t, map[string]interface{}{ 808 "foo": "baz", 809 }), 810 Diff: &terraform.InstanceDiff{ 811 Attributes: map[string]*terraform.ResourceAttrDiff{ 812 "foo": &terraform.ResourceAttrDiff{ 813 Old: "bar", 814 New: "baz", 815 }, 816 }, 817 }, 818 Key: "foo", 819 Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, 820 }, 821 resourceDiffTestCase{ 822 Name: "non-computed key, should error", 823 Schema: map[string]*Schema{ 824 "foo": &Schema{ 825 Type: TypeString, 826 Required: true, 827 }, 828 }, 829 State: &terraform.InstanceState{ 830 Attributes: map[string]string{ 831 "foo": "bar", 832 }, 833 }, 834 Config: testConfig(t, map[string]interface{}{ 835 "foo": "baz", 836 }), 837 Diff: &terraform.InstanceDiff{ 838 Attributes: map[string]*terraform.ResourceAttrDiff{ 839 "foo": &terraform.ResourceAttrDiff{ 840 Old: "bar", 841 New: "baz", 842 }, 843 }, 844 }, 845 Key: "foo", 846 ExpectedError: true, 847 }, 848 resourceDiffTestCase{ 849 Name: "multi-value, one removed", 850 Schema: map[string]*Schema{ 851 "foo": &Schema{ 852 Type: TypeString, 853 Optional: true, 854 Computed: true, 855 }, 856 "one": &Schema{ 857 Type: TypeString, 858 Optional: true, 859 Computed: true, 860 }, 861 }, 862 State: &terraform.InstanceState{ 863 Attributes: map[string]string{ 864 "foo": "bar", 865 "one": "two", 866 }, 867 }, 868 Config: testConfig(t, map[string]interface{}{ 869 "foo": "baz", 870 "one": "three", 871 }), 872 Diff: &terraform.InstanceDiff{ 873 Attributes: map[string]*terraform.ResourceAttrDiff{ 874 "foo": &terraform.ResourceAttrDiff{ 875 Old: "bar", 876 New: "baz", 877 }, 878 "one": &terraform.ResourceAttrDiff{ 879 Old: "two", 880 New: "three", 881 }, 882 }, 883 }, 884 Key: "one", 885 Expected: &terraform.InstanceDiff{ 886 Attributes: map[string]*terraform.ResourceAttrDiff{ 887 "foo": &terraform.ResourceAttrDiff{ 888 Old: "bar", 889 New: "baz", 890 }, 891 }, 892 }, 893 }, 894 } 895 for _, tc := range cases { 896 t.Run(tc.Name, func(t *testing.T) { 897 m := schemaMap(tc.Schema) 898 d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) 899 err := d.Clear(tc.Key) 900 switch { 901 case err != nil && !tc.ExpectedError: 902 t.Fatalf("bad: %s", err) 903 case err == nil && tc.ExpectedError: 904 t.Fatalf("Expected error, got none") 905 case err != nil && tc.ExpectedError: 906 return 907 } 908 for _, k := range d.UpdatedKeys() { 909 if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { 910 t.Fatalf("bad: %s", err) 911 } 912 } 913 if !reflect.DeepEqual(tc.Expected, tc.Diff) { 914 t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) 915 } 916 }) 917 } 918 } 919 920 func TestGetChangedKeysPrefix(t *testing.T) { 921 cases := []resourceDiffTestCase{ 922 resourceDiffTestCase{ 923 Name: "basic primitive diff", 924 Schema: map[string]*Schema{ 925 "foo": &Schema{ 926 Type: TypeString, 927 Optional: true, 928 Computed: true, 929 }, 930 }, 931 State: &terraform.InstanceState{ 932 Attributes: map[string]string{ 933 "foo": "bar", 934 }, 935 }, 936 Config: testConfig(t, map[string]interface{}{ 937 "foo": "baz", 938 }), 939 Diff: &terraform.InstanceDiff{ 940 Attributes: map[string]*terraform.ResourceAttrDiff{ 941 "foo": &terraform.ResourceAttrDiff{ 942 Old: "bar", 943 New: "baz", 944 }, 945 }, 946 }, 947 Key: "foo", 948 ExpectedKeys: []string{ 949 "foo", 950 }, 951 }, 952 resourceDiffTestCase{ 953 Name: "nested field filtering", 954 Schema: map[string]*Schema{ 955 "testfield": &Schema{ 956 Type: TypeString, 957 Required: true, 958 }, 959 "foo": &Schema{ 960 Type: TypeList, 961 Required: true, 962 MaxItems: 1, 963 Elem: &Resource{ 964 Schema: map[string]*Schema{ 965 "bar": { 966 Type: TypeString, 967 Optional: true, 968 }, 969 "baz": { 970 Type: TypeString, 971 Optional: true, 972 }, 973 }, 974 }, 975 }, 976 }, 977 State: &terraform.InstanceState{ 978 Attributes: map[string]string{ 979 "testfield": "blablah", 980 "foo.#": "1", 981 "foo.0.bar": "abc", 982 "foo.0.baz": "xyz", 983 }, 984 }, 985 Config: testConfig(t, map[string]interface{}{ 986 "testfield": "modified", 987 "foo": []map[string]interface{}{ 988 map[string]interface{}{ 989 "bar": "abcdefg", 990 "baz": "changed", 991 }, 992 }, 993 }), 994 Diff: &terraform.InstanceDiff{ 995 Attributes: map[string]*terraform.ResourceAttrDiff{ 996 "testfield": &terraform.ResourceAttrDiff{ 997 Old: "blablah", 998 New: "modified", 999 }, 1000 "foo.0.bar": &terraform.ResourceAttrDiff{ 1001 Old: "abc", 1002 New: "abcdefg", 1003 }, 1004 "foo.0.baz": &terraform.ResourceAttrDiff{ 1005 Old: "xyz", 1006 New: "changed", 1007 }, 1008 }, 1009 }, 1010 Key: "foo", 1011 ExpectedKeys: []string{ 1012 "foo.0.bar", 1013 "foo.0.baz", 1014 }, 1015 }, 1016 } 1017 for _, tc := range cases { 1018 t.Run(tc.Name, func(t *testing.T) { 1019 m := schemaMap(tc.Schema) 1020 d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) 1021 keys := d.GetChangedKeysPrefix(tc.Key) 1022 1023 for _, k := range d.UpdatedKeys() { 1024 if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { 1025 t.Fatalf("bad: %s", err) 1026 } 1027 } 1028 1029 sort.Strings(keys) 1030 1031 if !reflect.DeepEqual(tc.ExpectedKeys, keys) { 1032 t.Fatalf("Expected %s, got %s", spew.Sdump(tc.ExpectedKeys), spew.Sdump(keys)) 1033 } 1034 }) 1035 } 1036 }