Files
opentf/internal/refactoring/move_statement_test.go
Martin Atkins ef5a1c9cfe refactoring: ImpliedMoveStatements function
This new function complements the existing function FindMoveStatements
by potentially generating additional "implied" move statements that aren't
written explicit in the configuration but that we'll infer by comparing
the configuration and te previous run state.

The goal here is to infer only enough to replicate the effect of the
"count boundary fixup" graph node (terraform.NodeCountBoundary) that we
currently use to deal with this concern of preserving the zero-instance
when switching between "count" and not "count".

This is just dead code for now. A subsequent commit will introduce this
into the "terraform" package while also removing
terraform.NodeCountBoundary, thus achieving the same effect as before but
in a way that'll get reported in the UI as a move, using the same language
that we'd use for an explicit move statement.
2021-09-20 09:06:22 -07:00

133 lines
4.7 KiB
Go

package refactoring
import (
"sort"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
func TestImpliedMoveStatements(t *testing.T) {
resourceAddr := func(name string) addrs.AbsResource {
return addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "foo",
Name: name,
}.Absolute(addrs.RootModuleInstance)
}
instObjState := func() *states.ResourceInstanceObjectSrc {
return &states.ResourceInstanceObjectSrc{}
}
providerAddr := addrs.AbsProviderConfig{
Module: addrs.RootModule,
Provider: addrs.MustParseProviderSourceString("hashicorp/foo"),
}
rootCfg, _ := loadRefactoringFixture(t, "testdata/move-statement-implied")
prevRunState := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(
resourceAddr("formerly_count").Instance(addrs.IntKey(0)),
instObjState(),
providerAddr,
)
s.SetResourceInstanceCurrent(
resourceAddr("formerly_count").Instance(addrs.IntKey(1)),
instObjState(),
providerAddr,
)
s.SetResourceInstanceCurrent(
resourceAddr("now_count").Instance(addrs.NoKey),
instObjState(),
providerAddr,
)
s.SetResourceInstanceCurrent(
resourceAddr("formerly_count_explicit").Instance(addrs.IntKey(0)),
instObjState(),
providerAddr,
)
s.SetResourceInstanceCurrent(
resourceAddr("formerly_count_explicit").Instance(addrs.IntKey(1)),
instObjState(),
providerAddr,
)
s.SetResourceInstanceCurrent(
resourceAddr("now_count_explicit").Instance(addrs.NoKey),
instObjState(),
providerAddr,
)
// This "ambiguous" resource is representing a rare but possible
// situation where we end up having a mixture of different index
// types in the state at the same time. The main way to get into
// this state would be to remove "count = 1" and then have the
// provider fail to destroy the zero-key instance even though we
// already created the no-key instance. Users can also get here
// by using "terraform state mv" in weird ways.
s.SetResourceInstanceCurrent(
resourceAddr("ambiguous").Instance(addrs.NoKey),
instObjState(),
providerAddr,
)
s.SetResourceInstanceCurrent(
resourceAddr("ambiguous").Instance(addrs.IntKey(0)),
instObjState(),
providerAddr,
)
})
explicitStmts := FindMoveStatements(rootCfg)
got := ImpliedMoveStatements(rootCfg, prevRunState, explicitStmts)
want := []MoveStatement{
{
From: addrs.ImpliedMoveStatementEndpoint(resourceAddr("formerly_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
To: addrs.ImpliedMoveStatementEndpoint(resourceAddr("formerly_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
Implied: true,
DeclRange: tfdiags.SourceRange{
Filename: "testdata/move-statement-implied/move-statement-implied.tf",
Start: tfdiags.SourcePos{Line: 9, Column: 1, Byte: 232},
End: tfdiags.SourcePos{Line: 9, Column: 32, Byte: 263},
},
},
{
From: addrs.ImpliedMoveStatementEndpoint(resourceAddr("now_count").Instance(addrs.NoKey), tfdiags.SourceRange{}),
To: addrs.ImpliedMoveStatementEndpoint(resourceAddr("now_count").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
Implied: true,
DeclRange: tfdiags.SourceRange{
Filename: "testdata/move-statement-implied/move-statement-implied.tf",
Start: tfdiags.SourcePos{Line: 14, Column: 11, Byte: 334},
End: tfdiags.SourcePos{Line: 14, Column: 12, Byte: 335},
},
},
// We generate foo.ambiguous[0] to foo.ambiguous here, even though
// there's already a foo.ambiguous in the state, because it's the
// responsibility of the later ApplyMoves step to deal with the
// situation where an object wants to move into an address already
// occupied by another object.
{
From: addrs.ImpliedMoveStatementEndpoint(resourceAddr("ambiguous").Instance(addrs.IntKey(0)), tfdiags.SourceRange{}),
To: addrs.ImpliedMoveStatementEndpoint(resourceAddr("ambiguous").Instance(addrs.NoKey), tfdiags.SourceRange{}),
Implied: true,
DeclRange: tfdiags.SourceRange{
Filename: "testdata/move-statement-implied/move-statement-implied.tf",
Start: tfdiags.SourcePos{Line: 42, Column: 1, Byte: 709},
End: tfdiags.SourcePos{Line: 42, Column: 27, Byte: 735},
},
},
}
sort.Slice(got, func(i, j int) bool {
// This is just an arbitrary sort to make the result consistent
// regardless of what order the ImpliedMoveStatements function
// visits the entries in the state/config.
return got[i].DeclRange.Start.Line < got[j].DeclRange.Start.Line
})
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
}