github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/resource_address_test.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 8 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 10 ) 11 12 func TestParseResourceAddressInternal(t *testing.T) { 13 cases := map[string]struct { 14 Input string 15 Expected *ResourceAddress 16 Output string 17 }{ 18 "basic resource": { 19 "aws_instance.foo", 20 &ResourceAddress{ 21 Mode: ManagedResourceMode, 22 Type: "aws_instance", 23 Name: "foo", 24 InstanceType: TypePrimary, 25 Index: -1, 26 }, 27 "aws_instance.foo", 28 }, 29 30 "basic resource with count": { 31 "aws_instance.foo.1", 32 &ResourceAddress{ 33 Mode: ManagedResourceMode, 34 Type: "aws_instance", 35 Name: "foo", 36 InstanceType: TypePrimary, 37 Index: 1, 38 }, 39 "aws_instance.foo[1]", 40 }, 41 42 "data resource": { 43 "data.aws_ami.foo", 44 &ResourceAddress{ 45 Mode: DataResourceMode, 46 Type: "aws_ami", 47 Name: "foo", 48 InstanceType: TypePrimary, 49 Index: -1, 50 }, 51 "data.aws_ami.foo", 52 }, 53 54 "data resource with count": { 55 "data.aws_ami.foo.1", 56 &ResourceAddress{ 57 Mode: DataResourceMode, 58 Type: "aws_ami", 59 Name: "foo", 60 InstanceType: TypePrimary, 61 Index: 1, 62 }, 63 "data.aws_ami.foo[1]", 64 }, 65 66 "non-data resource with 4 elements": { 67 "aws_instance.foo.bar.1", 68 nil, 69 "", 70 }, 71 } 72 73 for tn, tc := range cases { 74 t.Run(tc.Input, func(t *testing.T) { 75 out, err := parseResourceAddressInternal(tc.Input) 76 if (err != nil) != (tc.Expected == nil) { 77 t.Fatalf("%s: unexpected err: %#v", tn, err) 78 } 79 if err != nil { 80 return 81 } 82 83 if !reflect.DeepEqual(out, tc.Expected) { 84 t.Fatalf("bad: %q\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Expected, out) 85 } 86 87 // Compare outputs if those exist 88 expected := tc.Input 89 if tc.Output != "" { 90 expected = tc.Output 91 } 92 if out.String() != expected { 93 t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, expected, out) 94 } 95 96 // Compare equality because the internal parse is used 97 // to compare equality to equal inputs. 98 if !out.Equals(tc.Expected) { 99 t.Fatalf("expected equality:\n\n%#v\n\n%#v", out, tc.Expected) 100 } 101 }) 102 } 103 } 104 105 func TestParseResourceAddress(t *testing.T) { 106 cases := map[string]struct { 107 Input string 108 Expected *ResourceAddress 109 Output string 110 Err bool 111 }{ 112 "implicit primary managed instance, no specific index": { 113 "aws_instance.foo", 114 &ResourceAddress{ 115 Mode: ManagedResourceMode, 116 Type: "aws_instance", 117 Name: "foo", 118 InstanceType: TypePrimary, 119 Index: -1, 120 }, 121 "", 122 false, 123 }, 124 "implicit primary data instance, no specific index": { 125 "data.aws_instance.foo", 126 &ResourceAddress{ 127 Mode: DataResourceMode, 128 Type: "aws_instance", 129 Name: "foo", 130 InstanceType: TypePrimary, 131 Index: -1, 132 }, 133 "", 134 false, 135 }, 136 "implicit primary, explicit index": { 137 "aws_instance.foo[2]", 138 &ResourceAddress{ 139 Mode: ManagedResourceMode, 140 Type: "aws_instance", 141 Name: "foo", 142 InstanceType: TypePrimary, 143 Index: 2, 144 }, 145 "", 146 false, 147 }, 148 "implicit primary, explicit index over ten": { 149 "aws_instance.foo[12]", 150 &ResourceAddress{ 151 Mode: ManagedResourceMode, 152 Type: "aws_instance", 153 Name: "foo", 154 InstanceType: TypePrimary, 155 Index: 12, 156 }, 157 "", 158 false, 159 }, 160 "explicit primary, explicit index": { 161 "aws_instance.foo.primary[2]", 162 &ResourceAddress{ 163 Mode: ManagedResourceMode, 164 Type: "aws_instance", 165 Name: "foo", 166 InstanceType: TypePrimary, 167 InstanceTypeSet: true, 168 Index: 2, 169 }, 170 "", 171 false, 172 }, 173 "tainted": { 174 "aws_instance.foo.tainted", 175 &ResourceAddress{ 176 Mode: ManagedResourceMode, 177 Type: "aws_instance", 178 Name: "foo", 179 InstanceType: TypeTainted, 180 InstanceTypeSet: true, 181 Index: -1, 182 }, 183 "", 184 false, 185 }, 186 "deposed": { 187 "aws_instance.foo.deposed", 188 &ResourceAddress{ 189 Mode: ManagedResourceMode, 190 Type: "aws_instance", 191 Name: "foo", 192 InstanceType: TypeDeposed, 193 InstanceTypeSet: true, 194 Index: -1, 195 }, 196 "", 197 false, 198 }, 199 "with a hyphen": { 200 "aws_instance.foo-bar", 201 &ResourceAddress{ 202 Mode: ManagedResourceMode, 203 Type: "aws_instance", 204 Name: "foo-bar", 205 InstanceType: TypePrimary, 206 Index: -1, 207 }, 208 "", 209 false, 210 }, 211 "managed in a module": { 212 "module.child.aws_instance.foo", 213 &ResourceAddress{ 214 Path: []string{"child"}, 215 Mode: ManagedResourceMode, 216 Type: "aws_instance", 217 Name: "foo", 218 InstanceType: TypePrimary, 219 Index: -1, 220 }, 221 "", 222 false, 223 }, 224 "data in a module": { 225 "module.child.data.aws_instance.foo", 226 &ResourceAddress{ 227 Path: []string{"child"}, 228 Mode: DataResourceMode, 229 Type: "aws_instance", 230 Name: "foo", 231 InstanceType: TypePrimary, 232 Index: -1, 233 }, 234 "", 235 false, 236 }, 237 "nested modules": { 238 "module.a.module.b.module.forever.aws_instance.foo", 239 &ResourceAddress{ 240 Path: []string{"a", "b", "forever"}, 241 Mode: ManagedResourceMode, 242 Type: "aws_instance", 243 Name: "foo", 244 InstanceType: TypePrimary, 245 Index: -1, 246 }, 247 "", 248 false, 249 }, 250 "just a module": { 251 "module.a", 252 &ResourceAddress{ 253 Path: []string{"a"}, 254 Type: "", 255 Name: "", 256 InstanceType: TypePrimary, 257 Index: -1, 258 }, 259 "", 260 false, 261 }, 262 "just a nested module": { 263 "module.a.module.b", 264 &ResourceAddress{ 265 Path: []string{"a", "b"}, 266 Type: "", 267 Name: "", 268 InstanceType: TypePrimary, 269 Index: -1, 270 }, 271 "", 272 false, 273 }, 274 "module missing resource type": { 275 "module.name.foo", 276 nil, 277 "", 278 true, 279 }, 280 } 281 282 for tn, tc := range cases { 283 t.Run(tn, func(t *testing.T) { 284 out, err := ParseResourceAddress(tc.Input) 285 if (err != nil) != tc.Err { 286 t.Fatalf("%s: unexpected err: %#v", tn, err) 287 } 288 if tc.Err { 289 return 290 } 291 292 if !reflect.DeepEqual(out, tc.Expected) { 293 t.Fatalf("bad: %q\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Expected, out) 294 } 295 296 expected := tc.Input 297 if tc.Output != "" { 298 expected = tc.Output 299 } 300 if out.String() != expected { 301 t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, expected, out) 302 } 303 }) 304 } 305 } 306 307 func TestResourceAddressContains(t *testing.T) { 308 tests := []struct { 309 Address *ResourceAddress 310 Other *ResourceAddress 311 Want bool 312 }{ 313 { 314 &ResourceAddress{ 315 Mode: ManagedResourceMode, 316 Type: "aws_instance", 317 Name: "foo", 318 InstanceTypeSet: true, 319 InstanceType: TypePrimary, 320 Index: 0, 321 }, 322 &ResourceAddress{ 323 Mode: ManagedResourceMode, 324 Type: "aws_instance", 325 Name: "foo", 326 InstanceTypeSet: true, 327 InstanceType: TypePrimary, 328 Index: 0, 329 }, 330 true, 331 }, 332 { 333 &ResourceAddress{ 334 Mode: ManagedResourceMode, 335 Type: "aws_instance", 336 Name: "foo", 337 InstanceTypeSet: false, 338 Index: 0, 339 }, 340 &ResourceAddress{ 341 Mode: ManagedResourceMode, 342 Type: "aws_instance", 343 Name: "foo", 344 InstanceTypeSet: true, 345 InstanceType: TypePrimary, 346 Index: 0, 347 }, 348 true, 349 }, 350 { 351 &ResourceAddress{ 352 Mode: ManagedResourceMode, 353 Type: "aws_instance", 354 Name: "foo", 355 InstanceTypeSet: false, 356 Index: -1, 357 }, 358 &ResourceAddress{ 359 Mode: ManagedResourceMode, 360 Type: "aws_instance", 361 Name: "foo", 362 InstanceTypeSet: true, 363 InstanceType: TypePrimary, 364 Index: 0, 365 }, 366 true, 367 }, 368 { 369 &ResourceAddress{ 370 Mode: ManagedResourceMode, 371 Type: "aws_instance", 372 Name: "foo", 373 InstanceTypeSet: false, 374 Index: -1, 375 }, 376 &ResourceAddress{ 377 Mode: ManagedResourceMode, 378 Type: "aws_instance", 379 Name: "foo", 380 InstanceTypeSet: false, 381 Index: -1, 382 }, 383 true, 384 }, 385 { 386 &ResourceAddress{ 387 InstanceTypeSet: false, 388 Index: -1, 389 }, 390 &ResourceAddress{ 391 Mode: ManagedResourceMode, 392 Type: "aws_instance", 393 Name: "foo", 394 InstanceTypeSet: false, 395 Index: -1, 396 }, 397 true, 398 }, 399 { 400 &ResourceAddress{ 401 InstanceTypeSet: false, 402 Index: -1, 403 }, 404 &ResourceAddress{ 405 Path: []string{"bar"}, 406 Mode: ManagedResourceMode, 407 Type: "aws_instance", 408 Name: "foo", 409 InstanceTypeSet: false, 410 Index: -1, 411 }, 412 true, 413 }, 414 { 415 &ResourceAddress{ 416 Path: []string{"bar"}, 417 InstanceTypeSet: false, 418 Index: -1, 419 }, 420 &ResourceAddress{ 421 Path: []string{"bar"}, 422 Mode: ManagedResourceMode, 423 Type: "aws_instance", 424 Name: "foo", 425 InstanceTypeSet: false, 426 Index: -1, 427 }, 428 true, 429 }, 430 { 431 &ResourceAddress{ 432 Path: []string{"bar"}, 433 InstanceTypeSet: false, 434 Index: -1, 435 }, 436 &ResourceAddress{ 437 Path: []string{"bar", "baz"}, 438 Mode: ManagedResourceMode, 439 Type: "aws_instance", 440 Name: "foo", 441 InstanceTypeSet: false, 442 Index: -1, 443 }, 444 true, 445 }, 446 { 447 &ResourceAddress{ 448 Path: []string{"bar"}, 449 InstanceTypeSet: false, 450 Index: -1, 451 }, 452 &ResourceAddress{ 453 Path: []string{"bar", "baz"}, 454 InstanceTypeSet: false, 455 Index: -1, 456 }, 457 true, 458 }, 459 { 460 &ResourceAddress{ 461 Path: []string{"bar"}, 462 InstanceTypeSet: false, 463 Index: -1, 464 }, 465 &ResourceAddress{ 466 Path: []string{"bar", "baz", "foo", "pizza"}, 467 InstanceTypeSet: false, 468 Index: -1, 469 }, 470 true, 471 }, 472 473 { 474 &ResourceAddress{ 475 Mode: ManagedResourceMode, 476 Type: "aws_instance", 477 Name: "bar", 478 InstanceTypeSet: true, 479 InstanceType: TypePrimary, 480 Index: 0, 481 }, 482 &ResourceAddress{ 483 Mode: ManagedResourceMode, 484 Type: "aws_instance", 485 Name: "foo", 486 InstanceTypeSet: true, 487 InstanceType: TypePrimary, 488 Index: 0, 489 }, 490 false, 491 }, 492 { 493 &ResourceAddress{ 494 Mode: ManagedResourceMode, 495 Type: "aws_instance", 496 Name: "foo", 497 InstanceTypeSet: true, 498 InstanceType: TypePrimary, 499 Index: 0, 500 }, 501 &ResourceAddress{ 502 Mode: DataResourceMode, 503 Type: "aws_instance", 504 Name: "foo", 505 InstanceTypeSet: true, 506 InstanceType: TypePrimary, 507 Index: 0, 508 }, 509 false, 510 }, 511 { 512 &ResourceAddress{ 513 Path: []string{"bar"}, 514 InstanceTypeSet: false, 515 Index: -1, 516 }, 517 &ResourceAddress{ 518 Path: []string{"baz"}, 519 Mode: ManagedResourceMode, 520 Type: "aws_instance", 521 Name: "foo", 522 InstanceTypeSet: false, 523 Index: -1, 524 }, 525 false, 526 }, 527 { 528 &ResourceAddress{ 529 Path: []string{"bar"}, 530 InstanceTypeSet: false, 531 Index: -1, 532 }, 533 &ResourceAddress{ 534 Path: []string{"baz", "bar"}, 535 Mode: ManagedResourceMode, 536 Type: "aws_instance", 537 Name: "foo", 538 InstanceTypeSet: false, 539 Index: -1, 540 }, 541 false, 542 }, 543 { 544 &ResourceAddress{ 545 Mode: ManagedResourceMode, 546 Type: "aws_instance", 547 Name: "foo", 548 InstanceTypeSet: true, 549 InstanceType: TypePrimary, 550 Index: 0, 551 }, 552 &ResourceAddress{ 553 Mode: ManagedResourceMode, 554 Type: "aws_instance", 555 Name: "foo", 556 InstanceTypeSet: false, 557 Index: 0, 558 }, 559 false, 560 }, 561 { 562 &ResourceAddress{ 563 Path: []string{"bar", "baz"}, 564 InstanceTypeSet: false, 565 Index: -1, 566 }, 567 &ResourceAddress{ 568 Path: []string{"bar"}, 569 InstanceTypeSet: false, 570 Index: -1, 571 }, 572 false, 573 }, 574 { 575 &ResourceAddress{ 576 Type: "aws_instance", 577 Name: "foo", 578 Index: 1, 579 InstanceType: TypePrimary, 580 Mode: ManagedResourceMode, 581 }, 582 &ResourceAddress{ 583 Type: "aws_instance", 584 Name: "foo", 585 Index: -1, 586 InstanceType: TypePrimary, 587 Mode: ManagedResourceMode, 588 }, 589 false, 590 }, 591 } 592 593 for _, test := range tests { 594 t.Run(fmt.Sprintf("%s contains %s", test.Address, test.Other), func(t *testing.T) { 595 got := test.Address.Contains(test.Other) 596 if got != test.Want { 597 t.Errorf( 598 "wrong result\nrecv: %s\ngiven: %s\ngot: %#v\nwant: %#v", 599 test.Address, test.Other, 600 got, test.Want, 601 ) 602 } 603 }) 604 } 605 } 606 607 func TestResourceAddressEquals(t *testing.T) { 608 cases := map[string]struct { 609 Address *ResourceAddress 610 Other interface{} 611 Expect bool 612 }{ 613 "basic match": { 614 Address: &ResourceAddress{ 615 Mode: ManagedResourceMode, 616 Type: "aws_instance", 617 Name: "foo", 618 InstanceType: TypePrimary, 619 Index: 0, 620 }, 621 Other: &ResourceAddress{ 622 Mode: ManagedResourceMode, 623 Type: "aws_instance", 624 Name: "foo", 625 InstanceType: TypePrimary, 626 Index: 0, 627 }, 628 Expect: true, 629 }, 630 "address does not set index": { 631 Address: &ResourceAddress{ 632 Mode: ManagedResourceMode, 633 Type: "aws_instance", 634 Name: "foo", 635 InstanceType: TypePrimary, 636 Index: -1, 637 }, 638 Other: &ResourceAddress{ 639 Mode: ManagedResourceMode, 640 Type: "aws_instance", 641 Name: "foo", 642 InstanceType: TypePrimary, 643 Index: 3, 644 }, 645 Expect: true, 646 }, 647 "other does not set index": { 648 Address: &ResourceAddress{ 649 Mode: ManagedResourceMode, 650 Type: "aws_instance", 651 Name: "foo", 652 InstanceType: TypePrimary, 653 Index: 3, 654 }, 655 Other: &ResourceAddress{ 656 Mode: ManagedResourceMode, 657 Type: "aws_instance", 658 Name: "foo", 659 InstanceType: TypePrimary, 660 Index: -1, 661 }, 662 Expect: true, 663 }, 664 "neither sets index": { 665 Address: &ResourceAddress{ 666 Mode: ManagedResourceMode, 667 Type: "aws_instance", 668 Name: "foo", 669 InstanceType: TypePrimary, 670 Index: -1, 671 }, 672 Other: &ResourceAddress{ 673 Mode: ManagedResourceMode, 674 Type: "aws_instance", 675 Name: "foo", 676 InstanceType: TypePrimary, 677 Index: -1, 678 }, 679 Expect: true, 680 }, 681 "index over ten": { 682 Address: &ResourceAddress{ 683 Mode: ManagedResourceMode, 684 Type: "aws_instance", 685 Name: "foo", 686 InstanceType: TypePrimary, 687 Index: 1, 688 }, 689 Other: &ResourceAddress{ 690 Mode: ManagedResourceMode, 691 Type: "aws_instance", 692 Name: "foo", 693 InstanceType: TypePrimary, 694 Index: 13, 695 }, 696 Expect: false, 697 }, 698 "different type": { 699 Address: &ResourceAddress{ 700 Mode: ManagedResourceMode, 701 Type: "aws_instance", 702 Name: "foo", 703 InstanceType: TypePrimary, 704 Index: 0, 705 }, 706 Other: &ResourceAddress{ 707 Mode: ManagedResourceMode, 708 Type: "aws_vpc", 709 Name: "foo", 710 InstanceType: TypePrimary, 711 Index: 0, 712 }, 713 Expect: false, 714 }, 715 "different mode": { 716 Address: &ResourceAddress{ 717 Mode: ManagedResourceMode, 718 Type: "aws_instance", 719 Name: "foo", 720 InstanceType: TypePrimary, 721 Index: 0, 722 }, 723 Other: &ResourceAddress{ 724 Mode: DataResourceMode, 725 Type: "aws_instance", 726 Name: "foo", 727 InstanceType: TypePrimary, 728 Index: 0, 729 }, 730 Expect: false, 731 }, 732 "different name": { 733 Address: &ResourceAddress{ 734 Mode: ManagedResourceMode, 735 Type: "aws_instance", 736 Name: "foo", 737 InstanceType: TypePrimary, 738 Index: 0, 739 }, 740 Other: &ResourceAddress{ 741 Mode: ManagedResourceMode, 742 Type: "aws_instance", 743 Name: "bar", 744 InstanceType: TypePrimary, 745 Index: 0, 746 }, 747 Expect: false, 748 }, 749 "different instance type": { 750 Address: &ResourceAddress{ 751 Mode: ManagedResourceMode, 752 Type: "aws_instance", 753 Name: "foo", 754 InstanceType: TypePrimary, 755 Index: 0, 756 }, 757 Other: &ResourceAddress{ 758 Mode: ManagedResourceMode, 759 Type: "aws_instance", 760 Name: "foo", 761 InstanceType: TypeTainted, 762 Index: 0, 763 }, 764 Expect: false, 765 }, 766 "different index": { 767 Address: &ResourceAddress{ 768 Mode: ManagedResourceMode, 769 Type: "aws_instance", 770 Name: "foo", 771 InstanceType: TypePrimary, 772 Index: 0, 773 }, 774 Other: &ResourceAddress{ 775 Mode: ManagedResourceMode, 776 Type: "aws_instance", 777 Name: "foo", 778 InstanceType: TypePrimary, 779 Index: 1, 780 }, 781 Expect: false, 782 }, 783 "module address matches address of managed resource inside module": { 784 Address: &ResourceAddress{ 785 Path: []string{"a", "b"}, 786 Type: "", 787 Name: "", 788 InstanceType: TypePrimary, 789 Index: -1, 790 }, 791 Other: &ResourceAddress{ 792 Path: []string{"a", "b"}, 793 Mode: ManagedResourceMode, 794 Type: "aws_instance", 795 Name: "foo", 796 InstanceType: TypePrimary, 797 Index: 0, 798 }, 799 Expect: true, 800 }, 801 "module address matches address of data resource inside module": { 802 Address: &ResourceAddress{ 803 Path: []string{"a", "b"}, 804 Type: "", 805 Name: "", 806 InstanceType: TypePrimary, 807 Index: -1, 808 }, 809 Other: &ResourceAddress{ 810 Path: []string{"a", "b"}, 811 Mode: DataResourceMode, 812 Type: "aws_instance", 813 Name: "foo", 814 InstanceType: TypePrimary, 815 Index: 0, 816 }, 817 Expect: true, 818 }, 819 "module address doesn't match managed resource outside module": { 820 Address: &ResourceAddress{ 821 Path: []string{"a", "b"}, 822 Type: "", 823 Name: "", 824 InstanceType: TypePrimary, 825 Index: -1, 826 }, 827 Other: &ResourceAddress{ 828 Path: []string{"a"}, 829 Mode: ManagedResourceMode, 830 Type: "aws_instance", 831 Name: "foo", 832 InstanceType: TypePrimary, 833 Index: 0, 834 }, 835 Expect: false, 836 }, 837 "module address doesn't match data resource outside module": { 838 Address: &ResourceAddress{ 839 Path: []string{"a", "b"}, 840 Type: "", 841 Name: "", 842 InstanceType: TypePrimary, 843 Index: -1, 844 }, 845 Other: &ResourceAddress{ 846 Path: []string{"a"}, 847 Mode: DataResourceMode, 848 Type: "aws_instance", 849 Name: "foo", 850 InstanceType: TypePrimary, 851 Index: 0, 852 }, 853 Expect: false, 854 }, 855 "nil path vs empty path should match": { 856 Address: &ResourceAddress{ 857 Path: []string{}, 858 Mode: ManagedResourceMode, 859 Type: "aws_instance", 860 Name: "foo", 861 InstanceType: TypePrimary, 862 Index: -1, 863 }, 864 Other: &ResourceAddress{ 865 Path: nil, 866 Mode: ManagedResourceMode, 867 Type: "aws_instance", 868 Name: "foo", 869 InstanceType: TypePrimary, 870 Index: 0, 871 }, 872 Expect: true, 873 }, 874 } 875 876 for tn, tc := range cases { 877 actual := tc.Address.Equals(tc.Other) 878 if actual != tc.Expect { 879 t.Fatalf("%q: expected equals: %t, got %t for:\n%#v\n%#v", 880 tn, tc.Expect, actual, tc.Address, tc.Other) 881 } 882 } 883 } 884 885 func TestResourceAddressStateId(t *testing.T) { 886 cases := map[string]struct { 887 Input *ResourceAddress 888 Expected string 889 }{ 890 "basic resource": { 891 &ResourceAddress{ 892 Mode: ManagedResourceMode, 893 Type: "aws_instance", 894 Name: "foo", 895 InstanceType: TypePrimary, 896 Index: -1, 897 }, 898 "aws_instance.foo", 899 }, 900 901 "basic resource with index": { 902 &ResourceAddress{ 903 Mode: ManagedResourceMode, 904 Type: "aws_instance", 905 Name: "foo", 906 InstanceType: TypePrimary, 907 Index: 2, 908 }, 909 "aws_instance.foo.2", 910 }, 911 912 "data resource": { 913 &ResourceAddress{ 914 Mode: DataResourceMode, 915 Type: "aws_instance", 916 Name: "foo", 917 InstanceType: TypePrimary, 918 Index: -1, 919 }, 920 "data.aws_instance.foo", 921 }, 922 } 923 924 for tn, tc := range cases { 925 t.Run(tn, func(t *testing.T) { 926 actual := tc.Input.stateId() 927 if actual != tc.Expected { 928 t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, tc.Expected, actual) 929 } 930 }) 931 } 932 } 933 934 func TestResourceAddressHasResourceSpec(t *testing.T) { 935 cases := []struct { 936 Input string 937 Want bool 938 }{ 939 { 940 "module.foo", 941 false, 942 }, 943 { 944 "module.foo.module.bar", 945 false, 946 }, 947 { 948 "null_resource.baz", 949 true, 950 }, 951 { 952 "null_resource.baz[0]", 953 true, 954 }, 955 { 956 "data.null_data_source.baz", 957 true, 958 }, 959 { 960 "data.null_data_source.baz[0]", 961 true, 962 }, 963 { 964 "module.foo.null_resource.baz", 965 true, 966 }, 967 { 968 "module.foo.data.null_data_source.baz", 969 true, 970 }, 971 { 972 "module.foo.module.bar.null_resource.baz", 973 true, 974 }, 975 } 976 977 for _, test := range cases { 978 t.Run(test.Input, func(t *testing.T) { 979 addr, err := ParseResourceAddress(test.Input) 980 if err != nil { 981 t.Fatalf("error parsing address: %s", err) 982 } 983 got := addr.HasResourceSpec() 984 if got != test.Want { 985 t.Fatalf("%q: wrong result %#v; want %#v", test.Input, got, test.Want) 986 } 987 }) 988 } 989 } 990 991 func TestResourceAddressWholeModuleAddress(t *testing.T) { 992 cases := []struct { 993 Input string 994 Want string 995 }{ 996 { 997 "module.foo", 998 "module.foo", 999 }, 1000 { 1001 "module.foo.module.bar", 1002 "module.foo.module.bar", 1003 }, 1004 { 1005 "null_resource.baz", 1006 "", 1007 }, 1008 { 1009 "null_resource.baz[0]", 1010 "", 1011 }, 1012 { 1013 "data.null_data_source.baz", 1014 "", 1015 }, 1016 { 1017 "data.null_data_source.baz[0]", 1018 "", 1019 }, 1020 { 1021 "module.foo.null_resource.baz", 1022 "module.foo", 1023 }, 1024 { 1025 "module.foo.data.null_data_source.baz", 1026 "module.foo", 1027 }, 1028 { 1029 "module.foo.module.bar.null_resource.baz", 1030 "module.foo.module.bar", 1031 }, 1032 } 1033 1034 for _, test := range cases { 1035 t.Run(test.Input, func(t *testing.T) { 1036 addr, err := ParseResourceAddress(test.Input) 1037 if err != nil { 1038 t.Fatalf("error parsing address: %s", err) 1039 } 1040 gotAddr := addr.WholeModuleAddress() 1041 got := gotAddr.String() 1042 if got != test.Want { 1043 t.Fatalf("%q: wrong result %#v; want %#v", test.Input, got, test.Want) 1044 } 1045 }) 1046 } 1047 } 1048 1049 func TestResourceAddressMatchesResourceConfig(t *testing.T) { 1050 root := []string(nil) 1051 child := []string{"child"} 1052 grandchild := []string{"child", "grandchild"} 1053 irrelevant := []string{"irrelevant"} 1054 1055 tests := []struct { 1056 Addr *ResourceAddress 1057 ModulePath []string 1058 Resource *configs.Resource 1059 Want bool 1060 }{ 1061 { 1062 &ResourceAddress{ 1063 Mode: ManagedResourceMode, 1064 Type: "null_resource", 1065 Name: "baz", 1066 Index: -1, 1067 }, 1068 root, 1069 &configs.Resource{ 1070 Mode: addrs.ManagedResourceMode, 1071 Type: "null_resource", 1072 Name: "baz", 1073 }, 1074 true, 1075 }, 1076 { 1077 &ResourceAddress{ 1078 Path: []string{"child"}, 1079 Mode: ManagedResourceMode, 1080 Type: "null_resource", 1081 Name: "baz", 1082 Index: -1, 1083 }, 1084 child, 1085 &configs.Resource{ 1086 Mode: addrs.ManagedResourceMode, 1087 Type: "null_resource", 1088 Name: "baz", 1089 }, 1090 true, 1091 }, 1092 { 1093 &ResourceAddress{ 1094 Path: []string{"child", "grandchild"}, 1095 Mode: ManagedResourceMode, 1096 Type: "null_resource", 1097 Name: "baz", 1098 Index: -1, 1099 }, 1100 grandchild, 1101 &configs.Resource{ 1102 Mode: addrs.ManagedResourceMode, 1103 Type: "null_resource", 1104 Name: "baz", 1105 }, 1106 true, 1107 }, 1108 { 1109 &ResourceAddress{ 1110 Path: []string{"child"}, 1111 Index: -1, 1112 }, 1113 child, 1114 &configs.Resource{ 1115 Mode: addrs.ManagedResourceMode, 1116 Type: "null_resource", 1117 Name: "baz", 1118 }, 1119 true, 1120 }, 1121 { 1122 &ResourceAddress{ 1123 Path: []string{"child", "grandchild"}, 1124 Index: -1, 1125 }, 1126 grandchild, 1127 &configs.Resource{ 1128 Mode: addrs.ManagedResourceMode, 1129 Type: "null_resource", 1130 Name: "baz", 1131 }, 1132 true, 1133 }, 1134 { 1135 &ResourceAddress{ 1136 Mode: DataResourceMode, 1137 Type: "null_resource", 1138 Name: "baz", 1139 Index: -1, 1140 }, 1141 irrelevant, 1142 &configs.Resource{ 1143 Mode: addrs.ManagedResourceMode, 1144 Type: "null_resource", 1145 Name: "baz", 1146 }, 1147 false, 1148 }, 1149 { 1150 &ResourceAddress{ 1151 Mode: ManagedResourceMode, 1152 Type: "null_resource", 1153 Name: "baz", 1154 Index: -1, 1155 }, 1156 irrelevant, 1157 &configs.Resource{ 1158 Mode: addrs.ManagedResourceMode, 1159 Type: "null_resource", 1160 Name: "pizza", 1161 }, 1162 false, 1163 }, 1164 { 1165 &ResourceAddress{ 1166 Mode: ManagedResourceMode, 1167 Type: "null_resource", 1168 Name: "baz", 1169 Index: -1, 1170 }, 1171 irrelevant, 1172 &configs.Resource{ 1173 Mode: addrs.ManagedResourceMode, 1174 Type: "aws_instance", 1175 Name: "baz", 1176 }, 1177 false, 1178 }, 1179 { 1180 &ResourceAddress{ 1181 Path: []string{"child", "grandchild"}, 1182 Mode: ManagedResourceMode, 1183 Type: "null_resource", 1184 Name: "baz", 1185 Index: -1, 1186 }, 1187 child, 1188 &configs.Resource{ 1189 Mode: addrs.ManagedResourceMode, 1190 Type: "null_resource", 1191 Name: "baz", 1192 }, 1193 false, 1194 }, 1195 { 1196 &ResourceAddress{ 1197 Path: []string{"child"}, 1198 Mode: ManagedResourceMode, 1199 Type: "null_resource", 1200 Name: "baz", 1201 Index: -1, 1202 }, 1203 grandchild, 1204 &configs.Resource{ 1205 Mode: addrs.ManagedResourceMode, 1206 Type: "null_resource", 1207 Name: "baz", 1208 }, 1209 false, 1210 }, 1211 } 1212 1213 for i, test := range tests { 1214 t.Run(fmt.Sprintf("%02d-%s", i, test.Addr), func(t *testing.T) { 1215 got := test.Addr.MatchesResourceConfig(test.ModulePath, test.Resource) 1216 if got != test.Want { 1217 t.Errorf( 1218 "wrong result\naddr: %s\nmod: %#v\nrsrc: %#v\ngot: %#v\nwant: %#v", 1219 test.Addr, test.ModulePath, test.Resource, got, test.Want, 1220 ) 1221 } 1222 }) 1223 } 1224 } 1225 1226 func TestResourceAddressLess(t *testing.T) { 1227 tests := []struct { 1228 A string 1229 B string 1230 Want bool 1231 }{ 1232 { 1233 "foo.bar", 1234 "module.baz.foo.bar", 1235 true, 1236 }, 1237 { 1238 "module.baz.foo.bar", 1239 "zzz.bar", // would sort after "module" in lexicographical sort 1240 false, 1241 }, 1242 { 1243 "module.baz.foo.bar", 1244 "module.baz.foo.bar", 1245 false, 1246 }, 1247 { 1248 "module.baz.foo.bar", 1249 "module.boz.foo.bar", 1250 true, 1251 }, 1252 { 1253 "module.boz.foo.bar", 1254 "module.baz.foo.bar", 1255 false, 1256 }, 1257 { 1258 "a.b", 1259 "b.c", 1260 true, 1261 }, 1262 { 1263 "a.b", 1264 "a.c", 1265 true, 1266 }, 1267 { 1268 "c.b", 1269 "b.c", 1270 false, 1271 }, 1272 { 1273 "a.b[9]", 1274 "a.b[10]", 1275 true, 1276 }, 1277 { 1278 "b.b[9]", 1279 "a.b[10]", 1280 false, 1281 }, 1282 { 1283 "a.b", 1284 "a.b.deposed", 1285 true, 1286 }, 1287 { 1288 "a.b.tainted", 1289 "a.b.deposed", 1290 true, 1291 }, 1292 } 1293 1294 for _, test := range tests { 1295 t.Run(fmt.Sprintf("%s < %s", test.A, test.B), func(t *testing.T) { 1296 addrA, err := ParseResourceAddress(test.A) 1297 if err != nil { 1298 t.Fatal(err) 1299 } 1300 addrB, err := ParseResourceAddress(test.B) 1301 if err != nil { 1302 t.Fatal(err) 1303 } 1304 got := addrA.Less(addrB) 1305 invGot := addrB.Less(addrA) 1306 if got != test.Want { 1307 t.Errorf( 1308 "wrong result\ntest: %s < %s\ngot: %#v\nwant: %#v", 1309 test.A, test.B, got, test.Want, 1310 ) 1311 } 1312 if test.A != test.B { // inverse test doesn't apply when equal 1313 if invGot != !test.Want { 1314 t.Errorf( 1315 "wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v", 1316 test.B, test.A, invGot, !test.Want, 1317 ) 1318 } 1319 } else { 1320 if invGot != test.Want { 1321 t.Errorf( 1322 "wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v", 1323 test.B, test.A, invGot, test.Want, 1324 ) 1325 } 1326 } 1327 }) 1328 } 1329 }