github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/states/state_test.go (about)

     1  package states
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/go-test/deep"
     9  	"github.com/zclconf/go-cty/cty"
    10  
    11  	"github.com/hashicorp/terraform/internal/addrs"
    12  	"github.com/hashicorp/terraform/internal/lang/marks"
    13  )
    14  
    15  func TestState(t *testing.T) {
    16  	// This basic tests exercises the main mutation methods to construct
    17  	// a state. It is not fully comprehensive, so other tests should visit
    18  	// more esoteric codepaths.
    19  
    20  	state := NewState()
    21  
    22  	rootModule := state.RootModule()
    23  	if rootModule == nil {
    24  		t.Errorf("root module is nil; want valid object")
    25  	}
    26  
    27  	rootModule.SetLocalValue("foo", cty.StringVal("foo value"))
    28  	rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false)
    29  	rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true)
    30  	rootModule.SetResourceInstanceCurrent(
    31  		addrs.Resource{
    32  			Mode: addrs.ManagedResourceMode,
    33  			Type: "test_thing",
    34  			Name: "baz",
    35  		}.Instance(addrs.IntKey(0)),
    36  		&ResourceInstanceObjectSrc{
    37  			Status:        ObjectReady,
    38  			SchemaVersion: 1,
    39  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
    40  		},
    41  		addrs.AbsProviderConfig{
    42  			Provider: addrs.NewDefaultProvider("test"),
    43  			Module:   addrs.RootModule,
    44  		},
    45  	)
    46  
    47  	childModule := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
    48  	childModule.SetOutputValue("pizza", cty.StringVal("hawaiian"), false)
    49  	multiModA := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")))
    50  	multiModA.SetOutputValue("pizza", cty.StringVal("cheese"), false)
    51  	multiModB := state.EnsureModule(addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")))
    52  	multiModB.SetOutputValue("pizza", cty.StringVal("sausage"), false)
    53  
    54  	want := &State{
    55  		Modules: map[string]*Module{
    56  			"": {
    57  				Addr: addrs.RootModuleInstance,
    58  				LocalValues: map[string]cty.Value{
    59  					"foo": cty.StringVal("foo value"),
    60  				},
    61  				OutputValues: map[string]*OutputValue{
    62  					"bar": {
    63  						Addr: addrs.AbsOutputValue{
    64  							OutputValue: addrs.OutputValue{
    65  								Name: "bar",
    66  							},
    67  						},
    68  						Value:     cty.StringVal("bar value"),
    69  						Sensitive: false,
    70  					},
    71  					"secret": {
    72  						Addr: addrs.AbsOutputValue{
    73  							OutputValue: addrs.OutputValue{
    74  								Name: "secret",
    75  							},
    76  						},
    77  						Value:     cty.StringVal("secret value"),
    78  						Sensitive: true,
    79  					},
    80  				},
    81  				Resources: map[string]*Resource{
    82  					"test_thing.baz": {
    83  						Addr: addrs.Resource{
    84  							Mode: addrs.ManagedResourceMode,
    85  							Type: "test_thing",
    86  							Name: "baz",
    87  						}.Absolute(addrs.RootModuleInstance),
    88  
    89  						Instances: map[addrs.InstanceKey]*ResourceInstance{
    90  							addrs.IntKey(0): {
    91  								Current: &ResourceInstanceObjectSrc{
    92  									SchemaVersion: 1,
    93  									Status:        ObjectReady,
    94  									AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
    95  								},
    96  								Deposed: map[DeposedKey]*ResourceInstanceObjectSrc{},
    97  							},
    98  						},
    99  						ProviderConfig: addrs.AbsProviderConfig{
   100  							Provider: addrs.NewDefaultProvider("test"),
   101  							Module:   addrs.RootModule,
   102  						},
   103  					},
   104  				},
   105  			},
   106  			"module.child": {
   107  				Addr:        addrs.RootModuleInstance.Child("child", addrs.NoKey),
   108  				LocalValues: map[string]cty.Value{},
   109  				OutputValues: map[string]*OutputValue{
   110  					"pizza": {
   111  						Addr: addrs.AbsOutputValue{
   112  							Module: addrs.RootModuleInstance.Child("child", addrs.NoKey),
   113  							OutputValue: addrs.OutputValue{
   114  								Name: "pizza",
   115  							},
   116  						},
   117  						Value:     cty.StringVal("hawaiian"),
   118  						Sensitive: false,
   119  					},
   120  				},
   121  				Resources: map[string]*Resource{},
   122  			},
   123  			`module.multi["a"]`: {
   124  				Addr:        addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")),
   125  				LocalValues: map[string]cty.Value{},
   126  				OutputValues: map[string]*OutputValue{
   127  					"pizza": {
   128  						Addr: addrs.AbsOutputValue{
   129  							Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("a")),
   130  							OutputValue: addrs.OutputValue{
   131  								Name: "pizza",
   132  							},
   133  						},
   134  						Value:     cty.StringVal("cheese"),
   135  						Sensitive: false,
   136  					},
   137  				},
   138  				Resources: map[string]*Resource{},
   139  			},
   140  			`module.multi["b"]`: {
   141  				Addr:        addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")),
   142  				LocalValues: map[string]cty.Value{},
   143  				OutputValues: map[string]*OutputValue{
   144  					"pizza": {
   145  						Addr: addrs.AbsOutputValue{
   146  							Module: addrs.RootModuleInstance.Child("multi", addrs.StringKey("b")),
   147  							OutputValue: addrs.OutputValue{
   148  								Name: "pizza",
   149  							},
   150  						},
   151  						Value:     cty.StringVal("sausage"),
   152  						Sensitive: false,
   153  					},
   154  				},
   155  				Resources: map[string]*Resource{},
   156  			},
   157  		},
   158  	}
   159  
   160  	{
   161  		// Our structure goes deep, so we need to temporarily override the
   162  		// deep package settings to ensure that we visit the full structure.
   163  		oldDeepDepth := deep.MaxDepth
   164  		oldDeepCompareUnexp := deep.CompareUnexportedFields
   165  		deep.MaxDepth = 50
   166  		deep.CompareUnexportedFields = true
   167  		defer func() {
   168  			deep.MaxDepth = oldDeepDepth
   169  			deep.CompareUnexportedFields = oldDeepCompareUnexp
   170  		}()
   171  	}
   172  
   173  	for _, problem := range deep.Equal(state, want) {
   174  		t.Error(problem)
   175  	}
   176  
   177  	expectedOutputs := map[string]string{
   178  		`module.multi["a"].output.pizza`: "cheese",
   179  		`module.multi["b"].output.pizza`: "sausage",
   180  	}
   181  
   182  	for _, o := range state.ModuleOutputs(addrs.RootModuleInstance, addrs.ModuleCall{Name: "multi"}) {
   183  		addr := o.Addr.String()
   184  		expected := expectedOutputs[addr]
   185  		delete(expectedOutputs, addr)
   186  
   187  		if expected != o.Value.AsString() {
   188  			t.Fatalf("expected %q:%q, got %q", addr, expected, o.Value.AsString())
   189  		}
   190  	}
   191  
   192  	for addr, o := range expectedOutputs {
   193  		t.Fatalf("missing output %q:%q", addr, o)
   194  	}
   195  }
   196  
   197  func TestStateDeepCopyObject(t *testing.T) {
   198  	obj := &ResourceInstanceObject{
   199  		Value: cty.ObjectVal(map[string]cty.Value{
   200  			"id": cty.StringVal("id"),
   201  		}),
   202  		Private: []byte("private"),
   203  		Status:  ObjectReady,
   204  		Dependencies: []addrs.ConfigResource{
   205  			{
   206  				Module: addrs.RootModule,
   207  				Resource: addrs.Resource{
   208  					Mode: addrs.ManagedResourceMode,
   209  					Type: "test_instance",
   210  					Name: "bar",
   211  				},
   212  			},
   213  		},
   214  		CreateBeforeDestroy: true,
   215  	}
   216  
   217  	objCopy := obj.DeepCopy()
   218  	if !reflect.DeepEqual(obj, objCopy) {
   219  		t.Fatalf("not equal\n%#v\n%#v", obj, objCopy)
   220  	}
   221  }
   222  
   223  func TestStateDeepCopy(t *testing.T) {
   224  	state := NewState()
   225  
   226  	rootModule := state.RootModule()
   227  	if rootModule == nil {
   228  		t.Errorf("root module is nil; want valid object")
   229  	}
   230  
   231  	rootModule.SetLocalValue("foo", cty.StringVal("foo value"))
   232  	rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false)
   233  	rootModule.SetOutputValue("secret", cty.StringVal("secret value"), true)
   234  	rootModule.SetResourceInstanceCurrent(
   235  		addrs.Resource{
   236  			Mode: addrs.ManagedResourceMode,
   237  			Type: "test_thing",
   238  			Name: "baz",
   239  		}.Instance(addrs.IntKey(0)),
   240  		&ResourceInstanceObjectSrc{
   241  			Status:              ObjectReady,
   242  			SchemaVersion:       1,
   243  			AttrsJSON:           []byte(`{"woozles":"confuzles"}`),
   244  			Private:             []byte("private data"),
   245  			Dependencies:        []addrs.ConfigResource{},
   246  			CreateBeforeDestroy: true,
   247  		},
   248  		addrs.AbsProviderConfig{
   249  			Provider: addrs.NewDefaultProvider("test"),
   250  			Module:   addrs.RootModule,
   251  		},
   252  	)
   253  	rootModule.SetResourceInstanceCurrent(
   254  		addrs.Resource{
   255  			Mode: addrs.ManagedResourceMode,
   256  			Type: "test_thing",
   257  			Name: "bar",
   258  		}.Instance(addrs.IntKey(0)),
   259  		&ResourceInstanceObjectSrc{
   260  			Status:        ObjectReady,
   261  			SchemaVersion: 1,
   262  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   263  			// Sensitive path at "woozles"
   264  			AttrSensitivePaths: []cty.PathValueMarks{
   265  				{
   266  					Path:  cty.Path{cty.GetAttrStep{Name: "woozles"}},
   267  					Marks: cty.NewValueMarks(marks.Sensitive),
   268  				},
   269  			},
   270  			Private: []byte("private data"),
   271  			Dependencies: []addrs.ConfigResource{
   272  				{
   273  					Module: addrs.RootModule,
   274  					Resource: addrs.Resource{
   275  						Mode: addrs.ManagedResourceMode,
   276  						Type: "test_thing",
   277  						Name: "baz",
   278  					},
   279  				},
   280  			},
   281  		},
   282  		addrs.AbsProviderConfig{
   283  			Provider: addrs.NewDefaultProvider("test"),
   284  			Module:   addrs.RootModule,
   285  		},
   286  	)
   287  
   288  	childModule := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
   289  	childModule.SetOutputValue("pizza", cty.StringVal("hawaiian"), false)
   290  
   291  	stateCopy := state.DeepCopy()
   292  	if !state.Equal(stateCopy) {
   293  		t.Fatalf("\nexpected:\n%q\ngot:\n%q\n", state, stateCopy)
   294  	}
   295  }
   296  
   297  func TestState_MoveAbsResource(t *testing.T) {
   298  	// Set up a starter state for the embedded tests, which should start from a copy of this state.
   299  	state := NewState()
   300  	rootModule := state.RootModule()
   301  	rootModule.SetResourceInstanceCurrent(
   302  		addrs.Resource{
   303  			Mode: addrs.ManagedResourceMode,
   304  			Type: "test_thing",
   305  			Name: "foo",
   306  		}.Instance(addrs.IntKey(0)),
   307  		&ResourceInstanceObjectSrc{
   308  			Status:        ObjectReady,
   309  			SchemaVersion: 1,
   310  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   311  		},
   312  		addrs.AbsProviderConfig{
   313  			Provider: addrs.NewDefaultProvider("test"),
   314  			Module:   addrs.RootModule,
   315  		},
   316  	)
   317  	src := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "foo"}.Absolute(addrs.RootModuleInstance)
   318  
   319  	t.Run("basic move", func(t *testing.T) {
   320  		s := state.DeepCopy()
   321  		dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "bar"}.Absolute(addrs.RootModuleInstance)
   322  
   323  		s.MoveAbsResource(src, dst)
   324  
   325  		if s.Empty() {
   326  			t.Fatal("unexpected empty state")
   327  		}
   328  
   329  		if len(s.RootModule().Resources) != 1 {
   330  			t.Fatalf("wrong number of resources in state; expected 1, found %d", len(state.RootModule().Resources))
   331  		}
   332  
   333  		got := s.Resource(dst)
   334  		if got.Addr.Resource != dst.Resource {
   335  			t.Fatalf("dst resource not in state")
   336  		}
   337  	})
   338  
   339  	t.Run("move to new module", func(t *testing.T) {
   340  		s := state.DeepCopy()
   341  		dstModule := addrs.RootModuleInstance.Child("kinder", addrs.StringKey("one"))
   342  		dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "bar"}.Absolute(dstModule)
   343  
   344  		s.MoveAbsResource(src, dst)
   345  
   346  		if s.Empty() {
   347  			t.Fatal("unexpected empty state")
   348  		}
   349  
   350  		if s.Module(dstModule) == nil {
   351  			t.Fatalf("child module %s not in state", dstModule.String())
   352  		}
   353  
   354  		if len(s.Module(dstModule).Resources) != 1 {
   355  			t.Fatalf("wrong number of resources in state; expected 1, found %d", len(s.Module(dstModule).Resources))
   356  		}
   357  
   358  		got := s.Resource(dst)
   359  		if got.Addr.Resource != dst.Resource {
   360  			t.Fatalf("dst resource not in state")
   361  		}
   362  	})
   363  
   364  	t.Run("from a child module to root", func(t *testing.T) {
   365  		s := state.DeepCopy()
   366  		srcModule := addrs.RootModuleInstance.Child("kinder", addrs.NoKey)
   367  		cm := s.EnsureModule(srcModule)
   368  		cm.SetResourceInstanceCurrent(
   369  			addrs.Resource{
   370  				Mode: addrs.ManagedResourceMode,
   371  				Type: "test_thing",
   372  				Name: "child",
   373  			}.Instance(addrs.IntKey(0)), // Moving the AbsResouce moves all instances
   374  			&ResourceInstanceObjectSrc{
   375  				Status:        ObjectReady,
   376  				SchemaVersion: 1,
   377  				AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   378  			},
   379  			addrs.AbsProviderConfig{
   380  				Provider: addrs.NewDefaultProvider("test"),
   381  				Module:   addrs.RootModule,
   382  			},
   383  		)
   384  		cm.SetResourceInstanceCurrent(
   385  			addrs.Resource{
   386  				Mode: addrs.ManagedResourceMode,
   387  				Type: "test_thing",
   388  				Name: "child",
   389  			}.Instance(addrs.IntKey(1)), // Moving the AbsResouce moves all instances
   390  			&ResourceInstanceObjectSrc{
   391  				Status:        ObjectReady,
   392  				SchemaVersion: 1,
   393  				AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   394  			},
   395  			addrs.AbsProviderConfig{
   396  				Provider: addrs.NewDefaultProvider("test"),
   397  				Module:   addrs.RootModule,
   398  			},
   399  		)
   400  
   401  		src := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "child"}.Absolute(srcModule)
   402  		dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "child"}.Absolute(addrs.RootModuleInstance)
   403  		s.MoveAbsResource(src, dst)
   404  
   405  		if s.Empty() {
   406  			t.Fatal("unexpected empty state")
   407  		}
   408  
   409  		// The child module should have been removed after removing its only resource
   410  		if s.Module(srcModule) != nil {
   411  			t.Fatalf("child module %s was not removed from state after mv", srcModule.String())
   412  		}
   413  
   414  		if len(s.RootModule().Resources) != 2 {
   415  			t.Fatalf("wrong number of resources in state; expected 2, found %d", len(s.RootModule().Resources))
   416  		}
   417  
   418  		if len(s.Resource(dst).Instances) != 2 {
   419  			t.Fatalf("wrong number of resource instances for dst, got %d expected 2", len(s.Resource(dst).Instances))
   420  		}
   421  
   422  		got := s.Resource(dst)
   423  		if got.Addr.Resource != dst.Resource {
   424  			t.Fatalf("dst resource not in state")
   425  		}
   426  	})
   427  
   428  	t.Run("module to new module", func(t *testing.T) {
   429  		s := NewState()
   430  		srcModule := addrs.RootModuleInstance.Child("kinder", addrs.StringKey("exists"))
   431  		dstModule := addrs.RootModuleInstance.Child("kinder", addrs.StringKey("new"))
   432  		cm := s.EnsureModule(srcModule)
   433  		cm.SetResourceInstanceCurrent(
   434  			addrs.Resource{
   435  				Mode: addrs.ManagedResourceMode,
   436  				Type: "test_thing",
   437  				Name: "child",
   438  			}.Instance(addrs.NoKey),
   439  			&ResourceInstanceObjectSrc{
   440  				Status:        ObjectReady,
   441  				SchemaVersion: 1,
   442  				AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   443  			},
   444  			addrs.AbsProviderConfig{
   445  				Provider: addrs.NewDefaultProvider("test"),
   446  				Module:   addrs.RootModule,
   447  			},
   448  		)
   449  
   450  		src := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "child"}.Absolute(srcModule)
   451  		dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "child"}.Absolute(dstModule)
   452  		s.MoveAbsResource(src, dst)
   453  
   454  		if s.Empty() {
   455  			t.Fatal("unexpected empty state")
   456  		}
   457  
   458  		// The child module should have been removed after removing its only resource
   459  		if s.Module(srcModule) != nil {
   460  			t.Fatalf("child module %s was not removed from state after mv", srcModule.String())
   461  		}
   462  
   463  		gotMod := s.Module(dstModule)
   464  		if len(gotMod.Resources) != 1 {
   465  			t.Fatalf("wrong number of resources in state; expected 1, found %d", len(gotMod.Resources))
   466  		}
   467  
   468  		got := s.Resource(dst)
   469  		if got.Addr.Resource != dst.Resource {
   470  			t.Fatalf("dst resource not in state")
   471  		}
   472  	})
   473  
   474  	t.Run("module to new module", func(t *testing.T) {
   475  		s := NewState()
   476  		srcModule := addrs.RootModuleInstance.Child("kinder", addrs.StringKey("exists"))
   477  		dstModule := addrs.RootModuleInstance.Child("kinder", addrs.StringKey("new"))
   478  		cm := s.EnsureModule(srcModule)
   479  		cm.SetResourceInstanceCurrent(
   480  			addrs.Resource{
   481  				Mode: addrs.ManagedResourceMode,
   482  				Type: "test_thing",
   483  				Name: "child",
   484  			}.Instance(addrs.NoKey),
   485  			&ResourceInstanceObjectSrc{
   486  				Status:        ObjectReady,
   487  				SchemaVersion: 1,
   488  				AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   489  			},
   490  			addrs.AbsProviderConfig{
   491  				Provider: addrs.NewDefaultProvider("test"),
   492  				Module:   addrs.RootModule,
   493  			},
   494  		)
   495  
   496  		src := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "child"}.Absolute(srcModule)
   497  		dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "child"}.Absolute(dstModule)
   498  		s.MoveAbsResource(src, dst)
   499  
   500  		if s.Empty() {
   501  			t.Fatal("unexpected empty state")
   502  		}
   503  
   504  		// The child module should have been removed after removing its only resource
   505  		if s.Module(srcModule) != nil {
   506  			t.Fatalf("child module %s was not removed from state after mv", srcModule.String())
   507  		}
   508  
   509  		gotMod := s.Module(dstModule)
   510  		if len(gotMod.Resources) != 1 {
   511  			t.Fatalf("wrong number of resources in state; expected 1, found %d", len(gotMod.Resources))
   512  		}
   513  
   514  		got := s.Resource(dst)
   515  		if got.Addr.Resource != dst.Resource {
   516  			t.Fatalf("dst resource not in state")
   517  		}
   518  	})
   519  }
   520  
   521  func TestState_MaybeMoveAbsResource(t *testing.T) {
   522  	state := NewState()
   523  	rootModule := state.RootModule()
   524  	rootModule.SetResourceInstanceCurrent(
   525  		addrs.Resource{
   526  			Mode: addrs.ManagedResourceMode,
   527  			Type: "test_thing",
   528  			Name: "foo",
   529  		}.Instance(addrs.IntKey(0)),
   530  		&ResourceInstanceObjectSrc{
   531  			Status:        ObjectReady,
   532  			SchemaVersion: 1,
   533  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   534  		},
   535  		addrs.AbsProviderConfig{
   536  			Provider: addrs.NewDefaultProvider("test"),
   537  			Module:   addrs.RootModule,
   538  		},
   539  	)
   540  
   541  	src := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "foo"}.Absolute(addrs.RootModuleInstance)
   542  	dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "bar"}.Absolute(addrs.RootModuleInstance)
   543  
   544  	// First move, success
   545  	t.Run("first move", func(t *testing.T) {
   546  		moved := state.MaybeMoveAbsResource(src, dst)
   547  		if !moved {
   548  			t.Fatal("wrong result")
   549  		}
   550  	})
   551  
   552  	// Trying to move a resource that doesn't exist in state to a resource which does exist should be a noop.
   553  	t.Run("noop", func(t *testing.T) {
   554  		moved := state.MaybeMoveAbsResource(src, dst)
   555  		if moved {
   556  			t.Fatal("wrong result")
   557  		}
   558  	})
   559  }
   560  
   561  func TestState_MoveAbsResourceInstance(t *testing.T) {
   562  	state := NewState()
   563  	rootModule := state.RootModule()
   564  	rootModule.SetResourceInstanceCurrent(
   565  		addrs.Resource{
   566  			Mode: addrs.ManagedResourceMode,
   567  			Type: "test_thing",
   568  			Name: "foo",
   569  		}.Instance(addrs.NoKey),
   570  		&ResourceInstanceObjectSrc{
   571  			Status:        ObjectReady,
   572  			SchemaVersion: 1,
   573  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   574  		},
   575  		addrs.AbsProviderConfig{
   576  			Provider: addrs.NewDefaultProvider("test"),
   577  			Module:   addrs.RootModule,
   578  		},
   579  	)
   580  	// src resource from the state above
   581  	src := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "foo"}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
   582  
   583  	t.Run("resource to resource instance", func(t *testing.T) {
   584  		s := state.DeepCopy()
   585  		// For a little extra fun, move a resource to a resource instance: test_thing.foo to test_thing.foo[1]
   586  		dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "foo"}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance)
   587  
   588  		s.MoveAbsResourceInstance(src, dst)
   589  
   590  		if s.Empty() {
   591  			t.Fatal("unexpected empty state")
   592  		}
   593  
   594  		if len(s.RootModule().Resources) != 1 {
   595  			t.Fatalf("wrong number of resources in state; expected 1, found %d", len(state.RootModule().Resources))
   596  		}
   597  
   598  		got := s.ResourceInstance(dst)
   599  		if got == nil {
   600  			t.Fatalf("dst resource not in state")
   601  		}
   602  	})
   603  
   604  	t.Run("move to new module", func(t *testing.T) {
   605  		s := state.DeepCopy()
   606  		// test_thing.foo to module.kinder.test_thing.foo["baz"]
   607  		dstModule := addrs.RootModuleInstance.Child("kinder", addrs.NoKey)
   608  		dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "foo"}.Instance(addrs.IntKey(1)).Absolute(dstModule)
   609  
   610  		s.MoveAbsResourceInstance(src, dst)
   611  
   612  		if s.Empty() {
   613  			t.Fatal("unexpected empty state")
   614  		}
   615  
   616  		if s.Module(dstModule) == nil {
   617  			t.Fatalf("child module %s not in state", dstModule.String())
   618  		}
   619  
   620  		if len(s.Module(dstModule).Resources) != 1 {
   621  			t.Fatalf("wrong number of resources in state; expected 1, found %d", len(s.Module(dstModule).Resources))
   622  		}
   623  
   624  		got := s.ResourceInstance(dst)
   625  		if got == nil {
   626  			t.Fatalf("dst resource not in state")
   627  		}
   628  	})
   629  }
   630  
   631  func TestState_MaybeMoveAbsResourceInstance(t *testing.T) {
   632  	state := NewState()
   633  	rootModule := state.RootModule()
   634  	rootModule.SetResourceInstanceCurrent(
   635  		addrs.Resource{
   636  			Mode: addrs.ManagedResourceMode,
   637  			Type: "test_thing",
   638  			Name: "foo",
   639  		}.Instance(addrs.NoKey),
   640  		&ResourceInstanceObjectSrc{
   641  			Status:        ObjectReady,
   642  			SchemaVersion: 1,
   643  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   644  		},
   645  		addrs.AbsProviderConfig{
   646  			Provider: addrs.NewDefaultProvider("test"),
   647  			Module:   addrs.RootModule,
   648  		},
   649  	)
   650  
   651  	// For a little extra fun, let's go from a resource to a resource instance: test_thing.foo to test_thing.bar[1]
   652  	src := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "foo"}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
   653  	dst := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_thing", Name: "foo"}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance)
   654  
   655  	// First move, success
   656  	t.Run("first move", func(t *testing.T) {
   657  		moved := state.MaybeMoveAbsResourceInstance(src, dst)
   658  		if !moved {
   659  			t.Fatal("wrong result")
   660  		}
   661  		got := state.ResourceInstance(dst)
   662  		if got == nil {
   663  			t.Fatal("destination resource instance not in state")
   664  		}
   665  	})
   666  
   667  	// Moving a resource instance that doesn't exist in state to a resource which does exist should be a noop.
   668  	t.Run("noop", func(t *testing.T) {
   669  		moved := state.MaybeMoveAbsResourceInstance(src, dst)
   670  		if moved {
   671  			t.Fatal("wrong result")
   672  		}
   673  	})
   674  }
   675  
   676  func TestState_MoveModuleInstance(t *testing.T) {
   677  	state := NewState()
   678  	srcModule := addrs.RootModuleInstance.Child("kinder", addrs.NoKey)
   679  	m := state.EnsureModule(srcModule)
   680  	m.SetResourceInstanceCurrent(
   681  		addrs.Resource{
   682  			Mode: addrs.ManagedResourceMode,
   683  			Type: "test_thing",
   684  			Name: "foo",
   685  		}.Instance(addrs.NoKey),
   686  		&ResourceInstanceObjectSrc{
   687  			Status:        ObjectReady,
   688  			SchemaVersion: 1,
   689  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   690  		},
   691  		addrs.AbsProviderConfig{
   692  			Provider: addrs.NewDefaultProvider("test"),
   693  			Module:   addrs.RootModule,
   694  		},
   695  	)
   696  
   697  	dstModule := addrs.RootModuleInstance.Child("child", addrs.IntKey(3))
   698  	state.MoveModuleInstance(srcModule, dstModule)
   699  
   700  	// srcModule should have been removed, dstModule should exist and have one resource
   701  	if len(state.Modules) != 2 { // kinder[3] and root
   702  		t.Fatalf("wrong number of modules in state. Expected 2, got %d", len(state.Modules))
   703  	}
   704  
   705  	got := state.Module(dstModule)
   706  	if got == nil {
   707  		t.Fatal("dstModule not found")
   708  	}
   709  
   710  	gone := state.Module(srcModule)
   711  	if gone != nil {
   712  		t.Fatal("srcModule not removed from state")
   713  	}
   714  
   715  	r := got.Resource(mustAbsResourceAddr("test_thing.foo").Resource)
   716  	if r.Addr.Module.String() != dstModule.String() {
   717  		fmt.Println(r.Addr.Module.String())
   718  		t.Fatal("resource address was not updated")
   719  	}
   720  
   721  }
   722  
   723  func TestState_MaybeMoveModuleInstance(t *testing.T) {
   724  	state := NewState()
   725  	src := addrs.RootModuleInstance.Child("child", addrs.StringKey("a"))
   726  	cm := state.EnsureModule(src)
   727  	cm.SetResourceInstanceCurrent(
   728  		addrs.Resource{
   729  			Mode: addrs.ManagedResourceMode,
   730  			Type: "test_thing",
   731  			Name: "foo",
   732  		}.Instance(addrs.NoKey),
   733  		&ResourceInstanceObjectSrc{
   734  			Status:        ObjectReady,
   735  			SchemaVersion: 1,
   736  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   737  		},
   738  		addrs.AbsProviderConfig{
   739  			Provider: addrs.NewDefaultProvider("test"),
   740  			Module:   addrs.RootModule,
   741  		},
   742  	)
   743  
   744  	dst := addrs.RootModuleInstance.Child("kinder", addrs.StringKey("b"))
   745  
   746  	// First move, success
   747  	t.Run("first move", func(t *testing.T) {
   748  		moved := state.MaybeMoveModuleInstance(src, dst)
   749  		if !moved {
   750  			t.Fatal("wrong result")
   751  		}
   752  	})
   753  
   754  	// Second move, should be a noop
   755  	t.Run("noop", func(t *testing.T) {
   756  		moved := state.MaybeMoveModuleInstance(src, dst)
   757  		if moved {
   758  			t.Fatal("wrong result")
   759  		}
   760  	})
   761  }
   762  
   763  func TestState_MoveModule(t *testing.T) {
   764  	// For this test, add two module instances (kinder and kinder["a"]).
   765  	// MoveModule(kinder) should move both instances.
   766  	state := NewState() // starter state, should be copied by the subtests.
   767  	srcModule := addrs.RootModule.Child("kinder")
   768  	m := state.EnsureModule(srcModule.UnkeyedInstanceShim())
   769  	m.SetResourceInstanceCurrent(
   770  		addrs.Resource{
   771  			Mode: addrs.ManagedResourceMode,
   772  			Type: "test_thing",
   773  			Name: "foo",
   774  		}.Instance(addrs.NoKey),
   775  		&ResourceInstanceObjectSrc{
   776  			Status:        ObjectReady,
   777  			SchemaVersion: 1,
   778  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   779  		},
   780  		addrs.AbsProviderConfig{
   781  			Provider: addrs.NewDefaultProvider("test"),
   782  			Module:   addrs.RootModule,
   783  		},
   784  	)
   785  
   786  	moduleInstance := addrs.RootModuleInstance.Child("kinder", addrs.StringKey("a"))
   787  	mi := state.EnsureModule(moduleInstance)
   788  	mi.SetResourceInstanceCurrent(
   789  		addrs.Resource{
   790  			Mode: addrs.ManagedResourceMode,
   791  			Type: "test_thing",
   792  			Name: "foo",
   793  		}.Instance(addrs.NoKey),
   794  		&ResourceInstanceObjectSrc{
   795  			Status:        ObjectReady,
   796  			SchemaVersion: 1,
   797  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   798  		},
   799  		addrs.AbsProviderConfig{
   800  			Provider: addrs.NewDefaultProvider("test"),
   801  			Module:   addrs.RootModule,
   802  		},
   803  	)
   804  
   805  	_, mc := srcModule.Call()
   806  	src := mc.Absolute(addrs.RootModuleInstance.Child("kinder", addrs.NoKey))
   807  
   808  	t.Run("basic", func(t *testing.T) {
   809  		s := state.DeepCopy()
   810  		_, dstMC := addrs.RootModule.Child("child").Call()
   811  		dst := dstMC.Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey))
   812  		s.MoveModule(src, dst)
   813  
   814  		// srcModule should have been removed, dstModule should exist and have one resource
   815  		if len(s.Modules) != 3 { // child, child["a"] and root
   816  			t.Fatalf("wrong number of modules in state. Expected 3, got %d", len(s.Modules))
   817  		}
   818  
   819  		got := s.Module(dst.Module)
   820  		if got == nil {
   821  			t.Fatal("dstModule not found")
   822  		}
   823  
   824  		got = s.Module(addrs.RootModuleInstance.Child("child", addrs.StringKey("a")))
   825  		if got == nil {
   826  			t.Fatal("dstModule instance \"a\" not found")
   827  		}
   828  
   829  		gone := s.Module(srcModule.UnkeyedInstanceShim())
   830  		if gone != nil {
   831  			t.Fatal("srcModule not removed from state")
   832  		}
   833  	})
   834  
   835  	t.Run("nested modules", func(t *testing.T) {
   836  		s := state.DeepCopy()
   837  
   838  		// add a child module to module.kinder
   839  		mi := mustParseModuleInstanceStr(`module.kinder.module.grand[1]`)
   840  		m := s.EnsureModule(mi)
   841  		m.SetResourceInstanceCurrent(
   842  			addrs.Resource{
   843  				Mode: addrs.ManagedResourceMode,
   844  				Type: "test_thing",
   845  				Name: "foo",
   846  			}.Instance(addrs.NoKey),
   847  			&ResourceInstanceObjectSrc{
   848  				Status:        ObjectReady,
   849  				SchemaVersion: 1,
   850  				AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   851  			},
   852  			addrs.AbsProviderConfig{
   853  				Provider: addrs.NewDefaultProvider("test"),
   854  				Module:   addrs.RootModule,
   855  			},
   856  		)
   857  
   858  		_, dstMC := addrs.RootModule.Child("child").Call()
   859  		dst := dstMC.Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey))
   860  		s.MoveModule(src, dst)
   861  
   862  		moved := s.Module(addrs.RootModuleInstance.Child("child", addrs.StringKey("a")))
   863  		if moved == nil {
   864  			t.Fatal("dstModule not found")
   865  		}
   866  
   867  		// The nested module's relative address should also have been updated
   868  		nested := s.Module(mustParseModuleInstanceStr(`module.child.module.grand[1]`))
   869  		if nested == nil {
   870  			t.Fatal("nested child module of src wasn't moved")
   871  		}
   872  	})
   873  }
   874  
   875  func mustParseModuleInstanceStr(str string) addrs.ModuleInstance {
   876  	addr, diags := addrs.ParseModuleInstanceStr(str)
   877  	if diags.HasErrors() {
   878  		panic(diags.Err())
   879  	}
   880  	return addr
   881  }
   882  
   883  func mustAbsResourceAddr(s string) addrs.AbsResource {
   884  	addr, diags := addrs.ParseAbsResourceStr(s)
   885  	if diags.HasErrors() {
   886  		panic(diags.Err())
   887  	}
   888  	return addr
   889  }