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  }