mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
94510bc1b9
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.
103 lines
3.1 KiB
Go
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)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|