github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/refactoring/move_statement_test.go (about)

     1  package refactoring
     2  
     3  import (
     4  	"sort"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  
     9  	"github.com/hashicorp/terraform/internal/addrs"
    10  	"github.com/hashicorp/terraform/internal/states"
    11  	"github.com/hashicorp/terraform/internal/tfdiags"
    12  )
    13  
    14  func TestImpliedMoveStatements(t *testing.T) {
    15  	resourceAddr := func(name string) addrs.AbsResource {
    16  		return addrs.Resource{
    17  			Mode: addrs.ManagedResourceMode,
    18  			Type: "foo",
    19  			Name: name,
    20  		}.Absolute(addrs.RootModuleInstance)
    21  	}
    22  
    23  	nestedResourceAddr := func(mod, name string) addrs.AbsResource {
    24  		return addrs.Resource{
    25  			Mode: addrs.ManagedResourceMode,
    26  			Type: "foo",
    27  			Name: name,
    28  		}.Absolute(addrs.RootModuleInstance.Child(mod, addrs.NoKey))
    29  	}
    30  
    31  	instObjState := func() *states.ResourceInstanceObjectSrc {
    32  		return &states.ResourceInstanceObjectSrc{}
    33  	}
    34  	providerAddr := addrs.AbsProviderConfig{
    35  		Module:   addrs.RootModule,
    36  		Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
    37  	}
    38  
    39  	rootCfg, _ := loadRefactoringFixture(t, "testdata/move-statement-implied")
    40  	prevRunState := states.BuildState(func(s *states.SyncState) {
    41  		s.SetResourceInstanceCurrent(
    42  			resourceAddr("formerly_count").Instance(addrs.IntKey(0)),
    43  			instObjState(),
    44  			providerAddr,
    45  		)
    46  		s.SetResourceInstanceCurrent(
    47  			resourceAddr("formerly_count").Instance(addrs.IntKey(1)),
    48  			instObjState(),
    49  			providerAddr,
    50  		)
    51  		s.SetResourceInstanceCurrent(
    52  			resourceAddr("now_count").Instance(addrs.NoKey),
    53  			instObjState(),
    54  			providerAddr,
    55  		)
    56  		s.SetResourceInstanceCurrent(
    57  			resourceAddr("formerly_count_explicit").Instance(addrs.IntKey(0)),
    58  			instObjState(),
    59  			providerAddr,
    60  		)
    61  		s.SetResourceInstanceCurrent(
    62  			resourceAddr("formerly_count_explicit").Instance(addrs.IntKey(1)),
    63  			instObjState(),
    64  			providerAddr,
    65  		)
    66  		s.SetResourceInstanceCurrent(
    67  			resourceAddr("now_count_explicit").Instance(addrs.NoKey),
    68  			instObjState(),
    69  			providerAddr,
    70  		)
    71  		s.SetResourceInstanceCurrent(
    72  			resourceAddr("now_for_each_formerly_count").Instance(addrs.IntKey(0)),
    73  			instObjState(),
    74  			providerAddr,
    75  		)
    76  		s.SetResourceInstanceCurrent(
    77  			resourceAddr("now_for_each_formerly_no_count").Instance(addrs.NoKey),
    78  			instObjState(),
    79  			providerAddr,
    80  		)
    81  
    82  		// This "ambiguous" resource is representing a rare but possible
    83  		// situation where we end up having a mixture of different index
    84  		// types in the state at the same time. The main way to get into
    85  		// this state would be to remove "count = 1" and then have the
    86  		// provider fail to destroy the zero-key instance even though we
    87  		// already created the no-key instance. Users can also get here
    88  		// by using "terraform state mv" in weird ways.
    89  		s.SetResourceInstanceCurrent(
    90  			resourceAddr("ambiguous").Instance(addrs.NoKey),
    91  			instObjState(),
    92  			providerAddr,
    93  		)
    94  		s.SetResourceInstanceCurrent(
    95  			resourceAddr("ambiguous").Instance(addrs.IntKey(0)),
    96  			instObjState(),
    97  			providerAddr,
    98  		)
    99  
   100  		// Add two resource nested in a module to ensure we find these
   101  		// recursively.
   102  		s.SetResourceInstanceCurrent(
   103  			nestedResourceAddr("child", "formerly_count").Instance(addrs.IntKey(0)),
   104  			instObjState(),
   105  			providerAddr,
   106  		)
   107  		s.SetResourceInstanceCurrent(
   108  			nestedResourceAddr("child", "now_count").Instance(addrs.NoKey),
   109  			instObjState(),
   110  			providerAddr,
   111  		)
   112  	})
   113  
   114  	explicitStmts := FindMoveStatements(rootCfg)
   115  	got := ImpliedMoveStatements(rootCfg, prevRunState, explicitStmts)
   116  	want := []MoveStatement{
   117  		{
   118  			From:    addrs.ImpliedMoveStatementEndpoint(resourceAddr("formerly_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
   119  			To:      addrs.ImpliedMoveStatementEndpoint(resourceAddr("formerly_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
   120  			Implied: true,
   121  			DeclRange: tfdiags.SourceRange{
   122  				Filename: "testdata/move-statement-implied/move-statement-implied.tf",
   123  				Start:    tfdiags.SourcePos{Line: 5, Column: 1, Byte: 180},
   124  				End:      tfdiags.SourcePos{Line: 5, Column: 32, Byte: 211},
   125  			},
   126  		},
   127  
   128  		// Found implied moves in a nested module, ignoring the explicit moves
   129  		{
   130  			From:    addrs.ImpliedMoveStatementEndpoint(nestedResourceAddr("child", "formerly_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
   131  			To:      addrs.ImpliedMoveStatementEndpoint(nestedResourceAddr("child", "formerly_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
   132  			Implied: true,
   133  			DeclRange: tfdiags.SourceRange{
   134  				Filename: "testdata/move-statement-implied/child/move-statement-implied.tf",
   135  				Start:    tfdiags.SourcePos{Line: 5, Column: 1, Byte: 180},
   136  				End:      tfdiags.SourcePos{Line: 5, Column: 32, Byte: 211},
   137  			},
   138  		},
   139  
   140  		{
   141  			From:    addrs.ImpliedMoveStatementEndpoint(resourceAddr("now_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
   142  			To:      addrs.ImpliedMoveStatementEndpoint(resourceAddr("now_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
   143  			Implied: true,
   144  			DeclRange: tfdiags.SourceRange{
   145  				Filename: "testdata/move-statement-implied/move-statement-implied.tf",
   146  				Start:    tfdiags.SourcePos{Line: 10, Column: 11, Byte: 282},
   147  				End:      tfdiags.SourcePos{Line: 10, Column: 12, Byte: 283},
   148  			},
   149  		},
   150  
   151  		// Found implied moves in a nested module, ignoring the explicit moves
   152  		{
   153  			From:    addrs.ImpliedMoveStatementEndpoint(nestedResourceAddr("child", "now_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
   154  			To:      addrs.ImpliedMoveStatementEndpoint(nestedResourceAddr("child", "now_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
   155  			Implied: true,
   156  			DeclRange: tfdiags.SourceRange{
   157  				Filename: "testdata/move-statement-implied/child/move-statement-implied.tf",
   158  				Start:    tfdiags.SourcePos{Line: 10, Column: 11, Byte: 282},
   159  				End:      tfdiags.SourcePos{Line: 10, Column: 12, Byte: 283},
   160  			},
   161  		},
   162  
   163  		// We generate foo.ambiguous[0] to foo.ambiguous here, even though
   164  		// there's already a foo.ambiguous in the state, because it's the
   165  		// responsibility of the later ApplyMoves step to deal with the
   166  		// situation where an object wants to move into an address already
   167  		// occupied by another object.
   168  		{
   169  			From:    addrs.ImpliedMoveStatementEndpoint(resourceAddr("ambiguous").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
   170  			To:      addrs.ImpliedMoveStatementEndpoint(resourceAddr("ambiguous").Instance(addrs.NoKey), tfdiags.SourceRange{}),
   171  			Implied: true,
   172  			DeclRange: tfdiags.SourceRange{
   173  				Filename: "testdata/move-statement-implied/move-statement-implied.tf",
   174  				Start:    tfdiags.SourcePos{Line: 46, Column: 1, Byte: 806},
   175  				End:      tfdiags.SourcePos{Line: 46, Column: 27, Byte: 832},
   176  			},
   177  		},
   178  	}
   179  
   180  	sort.Slice(got, func(i, j int) bool {
   181  		// This is just an arbitrary sort to make the result consistent
   182  		// regardless of what order the ImpliedMoveStatements function
   183  		// visits the entries in the state/config.
   184  		return got[i].DeclRange.Start.Line < got[j].DeclRange.Start.Line
   185  	})
   186  
   187  	if diff := cmp.Diff(want, got); diff != "" {
   188  		t.Errorf("wrong result\n%s", diff)
   189  	}
   190  }