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  }