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