github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/instances/expander_test.go (about)

     1  package instances
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/zclconf/go-cty/cty"
    10  
    11  	"github.com/eliastor/durgaform/internal/addrs"
    12  )
    13  
    14  func TestExpander(t *testing.T) {
    15  	// Some module and resource addresses and values we'll use repeatedly below.
    16  	singleModuleAddr := addrs.ModuleCall{Name: "single"}
    17  	count2ModuleAddr := addrs.ModuleCall{Name: "count2"}
    18  	count0ModuleAddr := addrs.ModuleCall{Name: "count0"}
    19  	forEachModuleAddr := addrs.ModuleCall{Name: "for_each"}
    20  	singleResourceAddr := addrs.Resource{
    21  		Mode: addrs.ManagedResourceMode,
    22  		Type: "test",
    23  		Name: "single",
    24  	}
    25  	count2ResourceAddr := addrs.Resource{
    26  		Mode: addrs.ManagedResourceMode,
    27  		Type: "test",
    28  		Name: "count2",
    29  	}
    30  	count0ResourceAddr := addrs.Resource{
    31  		Mode: addrs.ManagedResourceMode,
    32  		Type: "test",
    33  		Name: "count0",
    34  	}
    35  	forEachResourceAddr := addrs.Resource{
    36  		Mode: addrs.ManagedResourceMode,
    37  		Type: "test",
    38  		Name: "for_each",
    39  	}
    40  	eachMap := map[string]cty.Value{
    41  		"a": cty.NumberIntVal(1),
    42  		"b": cty.NumberIntVal(2),
    43  	}
    44  
    45  	// In normal use, Expander would be called in the context of a graph
    46  	// traversal to ensure that information is registered/requested in the
    47  	// correct sequence, but to keep this test self-contained we'll just
    48  	// manually write out the steps here.
    49  	//
    50  	// The steps below are assuming a configuration tree like the following:
    51  	// - root module
    52  	//   - resource test.single with no count or for_each
    53  	//   - resource test.count2 with count = 2
    54  	//   - resource test.count0 with count = 0
    55  	//   - resource test.for_each with for_each = { a = 1, b = 2 }
    56  	//   - child module "single" with no count or for_each
    57  	//     - resource test.single with no count or for_each
    58  	//     - resource test.count2 with count = 2
    59  	//   - child module "count2" with count = 2
    60  	//     - resource test.single with no count or for_each
    61  	//     - resource test.count2 with count = 2
    62  	//     - child module "count2" with count = 2
    63  	//       - resource test.count2 with count = 2
    64  	//   - child module "count0" with count = 0
    65  	//     - resource test.single with no count or for_each
    66  	//   - child module for_each with for_each = { a = 1, b = 2 }
    67  	//     - resource test.single with no count or for_each
    68  	//     - resource test.count2 with count = 2
    69  
    70  	ex := NewExpander()
    71  
    72  	// We don't register the root module, because it's always implied to exist.
    73  	//
    74  	// Below we're going to use braces and indentation just to help visually
    75  	// reflect the tree structure from the tree in the above comment, in the
    76  	// hope that the following is easier to follow.
    77  	//
    78  	// The Expander API requires that we register containing modules before
    79  	// registering anything inside them, so we'll work through the above
    80  	// in a depth-first order in the registration steps that follow.
    81  	{
    82  		ex.SetResourceSingle(addrs.RootModuleInstance, singleResourceAddr)
    83  		ex.SetResourceCount(addrs.RootModuleInstance, count2ResourceAddr, 2)
    84  		ex.SetResourceCount(addrs.RootModuleInstance, count0ResourceAddr, 0)
    85  		ex.SetResourceForEach(addrs.RootModuleInstance, forEachResourceAddr, eachMap)
    86  
    87  		ex.SetModuleSingle(addrs.RootModuleInstance, singleModuleAddr)
    88  		{
    89  			// The single instance of the module
    90  			moduleInstanceAddr := addrs.RootModuleInstance.Child("single", addrs.NoKey)
    91  			ex.SetResourceSingle(moduleInstanceAddr, singleResourceAddr)
    92  			ex.SetResourceCount(moduleInstanceAddr, count2ResourceAddr, 2)
    93  		}
    94  
    95  		ex.SetModuleCount(addrs.RootModuleInstance, count2ModuleAddr, 2)
    96  		for i1 := 0; i1 < 2; i1++ {
    97  			moduleInstanceAddr := addrs.RootModuleInstance.Child("count2", addrs.IntKey(i1))
    98  			ex.SetResourceSingle(moduleInstanceAddr, singleResourceAddr)
    99  			ex.SetResourceCount(moduleInstanceAddr, count2ResourceAddr, 2)
   100  			ex.SetModuleCount(moduleInstanceAddr, count2ModuleAddr, 2)
   101  			for i2 := 0; i2 < 2; i2++ {
   102  				moduleInstanceAddr := moduleInstanceAddr.Child("count2", addrs.IntKey(i2))
   103  				ex.SetResourceCount(moduleInstanceAddr, count2ResourceAddr, 2)
   104  			}
   105  		}
   106  
   107  		ex.SetModuleCount(addrs.RootModuleInstance, count0ModuleAddr, 0)
   108  		{
   109  			// There are no instances of module "count0", so our nested module
   110  			// would never actually get registered here: the expansion node
   111  			// for the resource would see that its containing module has no
   112  			// instances and so do nothing.
   113  		}
   114  
   115  		ex.SetModuleForEach(addrs.RootModuleInstance, forEachModuleAddr, eachMap)
   116  		for k := range eachMap {
   117  			moduleInstanceAddr := addrs.RootModuleInstance.Child("for_each", addrs.StringKey(k))
   118  			ex.SetResourceSingle(moduleInstanceAddr, singleResourceAddr)
   119  			ex.SetResourceCount(moduleInstanceAddr, count2ResourceAddr, 2)
   120  		}
   121  	}
   122  
   123  	t.Run("root module", func(t *testing.T) {
   124  		// Requesting expansion of the root module doesn't really mean anything
   125  		// since it's always a singleton, but for consistency it should work.
   126  		got := ex.ExpandModule(addrs.RootModule)
   127  		want := []addrs.ModuleInstance{addrs.RootModuleInstance}
   128  		if diff := cmp.Diff(want, got); diff != "" {
   129  			t.Errorf("wrong result\n%s", diff)
   130  		}
   131  	})
   132  	t.Run("resource single", func(t *testing.T) {
   133  		got := ex.ExpandModuleResource(
   134  			addrs.RootModule,
   135  			singleResourceAddr,
   136  		)
   137  		want := []addrs.AbsResourceInstance{
   138  			mustAbsResourceInstanceAddr(`test.single`),
   139  		}
   140  		if diff := cmp.Diff(want, got); diff != "" {
   141  			t.Errorf("wrong result\n%s", diff)
   142  		}
   143  	})
   144  	t.Run("resource count2", func(t *testing.T) {
   145  		got := ex.ExpandModuleResource(
   146  			addrs.RootModule,
   147  			count2ResourceAddr,
   148  		)
   149  		want := []addrs.AbsResourceInstance{
   150  			mustAbsResourceInstanceAddr(`test.count2[0]`),
   151  			mustAbsResourceInstanceAddr(`test.count2[1]`),
   152  		}
   153  		if diff := cmp.Diff(want, got); diff != "" {
   154  			t.Errorf("wrong result\n%s", diff)
   155  		}
   156  	})
   157  	t.Run("resource count0", func(t *testing.T) {
   158  		got := ex.ExpandModuleResource(
   159  			addrs.RootModule,
   160  			count0ResourceAddr,
   161  		)
   162  		want := []addrs.AbsResourceInstance(nil)
   163  		if diff := cmp.Diff(want, got); diff != "" {
   164  			t.Errorf("wrong result\n%s", diff)
   165  		}
   166  	})
   167  	t.Run("resource for_each", func(t *testing.T) {
   168  		got := ex.ExpandModuleResource(
   169  			addrs.RootModule,
   170  			forEachResourceAddr,
   171  		)
   172  		want := []addrs.AbsResourceInstance{
   173  			mustAbsResourceInstanceAddr(`test.for_each["a"]`),
   174  			mustAbsResourceInstanceAddr(`test.for_each["b"]`),
   175  		}
   176  		if diff := cmp.Diff(want, got); diff != "" {
   177  			t.Errorf("wrong result\n%s", diff)
   178  		}
   179  	})
   180  	t.Run("module single", func(t *testing.T) {
   181  		got := ex.ExpandModule(addrs.RootModule.Child("single"))
   182  		want := []addrs.ModuleInstance{
   183  			mustModuleInstanceAddr(`module.single`),
   184  		}
   185  		if diff := cmp.Diff(want, got); diff != "" {
   186  			t.Errorf("wrong result\n%s", diff)
   187  		}
   188  	})
   189  	t.Run("module single resource single", func(t *testing.T) {
   190  		got := ex.ExpandModuleResource(
   191  			mustModuleAddr("single"),
   192  			singleResourceAddr,
   193  		)
   194  		want := []addrs.AbsResourceInstance{
   195  			mustAbsResourceInstanceAddr("module.single.test.single"),
   196  		}
   197  		if diff := cmp.Diff(want, got); diff != "" {
   198  			t.Errorf("wrong result\n%s", diff)
   199  		}
   200  	})
   201  	t.Run("module single resource count2", func(t *testing.T) {
   202  		// Two different ways of asking the same question, which should
   203  		// both produce the same result.
   204  		// First: nested expansion of all instances of the resource across
   205  		// all instances of the module, but it's a single-instance module
   206  		// so the first level is a singleton.
   207  		got1 := ex.ExpandModuleResource(
   208  			mustModuleAddr(`single`),
   209  			count2ResourceAddr,
   210  		)
   211  		// Second: expansion of only instances belonging to a specific
   212  		// instance of the module, but again it's a single-instance module
   213  		// so there's only one to ask about.
   214  		got2 := ex.ExpandResource(
   215  			count2ResourceAddr.Absolute(
   216  				addrs.RootModuleInstance.Child("single", addrs.NoKey),
   217  			),
   218  		)
   219  		want := []addrs.AbsResourceInstance{
   220  			mustAbsResourceInstanceAddr(`module.single.test.count2[0]`),
   221  			mustAbsResourceInstanceAddr(`module.single.test.count2[1]`),
   222  		}
   223  		if diff := cmp.Diff(want, got1); diff != "" {
   224  			t.Errorf("wrong ExpandModuleResource result\n%s", diff)
   225  		}
   226  		if diff := cmp.Diff(want, got2); diff != "" {
   227  			t.Errorf("wrong ExpandResource result\n%s", diff)
   228  		}
   229  	})
   230  	t.Run("module single resource count2 with non-existing module instance", func(t *testing.T) {
   231  		got := ex.ExpandResource(
   232  			count2ResourceAddr.Absolute(
   233  				// Note: This is intentionally an invalid instance key,
   234  				// so we're asking about module.single[1].test.count2
   235  				// even though module.single doesn't have count set and
   236  				// therefore there is no module.single[1].
   237  				addrs.RootModuleInstance.Child("single", addrs.IntKey(1)),
   238  			),
   239  		)
   240  		// If the containing module instance doesn't exist then it can't
   241  		// possibly have any resource instances inside it.
   242  		want := ([]addrs.AbsResourceInstance)(nil)
   243  		if diff := cmp.Diff(want, got); diff != "" {
   244  			t.Errorf("wrong result\n%s", diff)
   245  		}
   246  	})
   247  	t.Run("module count2", func(t *testing.T) {
   248  		got := ex.ExpandModule(mustModuleAddr(`count2`))
   249  		want := []addrs.ModuleInstance{
   250  			mustModuleInstanceAddr(`module.count2[0]`),
   251  			mustModuleInstanceAddr(`module.count2[1]`),
   252  		}
   253  		if diff := cmp.Diff(want, got); diff != "" {
   254  			t.Errorf("wrong result\n%s", diff)
   255  		}
   256  	})
   257  	t.Run("module count2 resource single", func(t *testing.T) {
   258  		got := ex.ExpandModuleResource(
   259  			mustModuleAddr(`count2`),
   260  			singleResourceAddr,
   261  		)
   262  		want := []addrs.AbsResourceInstance{
   263  			mustAbsResourceInstanceAddr(`module.count2[0].test.single`),
   264  			mustAbsResourceInstanceAddr(`module.count2[1].test.single`),
   265  		}
   266  		if diff := cmp.Diff(want, got); diff != "" {
   267  			t.Errorf("wrong result\n%s", diff)
   268  		}
   269  	})
   270  	t.Run("module count2 resource count2", func(t *testing.T) {
   271  		got := ex.ExpandModuleResource(
   272  			mustModuleAddr(`count2`),
   273  			count2ResourceAddr,
   274  		)
   275  		want := []addrs.AbsResourceInstance{
   276  			mustAbsResourceInstanceAddr(`module.count2[0].test.count2[0]`),
   277  			mustAbsResourceInstanceAddr(`module.count2[0].test.count2[1]`),
   278  			mustAbsResourceInstanceAddr(`module.count2[1].test.count2[0]`),
   279  			mustAbsResourceInstanceAddr(`module.count2[1].test.count2[1]`),
   280  		}
   281  		if diff := cmp.Diff(want, got); diff != "" {
   282  			t.Errorf("wrong result\n%s", diff)
   283  		}
   284  	})
   285  	t.Run("module count2 module count2", func(t *testing.T) {
   286  		got := ex.ExpandModule(mustModuleAddr(`count2.count2`))
   287  		want := []addrs.ModuleInstance{
   288  			mustModuleInstanceAddr(`module.count2[0].module.count2[0]`),
   289  			mustModuleInstanceAddr(`module.count2[0].module.count2[1]`),
   290  			mustModuleInstanceAddr(`module.count2[1].module.count2[0]`),
   291  			mustModuleInstanceAddr(`module.count2[1].module.count2[1]`),
   292  		}
   293  		if diff := cmp.Diff(want, got); diff != "" {
   294  			t.Errorf("wrong result\n%s", diff)
   295  		}
   296  	})
   297  	t.Run("module count2 module count2 GetDeepestExistingModuleInstance", func(t *testing.T) {
   298  		t.Run("first step invalid", func(t *testing.T) {
   299  			got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2["nope"].module.count2[0]`))
   300  			want := addrs.RootModuleInstance
   301  			if !want.Equal(got) {
   302  				t.Errorf("wrong result\ngot:  %s\nwant: %s", got, want)
   303  			}
   304  		})
   305  		t.Run("second step invalid", func(t *testing.T) {
   306  			got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2[1].module.count2`))
   307  			want := mustModuleInstanceAddr(`module.count2[1]`)
   308  			if !want.Equal(got) {
   309  				t.Errorf("wrong result\ngot:  %s\nwant: %s", got, want)
   310  			}
   311  		})
   312  		t.Run("neither step valid", func(t *testing.T) {
   313  			got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2.module.count2["nope"]`))
   314  			want := addrs.RootModuleInstance
   315  			if !want.Equal(got) {
   316  				t.Errorf("wrong result\ngot:  %s\nwant: %s", got, want)
   317  			}
   318  		})
   319  		t.Run("both steps valid", func(t *testing.T) {
   320  			got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2[1].module.count2[0]`))
   321  			want := mustModuleInstanceAddr(`module.count2[1].module.count2[0]`)
   322  			if !want.Equal(got) {
   323  				t.Errorf("wrong result\ngot:  %s\nwant: %s", got, want)
   324  			}
   325  		})
   326  	})
   327  	t.Run("module count2 resource count2 resource count2", func(t *testing.T) {
   328  		got := ex.ExpandModuleResource(
   329  			mustModuleAddr(`count2.count2`),
   330  			count2ResourceAddr,
   331  		)
   332  		want := []addrs.AbsResourceInstance{
   333  			mustAbsResourceInstanceAddr(`module.count2[0].module.count2[0].test.count2[0]`),
   334  			mustAbsResourceInstanceAddr(`module.count2[0].module.count2[0].test.count2[1]`),
   335  			mustAbsResourceInstanceAddr(`module.count2[0].module.count2[1].test.count2[0]`),
   336  			mustAbsResourceInstanceAddr(`module.count2[0].module.count2[1].test.count2[1]`),
   337  			mustAbsResourceInstanceAddr(`module.count2[1].module.count2[0].test.count2[0]`),
   338  			mustAbsResourceInstanceAddr(`module.count2[1].module.count2[0].test.count2[1]`),
   339  			mustAbsResourceInstanceAddr(`module.count2[1].module.count2[1].test.count2[0]`),
   340  			mustAbsResourceInstanceAddr(`module.count2[1].module.count2[1].test.count2[1]`),
   341  		}
   342  		if diff := cmp.Diff(want, got); diff != "" {
   343  			t.Errorf("wrong result\n%s", diff)
   344  		}
   345  	})
   346  	t.Run("module count2 resource count2 resource count2", func(t *testing.T) {
   347  		got := ex.ExpandResource(
   348  			count2ResourceAddr.Absolute(mustModuleInstanceAddr(`module.count2[0].module.count2[1]`)),
   349  		)
   350  		want := []addrs.AbsResourceInstance{
   351  			mustAbsResourceInstanceAddr(`module.count2[0].module.count2[1].test.count2[0]`),
   352  			mustAbsResourceInstanceAddr(`module.count2[0].module.count2[1].test.count2[1]`),
   353  		}
   354  		if diff := cmp.Diff(want, got); diff != "" {
   355  			t.Errorf("wrong result\n%s", diff)
   356  		}
   357  	})
   358  	t.Run("module count0", func(t *testing.T) {
   359  		got := ex.ExpandModule(mustModuleAddr(`count0`))
   360  		want := []addrs.ModuleInstance(nil)
   361  		if diff := cmp.Diff(want, got); diff != "" {
   362  			t.Errorf("wrong result\n%s", diff)
   363  		}
   364  	})
   365  	t.Run("module count0 resource single", func(t *testing.T) {
   366  		got := ex.ExpandModuleResource(
   367  			mustModuleAddr(`count0`),
   368  			singleResourceAddr,
   369  		)
   370  		// The containing module has zero instances, so therefore there
   371  		// are zero instances of this resource even though it doesn't have
   372  		// count = 0 set itself.
   373  		want := []addrs.AbsResourceInstance(nil)
   374  		if diff := cmp.Diff(want, got); diff != "" {
   375  			t.Errorf("wrong result\n%s", diff)
   376  		}
   377  	})
   378  	t.Run("module for_each", func(t *testing.T) {
   379  		got := ex.ExpandModule(mustModuleAddr(`for_each`))
   380  		want := []addrs.ModuleInstance{
   381  			mustModuleInstanceAddr(`module.for_each["a"]`),
   382  			mustModuleInstanceAddr(`module.for_each["b"]`),
   383  		}
   384  		if diff := cmp.Diff(want, got); diff != "" {
   385  			t.Errorf("wrong result\n%s", diff)
   386  		}
   387  	})
   388  	t.Run("module for_each resource single", func(t *testing.T) {
   389  		got := ex.ExpandModuleResource(
   390  			mustModuleAddr(`for_each`),
   391  			singleResourceAddr,
   392  		)
   393  		want := []addrs.AbsResourceInstance{
   394  			mustAbsResourceInstanceAddr(`module.for_each["a"].test.single`),
   395  			mustAbsResourceInstanceAddr(`module.for_each["b"].test.single`),
   396  		}
   397  		if diff := cmp.Diff(want, got); diff != "" {
   398  			t.Errorf("wrong result\n%s", diff)
   399  		}
   400  	})
   401  	t.Run("module for_each resource count2", func(t *testing.T) {
   402  		got := ex.ExpandModuleResource(
   403  			mustModuleAddr(`for_each`),
   404  			count2ResourceAddr,
   405  		)
   406  		want := []addrs.AbsResourceInstance{
   407  			mustAbsResourceInstanceAddr(`module.for_each["a"].test.count2[0]`),
   408  			mustAbsResourceInstanceAddr(`module.for_each["a"].test.count2[1]`),
   409  			mustAbsResourceInstanceAddr(`module.for_each["b"].test.count2[0]`),
   410  			mustAbsResourceInstanceAddr(`module.for_each["b"].test.count2[1]`),
   411  		}
   412  		if diff := cmp.Diff(want, got); diff != "" {
   413  			t.Errorf("wrong result\n%s", diff)
   414  		}
   415  	})
   416  	t.Run("module for_each resource count2", func(t *testing.T) {
   417  		got := ex.ExpandResource(
   418  			count2ResourceAddr.Absolute(mustModuleInstanceAddr(`module.for_each["a"]`)),
   419  		)
   420  		want := []addrs.AbsResourceInstance{
   421  			mustAbsResourceInstanceAddr(`module.for_each["a"].test.count2[0]`),
   422  			mustAbsResourceInstanceAddr(`module.for_each["a"].test.count2[1]`),
   423  		}
   424  		if diff := cmp.Diff(want, got); diff != "" {
   425  			t.Errorf("wrong result\n%s", diff)
   426  		}
   427  	})
   428  
   429  	t.Run(`module.for_each["b"] repetitiondata`, func(t *testing.T) {
   430  		got := ex.GetModuleInstanceRepetitionData(
   431  			mustModuleInstanceAddr(`module.for_each["b"]`),
   432  		)
   433  		want := RepetitionData{
   434  			EachKey:   cty.StringVal("b"),
   435  			EachValue: cty.NumberIntVal(2),
   436  		}
   437  		if diff := cmp.Diff(want, got, cmp.Comparer(valueEquals)); diff != "" {
   438  			t.Errorf("wrong result\n%s", diff)
   439  		}
   440  	})
   441  	t.Run(`module.count2[0].module.count2[1] repetitiondata`, func(t *testing.T) {
   442  		got := ex.GetModuleInstanceRepetitionData(
   443  			mustModuleInstanceAddr(`module.count2[0].module.count2[1]`),
   444  		)
   445  		want := RepetitionData{
   446  			CountIndex: cty.NumberIntVal(1),
   447  		}
   448  		if diff := cmp.Diff(want, got, cmp.Comparer(valueEquals)); diff != "" {
   449  			t.Errorf("wrong result\n%s", diff)
   450  		}
   451  	})
   452  	t.Run(`module.for_each["a"] repetitiondata`, func(t *testing.T) {
   453  		got := ex.GetModuleInstanceRepetitionData(
   454  			mustModuleInstanceAddr(`module.for_each["a"]`),
   455  		)
   456  		want := RepetitionData{
   457  			EachKey:   cty.StringVal("a"),
   458  			EachValue: cty.NumberIntVal(1),
   459  		}
   460  		if diff := cmp.Diff(want, got, cmp.Comparer(valueEquals)); diff != "" {
   461  			t.Errorf("wrong result\n%s", diff)
   462  		}
   463  	})
   464  
   465  	t.Run(`test.for_each["a"] repetitiondata`, func(t *testing.T) {
   466  		got := ex.GetResourceInstanceRepetitionData(
   467  			mustAbsResourceInstanceAddr(`test.for_each["a"]`),
   468  		)
   469  		want := RepetitionData{
   470  			EachKey:   cty.StringVal("a"),
   471  			EachValue: cty.NumberIntVal(1),
   472  		}
   473  		if diff := cmp.Diff(want, got, cmp.Comparer(valueEquals)); diff != "" {
   474  			t.Errorf("wrong result\n%s", diff)
   475  		}
   476  	})
   477  	t.Run(`module.for_each["a"].test.single repetitiondata`, func(t *testing.T) {
   478  		got := ex.GetResourceInstanceRepetitionData(
   479  			mustAbsResourceInstanceAddr(`module.for_each["a"].test.single`),
   480  		)
   481  		want := RepetitionData{}
   482  		if diff := cmp.Diff(want, got, cmp.Comparer(valueEquals)); diff != "" {
   483  			t.Errorf("wrong result\n%s", diff)
   484  		}
   485  	})
   486  	t.Run(`module.for_each["a"].test.count2[1] repetitiondata`, func(t *testing.T) {
   487  		got := ex.GetResourceInstanceRepetitionData(
   488  			mustAbsResourceInstanceAddr(`module.for_each["a"].test.count2[1]`),
   489  		)
   490  		want := RepetitionData{
   491  			CountIndex: cty.NumberIntVal(1),
   492  		}
   493  		if diff := cmp.Diff(want, got, cmp.Comparer(valueEquals)); diff != "" {
   494  			t.Errorf("wrong result\n%s", diff)
   495  		}
   496  	})
   497  }
   498  
   499  func mustAbsResourceInstanceAddr(str string) addrs.AbsResourceInstance {
   500  	addr, diags := addrs.ParseAbsResourceInstanceStr(str)
   501  	if diags.HasErrors() {
   502  		panic(fmt.Sprintf("invalid absolute resource instance address: %s", diags.Err()))
   503  	}
   504  	return addr
   505  }
   506  
   507  func mustModuleAddr(str string) addrs.Module {
   508  	if len(str) == 0 {
   509  		return addrs.RootModule
   510  	}
   511  	// We don't have a real parser for these because they don't appear in the
   512  	// language anywhere, but this interpretation mimics the format we
   513  	// produce from the String method on addrs.Module.
   514  	parts := strings.Split(str, ".")
   515  	return addrs.Module(parts)
   516  }
   517  
   518  func mustModuleInstanceAddr(str string) addrs.ModuleInstance {
   519  	if len(str) == 0 {
   520  		return addrs.RootModuleInstance
   521  	}
   522  	addr, diags := addrs.ParseModuleInstanceStr(str)
   523  	if diags.HasErrors() {
   524  		panic(fmt.Sprintf("invalid module instance address: %s", diags.Err()))
   525  	}
   526  	return addr
   527  }
   528  
   529  func valueEquals(a, b cty.Value) bool {
   530  	if a == cty.NilVal || b == cty.NilVal {
   531  		return a == b
   532  	}
   533  	return a.RawEquals(b)
   534  }