github.com/opentofu/opentofu@v1.7.1/internal/addrs/target_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package addrs
     7  
     8  import (
     9  	"fmt"
    10  	"testing"
    11  )
    12  
    13  func TestTargetContains(t *testing.T) {
    14  	for _, test := range []struct {
    15  		addr, other Targetable
    16  		expect      bool
    17  	}{
    18  		{
    19  			mustParseTarget("module.foo"),
    20  			mustParseTarget("module.bar"),
    21  			false,
    22  		},
    23  		{
    24  			mustParseTarget("module.foo"),
    25  			mustParseTarget("module.foo"),
    26  			true,
    27  		},
    28  		{
    29  			RootModuleInstance,
    30  			mustParseTarget("module.foo"),
    31  			true,
    32  		},
    33  		{
    34  			mustParseTarget("module.foo"),
    35  			RootModuleInstance,
    36  			false,
    37  		},
    38  		{
    39  			mustParseTarget("module.foo"),
    40  			mustParseTarget("module.foo.module.bar[0]"),
    41  			true,
    42  		},
    43  		{
    44  			mustParseTarget("module.foo"),
    45  			mustParseTarget("module.foo.module.bar[0]"),
    46  			true,
    47  		},
    48  		{
    49  			mustParseTarget("module.foo[2]"),
    50  			mustParseTarget("module.foo[2].module.bar[0]"),
    51  			true,
    52  		},
    53  		{
    54  			mustParseTarget("module.foo"),
    55  			mustParseTarget("module.foo.test_resource.bar"),
    56  			true,
    57  		},
    58  		{
    59  			mustParseTarget("module.foo"),
    60  			mustParseTarget("module.foo.test_resource.bar[0]"),
    61  			true,
    62  		},
    63  
    64  		// Resources
    65  		{
    66  			mustParseTarget("test_resource.foo"),
    67  			mustParseTarget("test_resource.foo[\"bar\"]"),
    68  			true,
    69  		},
    70  		{
    71  			mustParseTarget(`test_resource.foo["bar"]`),
    72  			mustParseTarget(`test_resource.foo["bar"]`),
    73  			true,
    74  		},
    75  		{
    76  			mustParseTarget("test_resource.foo"),
    77  			mustParseTarget("test_resource.foo[2]"),
    78  			true,
    79  		},
    80  		{
    81  			mustParseTarget("test_resource.foo"),
    82  			mustParseTarget("module.bar.test_resource.foo[2]"),
    83  			false,
    84  		},
    85  		{
    86  			mustParseTarget("module.bar.test_resource.foo"),
    87  			mustParseTarget("module.bar.test_resource.foo[2]"),
    88  			true,
    89  		},
    90  		{
    91  			mustParseTarget("module.bar.test_resource.foo"),
    92  			mustParseTarget("module.bar[0].test_resource.foo[2]"),
    93  			false,
    94  		},
    95  		{
    96  			mustParseTarget("module.bar.test_resource.foo"),
    97  			mustParseTarget("module.bar.test_resource.foo[0]"),
    98  			true,
    99  		},
   100  		{
   101  			mustParseTarget("module.bax"),
   102  			mustParseTarget("module.bax[0].test_resource.foo[0]"),
   103  			true,
   104  		},
   105  
   106  		// Config paths, while never returned from parsing a target, must still
   107  		// be targetable
   108  		{
   109  			ConfigResource{
   110  				Module: []string{"bar"},
   111  				Resource: Resource{
   112  					Mode: ManagedResourceMode,
   113  					Type: "test_resource",
   114  					Name: "foo",
   115  				},
   116  			},
   117  			mustParseTarget("module.bar.test_resource.foo[2]"),
   118  			true,
   119  		},
   120  		{
   121  			mustParseTarget("module.bar"),
   122  			ConfigResource{
   123  				Module: []string{"bar"},
   124  				Resource: Resource{
   125  					Mode: ManagedResourceMode,
   126  					Type: "test_resource",
   127  					Name: "foo",
   128  				},
   129  			},
   130  			true,
   131  		},
   132  		{
   133  			mustParseTarget("module.bar.test_resource.foo"),
   134  			ConfigResource{
   135  				Module: []string{"bar"},
   136  				Resource: Resource{
   137  					Mode: ManagedResourceMode,
   138  					Type: "test_resource",
   139  					Name: "foo",
   140  				},
   141  			},
   142  			true,
   143  		},
   144  		{
   145  			ConfigResource{
   146  				Resource: Resource{
   147  					Mode: ManagedResourceMode,
   148  					Type: "test_resource",
   149  					Name: "foo",
   150  				},
   151  			},
   152  			mustParseTarget("module.bar.test_resource.foo[2]"),
   153  			false,
   154  		},
   155  		{
   156  			ConfigResource{
   157  				Module: []string{"bar"},
   158  				Resource: Resource{
   159  					Mode: ManagedResourceMode,
   160  					Type: "test_resource",
   161  					Name: "foo",
   162  				},
   163  			},
   164  			mustParseTarget("module.bar[0].test_resource.foo"),
   165  			true,
   166  		},
   167  
   168  		// Modules are also never the result of parsing a target, but also need
   169  		// to be targetable
   170  		{
   171  			Module{"bar"},
   172  			Module{"bar", "baz"},
   173  			true,
   174  		},
   175  		{
   176  			Module{"bar"},
   177  			mustParseTarget("module.bar[0]"),
   178  			true,
   179  		},
   180  		{
   181  			// Parsing an ambiguous module path needs to ensure the
   182  			// ModuleInstance could contain the Module. This is safe because if
   183  			// the module could be expanded, it must have an index, meaning no
   184  			// index indicates that the module instance and module are
   185  			// functionally equivalent.
   186  			mustParseTarget("module.bar"),
   187  			Module{"bar"},
   188  			true,
   189  		},
   190  		{
   191  			// A specific ModuleInstance cannot contain a module
   192  			mustParseTarget("module.bar[0]"),
   193  			Module{"bar"},
   194  			false,
   195  		},
   196  		{
   197  			Module{"bar", "baz"},
   198  			mustParseTarget("module.bar[0].module.baz.test_resource.foo[1]"),
   199  			true,
   200  		},
   201  		{
   202  			mustParseTarget("module.bar[0].module.baz"),
   203  			Module{"bar", "baz"},
   204  			false,
   205  		},
   206  	} {
   207  		t.Run(fmt.Sprintf("%s-in-%s", test.other, test.addr), func(t *testing.T) {
   208  			got := test.addr.TargetContains(test.other)
   209  			if got != test.expect {
   210  				t.Fatalf("expected %q.TargetContains(%q) == %t", test.addr, test.other, test.expect)
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  func TestResourceContains(t *testing.T) {
   217  	for _, test := range []struct {
   218  		in, other Targetable
   219  		expect    bool
   220  	}{} {
   221  		t.Run(fmt.Sprintf("%s-in-%s", test.other, test.in), func(t *testing.T) {
   222  			got := test.in.TargetContains(test.other)
   223  			if got != test.expect {
   224  				t.Fatalf("expected %q.TargetContains(%q) == %t", test.in, test.other, test.expect)
   225  			}
   226  		})
   227  	}
   228  }
   229  
   230  func mustParseTarget(str string) Targetable {
   231  	t, diags := ParseTargetStr(str)
   232  	if diags != nil {
   233  		panic(fmt.Sprintf("%s: %s", str, diags.ErrWithWarnings()))
   234  	}
   235  	return t.Subject
   236  }