github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/backend/local/backend_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package local 5 6 import ( 7 "errors" 8 "os" 9 "path/filepath" 10 "reflect" 11 "strings" 12 "testing" 13 14 "github.com/terramate-io/tf/backend" 15 "github.com/terramate-io/tf/states/statefile" 16 "github.com/terramate-io/tf/states/statemgr" 17 ) 18 19 func TestLocal_impl(t *testing.T) { 20 var _ backend.Enhanced = New() 21 var _ backend.Local = New() 22 var _ backend.CLI = New() 23 } 24 25 func TestLocal_backend(t *testing.T) { 26 testTmpDir(t) 27 b := New() 28 backend.TestBackendStates(t, b) 29 backend.TestBackendStateLocks(t, b, b) 30 } 31 32 func checkState(t *testing.T, path, expected string) { 33 t.Helper() 34 // Read the state 35 f, err := os.Open(path) 36 if err != nil { 37 t.Fatalf("err: %s", err) 38 } 39 40 state, err := statefile.Read(f) 41 f.Close() 42 if err != nil { 43 t.Fatalf("err: %s", err) 44 } 45 46 actual := state.State.String() 47 expected = strings.TrimSpace(expected) 48 if actual != expected { 49 t.Fatalf("state does not match! actual:\n%s\n\nexpected:\n%s", actual, expected) 50 } 51 } 52 53 func TestLocal_StatePaths(t *testing.T) { 54 b := New() 55 56 // Test the defaults 57 path, out, back := b.StatePaths("") 58 59 if path != DefaultStateFilename { 60 t.Fatalf("expected %q, got %q", DefaultStateFilename, path) 61 } 62 63 if out != DefaultStateFilename { 64 t.Fatalf("expected %q, got %q", DefaultStateFilename, out) 65 } 66 67 dfltBackup := DefaultStateFilename + DefaultBackupExtension 68 if back != dfltBackup { 69 t.Fatalf("expected %q, got %q", dfltBackup, back) 70 } 71 72 // check with env 73 testEnv := "test_env" 74 path, out, back = b.StatePaths(testEnv) 75 76 expectedPath := filepath.Join(DefaultWorkspaceDir, testEnv, DefaultStateFilename) 77 expectedOut := expectedPath 78 expectedBackup := expectedPath + DefaultBackupExtension 79 80 if path != expectedPath { 81 t.Fatalf("expected %q, got %q", expectedPath, path) 82 } 83 84 if out != expectedOut { 85 t.Fatalf("expected %q, got %q", expectedOut, out) 86 } 87 88 if back != expectedBackup { 89 t.Fatalf("expected %q, got %q", expectedBackup, back) 90 } 91 92 } 93 94 func TestLocal_addAndRemoveStates(t *testing.T) { 95 testTmpDir(t) 96 dflt := backend.DefaultStateName 97 expectedStates := []string{dflt} 98 99 b := New() 100 states, err := b.Workspaces() 101 if err != nil { 102 t.Fatal(err) 103 } 104 105 if !reflect.DeepEqual(states, expectedStates) { 106 t.Fatalf("expected []string{%q}, got %q", dflt, states) 107 } 108 109 expectedA := "test_A" 110 if _, err := b.StateMgr(expectedA); err != nil { 111 t.Fatal(err) 112 } 113 114 states, err = b.Workspaces() 115 if err != nil { 116 t.Fatal(err) 117 } 118 119 expectedStates = append(expectedStates, expectedA) 120 if !reflect.DeepEqual(states, expectedStates) { 121 t.Fatalf("expected %q, got %q", expectedStates, states) 122 } 123 124 expectedB := "test_B" 125 if _, err := b.StateMgr(expectedB); err != nil { 126 t.Fatal(err) 127 } 128 129 states, err = b.Workspaces() 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 expectedStates = append(expectedStates, expectedB) 135 if !reflect.DeepEqual(states, expectedStates) { 136 t.Fatalf("expected %q, got %q", expectedStates, states) 137 } 138 139 if err := b.DeleteWorkspace(expectedA, true); err != nil { 140 t.Fatal(err) 141 } 142 143 states, err = b.Workspaces() 144 if err != nil { 145 t.Fatal(err) 146 } 147 148 expectedStates = []string{dflt, expectedB} 149 if !reflect.DeepEqual(states, expectedStates) { 150 t.Fatalf("expected %q, got %q", expectedStates, states) 151 } 152 153 if err := b.DeleteWorkspace(expectedB, true); err != nil { 154 t.Fatal(err) 155 } 156 157 states, err = b.Workspaces() 158 if err != nil { 159 t.Fatal(err) 160 } 161 162 expectedStates = []string{dflt} 163 if !reflect.DeepEqual(states, expectedStates) { 164 t.Fatalf("expected %q, got %q", expectedStates, states) 165 } 166 167 if err := b.DeleteWorkspace(dflt, true); err == nil { 168 t.Fatal("expected error deleting default state") 169 } 170 } 171 172 // a local backend which returns sentinel errors for NamedState methods to 173 // verify it's being called. 174 type testDelegateBackend struct { 175 *Local 176 177 // return a sentinel error on these calls 178 stateErr bool 179 statesErr bool 180 deleteErr bool 181 } 182 183 var errTestDelegateState = errors.New("state called") 184 var errTestDelegateStates = errors.New("states called") 185 var errTestDelegateDeleteState = errors.New("delete called") 186 187 func (b *testDelegateBackend) StateMgr(name string) (statemgr.Full, error) { 188 if b.stateErr { 189 return nil, errTestDelegateState 190 } 191 s := statemgr.NewFilesystem("terraform.tfstate") 192 return s, nil 193 } 194 195 func (b *testDelegateBackend) Workspaces() ([]string, error) { 196 if b.statesErr { 197 return nil, errTestDelegateStates 198 } 199 return []string{"default"}, nil 200 } 201 202 func (b *testDelegateBackend) DeleteWorkspace(name string, force bool) error { 203 if b.deleteErr { 204 return errTestDelegateDeleteState 205 } 206 return nil 207 } 208 209 // verify that the MultiState methods are dispatched to the correct Backend. 210 func TestLocal_multiStateBackend(t *testing.T) { 211 // assign a separate backend where we can read the state 212 b := NewWithBackend(&testDelegateBackend{ 213 stateErr: true, 214 statesErr: true, 215 deleteErr: true, 216 }) 217 218 if _, err := b.StateMgr("test"); err != errTestDelegateState { 219 t.Fatal("expected errTestDelegateState, got:", err) 220 } 221 222 if _, err := b.Workspaces(); err != errTestDelegateStates { 223 t.Fatal("expected errTestDelegateStates, got:", err) 224 } 225 226 if err := b.DeleteWorkspace("test", true); err != errTestDelegateDeleteState { 227 t.Fatal("expected errTestDelegateDeleteState, got:", err) 228 } 229 } 230 231 // testTmpDir changes into a tmp dir and change back automatically when the test 232 // and all its subtests complete. 233 func testTmpDir(t *testing.T) { 234 tmp := t.TempDir() 235 236 old, err := os.Getwd() 237 if err != nil { 238 t.Fatal(err) 239 } 240 241 if err := os.Chdir(tmp); err != nil { 242 t.Fatal(err) 243 } 244 245 t.Cleanup(func() { 246 // ignore errors and try to clean up 247 os.Chdir(old) 248 }) 249 }