github.com/jameswoolfenden/terraform@v0.11.12-beta1/terraform/resource_address_test.go (about)

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