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 }