github.com/kevinklinger/open_terraform@v1.3.6/noninternal/cloud/state_test.go (about)

     1  package cloud
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"testing"
     7  
     8  	tfe "github.com/hashicorp/go-tfe"
     9  	"github.com/kevinklinger/open_terraform/noninternal/states/statefile"
    10  	"github.com/kevinklinger/open_terraform/noninternal/states/statemgr"
    11  )
    12  
    13  func TestState_impl(t *testing.T) {
    14  	var _ statemgr.Reader = new(State)
    15  	var _ statemgr.Writer = new(State)
    16  	var _ statemgr.Persister = new(State)
    17  	var _ statemgr.Refresher = new(State)
    18  	var _ statemgr.OutputReader = new(State)
    19  	var _ statemgr.Locker = new(State)
    20  }
    21  
    22  type ExpectedOutput struct {
    23  	Name      string
    24  	Sensitive bool
    25  	IsNull    bool
    26  }
    27  
    28  func TestState_GetRootOutputValues(t *testing.T) {
    29  	b, bCleanup := testBackendWithOutputs(t)
    30  	defer bCleanup()
    31  
    32  	state := &State{tfeClient: b.client, organization: b.organization, workspace: &tfe.Workspace{
    33  		ID: "ws-abcd",
    34  	}}
    35  	outputs, err := state.GetRootOutputValues()
    36  
    37  	if err != nil {
    38  		t.Fatalf("error returned from GetRootOutputValues: %s", err)
    39  	}
    40  
    41  	cases := []ExpectedOutput{
    42  		{
    43  			Name:      "sensitive_output",
    44  			Sensitive: true,
    45  			IsNull:    false,
    46  		},
    47  		{
    48  			Name:      "nonsensitive_output",
    49  			Sensitive: false,
    50  			IsNull:    false,
    51  		},
    52  		{
    53  			Name:      "object_output",
    54  			Sensitive: false,
    55  			IsNull:    false,
    56  		},
    57  		{
    58  			Name:      "list_output",
    59  			Sensitive: false,
    60  			IsNull:    false,
    61  		},
    62  	}
    63  
    64  	if len(outputs) != len(cases) {
    65  		t.Errorf("Expected %d item but %d were returned", len(cases), len(outputs))
    66  	}
    67  
    68  	for _, testCase := range cases {
    69  		so, ok := outputs[testCase.Name]
    70  		if !ok {
    71  			t.Fatalf("Expected key %s but it was not found", testCase.Name)
    72  		}
    73  		if so.Value.IsNull() != testCase.IsNull {
    74  			t.Errorf("Key %s does not match null expectation %v", testCase.Name, testCase.IsNull)
    75  		}
    76  		if so.Sensitive != testCase.Sensitive {
    77  			t.Errorf("Key %s does not match sensitive expectation %v", testCase.Name, testCase.Sensitive)
    78  		}
    79  	}
    80  }
    81  
    82  func TestState(t *testing.T) {
    83  	var buf bytes.Buffer
    84  	s := statemgr.TestFullInitialState()
    85  	sf := statefile.New(s, "stub-lineage", 2)
    86  	err := statefile.Write(sf, &buf)
    87  	if err != nil {
    88  		t.Fatalf("err: %s", err)
    89  	}
    90  	data := buf.Bytes()
    91  
    92  	state := testCloudState(t)
    93  
    94  	jsonState, err := ioutil.ReadFile("../command/testdata/show-json-state/sensitive-variables/output.json")
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	jsonStateOutputs := []byte(`
   100  {
   101  	"outputs": {
   102  			"foo": {
   103  					"type": "string",
   104  					"value": "bar"
   105  			}
   106  	}
   107  }`)
   108  
   109  	if err := state.uploadState(state.lineage, state.serial, state.forcePush, data, jsonState, jsonStateOutputs); err != nil {
   110  		t.Fatalf("put: %s", err)
   111  	}
   112  
   113  	payload, err := state.getStatePayload()
   114  	if err != nil {
   115  		t.Fatalf("get: %s", err)
   116  	}
   117  	if !bytes.Equal(payload.Data, data) {
   118  		t.Fatalf("expected full state %q\n\ngot: %q", string(payload.Data), string(data))
   119  	}
   120  
   121  	if err := state.Delete(); err != nil {
   122  		t.Fatalf("delete: %s", err)
   123  	}
   124  
   125  	p, err := state.getStatePayload()
   126  	if err != nil {
   127  		t.Fatalf("get: %s", err)
   128  	}
   129  	if p != nil {
   130  		t.Fatalf("expected empty state, got: %q", string(p.Data))
   131  	}
   132  }
   133  
   134  func TestCloudLocks(t *testing.T) {
   135  	back, bCleanup := testBackendWithName(t)
   136  	defer bCleanup()
   137  
   138  	a, err := back.StateMgr(testBackendSingleWorkspaceName)
   139  	if err != nil {
   140  		t.Fatalf("expected no error, got %v", err)
   141  	}
   142  	b, err := back.StateMgr(testBackendSingleWorkspaceName)
   143  	if err != nil {
   144  		t.Fatalf("expected no error, got %v", err)
   145  	}
   146  
   147  	lockerA, ok := a.(statemgr.Locker)
   148  	if !ok {
   149  		t.Fatal("client A not a statemgr.Locker")
   150  	}
   151  
   152  	lockerB, ok := b.(statemgr.Locker)
   153  	if !ok {
   154  		t.Fatal("client B not a statemgr.Locker")
   155  	}
   156  
   157  	infoA := statemgr.NewLockInfo()
   158  	infoA.Operation = "test"
   159  	infoA.Who = "clientA"
   160  
   161  	infoB := statemgr.NewLockInfo()
   162  	infoB.Operation = "test"
   163  	infoB.Who = "clientB"
   164  
   165  	lockIDA, err := lockerA.Lock(infoA)
   166  	if err != nil {
   167  		t.Fatal("unable to get initial lock:", err)
   168  	}
   169  
   170  	_, err = lockerB.Lock(infoB)
   171  	if err == nil {
   172  		lockerA.Unlock(lockIDA)
   173  		t.Fatal("client B obtained lock while held by client A")
   174  	}
   175  	if _, ok := err.(*statemgr.LockError); !ok {
   176  		t.Errorf("expected a LockError, but was %t: %s", err, err)
   177  	}
   178  
   179  	if err := lockerA.Unlock(lockIDA); err != nil {
   180  		t.Fatal("error unlocking client A", err)
   181  	}
   182  
   183  	lockIDB, err := lockerB.Lock(infoB)
   184  	if err != nil {
   185  		t.Fatal("unable to obtain lock from client B")
   186  	}
   187  
   188  	if lockIDB == lockIDA {
   189  		t.Fatalf("duplicate lock IDs: %q", lockIDB)
   190  	}
   191  
   192  	if err = lockerB.Unlock(lockIDB); err != nil {
   193  		t.Fatal("error unlocking client B:", err)
   194  	}
   195  }