opentofu/states/statemgr/migrate_test.go
Martin Atkins 94510bc1b9 states/statemgr: Migrate, Import, and Export functions
In our recent refactoring of the state manager interfaces we made serial
and lineage management the responsibility of the state managers
themselves, not exposing them at all to most callers, and allowing for
simple state managers that don't implement them at all.

However, we do have some specific cases where we need to preserve these
properly when available, such as migration between backends, and the
"terraform state push" and "terraform state pull" commands.

These new functions and their associated optional interface allow the
logic here to be captured in one place and access via some simple
calls. Separating this from the main interface leaves things simple for
the normal uses of state managers.

Since these functions are mostly just thin wrappers around other
functionality, they are not yet well-tested directly, but will be
indirectly tested through the tests of their callers. A subsequent commit
will add more unit tests here.
2018-11-19 09:02:35 -08:00

103 lines
3.1 KiB
Go

package statemgr
import (
"testing"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/states/statefile"
)
func TestCheckValidImport(t *testing.T) {
barState := states.BuildState(func(s *states.SyncState) {
s.SetOutputValue(
addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
cty.StringVal("bar"), false,
)
})
notBarState := states.BuildState(func(s *states.SyncState) {
s.SetOutputValue(
addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
cty.StringVal("not bar"), false,
)
})
emptyState := states.NewState()
tests := map[string]struct {
New *statefile.File
Existing *statefile.File
WantErr string
}{
"exact match": {
New: statefile.New(barState, "lineage", 1),
Existing: statefile.New(barState, "lineage", 1),
WantErr: ``,
},
"overwrite unrelated empty state": {
New: statefile.New(barState, "lineage1", 1),
Existing: statefile.New(emptyState, "lineage2", 1),
WantErr: ``,
},
"different state with same serial": {
New: statefile.New(barState, "lineage", 1),
Existing: statefile.New(notBarState, "lineage", 1),
WantErr: `cannot overwrite existing state with serial 1 with a different state that has the same serial`,
},
"different state with newer serial": {
New: statefile.New(barState, "lineage", 2),
Existing: statefile.New(notBarState, "lineage", 1),
WantErr: ``,
},
"different state with older serial": {
New: statefile.New(barState, "lineage", 1),
Existing: statefile.New(notBarState, "lineage", 2),
WantErr: `cannot import state with serial 1 over newer state with serial 2`,
},
"different lineage with same serial": {
New: statefile.New(barState, "lineage1", 2),
Existing: statefile.New(notBarState, "lineage2", 2),
WantErr: `cannot import state with lineage "lineage1" over unrelated state with lineage "lineage2"`,
},
"different lineage with different serial": {
New: statefile.New(barState, "lineage1", 3),
Existing: statefile.New(notBarState, "lineage2", 2),
WantErr: `cannot import state with lineage "lineage1" over unrelated state with lineage "lineage2"`,
},
"new state is legacy": {
New: statefile.New(barState, "", 2),
Existing: statefile.New(notBarState, "lineage", 2),
WantErr: ``,
},
"old state is legacy": {
New: statefile.New(barState, "lineage", 2),
Existing: statefile.New(notBarState, "", 2),
WantErr: ``,
},
"both states are legacy": {
New: statefile.New(barState, "", 2),
Existing: statefile.New(notBarState, "", 2),
WantErr: ``,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
gotErr := CheckValidImport(test.New, test.Existing)
if test.WantErr == "" {
if gotErr != nil {
t.Errorf("unexpected error: %s", gotErr)
}
} else {
if gotErr == nil {
t.Errorf("succeeded, but want error: %s", test.WantErr)
} else if got, want := gotErr.Error(), test.WantErr; got != want {
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
}
}
})
}
}