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