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