github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/states/remote/testing.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package remote
     5  
     6  import (
     7  	"bytes"
     8  	"testing"
     9  
    10  	"github.com/terramate-io/tf/states/statefile"
    11  	"github.com/terramate-io/tf/states/statemgr"
    12  )
    13  
    14  // TestClient is a generic function to test any client.
    15  func TestClient(t *testing.T, c Client) {
    16  	var buf bytes.Buffer
    17  	s := statemgr.TestFullInitialState()
    18  	sf := statefile.New(s, "stub-lineage", 2)
    19  	err := statefile.Write(sf, &buf)
    20  	if err != nil {
    21  		t.Fatalf("err: %s", err)
    22  	}
    23  	data := buf.Bytes()
    24  
    25  	if err := c.Put(data); err != nil {
    26  		t.Fatalf("put: %s", err)
    27  	}
    28  
    29  	p, err := c.Get()
    30  	if err != nil {
    31  		t.Fatalf("get: %s", err)
    32  	}
    33  	if !bytes.Equal(p.Data, data) {
    34  		t.Fatalf("expected full state %q\n\ngot: %q", string(p.Data), string(data))
    35  	}
    36  
    37  	if err := c.Delete(); err != nil {
    38  		t.Fatalf("delete: %s", err)
    39  	}
    40  
    41  	p, err = c.Get()
    42  	if err != nil {
    43  		t.Fatalf("get: %s", err)
    44  	}
    45  	if p != nil {
    46  		t.Fatalf("expected empty state, got: %q", string(p.Data))
    47  	}
    48  }
    49  
    50  // Test the lock implementation for a remote.Client.
    51  // This test requires 2 client instances, in oder to have multiple remote
    52  // clients since some implementations may tie the client to the lock, or may
    53  // have reentrant locks.
    54  func TestRemoteLocks(t *testing.T, a, b Client) {
    55  	lockerA, ok := a.(statemgr.Locker)
    56  	if !ok {
    57  		t.Fatal("client A not a statemgr.Locker")
    58  	}
    59  
    60  	lockerB, ok := b.(statemgr.Locker)
    61  	if !ok {
    62  		t.Fatal("client B not a statemgr.Locker")
    63  	}
    64  
    65  	infoA := statemgr.NewLockInfo()
    66  	infoA.Operation = "test"
    67  	infoA.Who = "clientA"
    68  
    69  	infoB := statemgr.NewLockInfo()
    70  	infoB.Operation = "test"
    71  	infoB.Who = "clientB"
    72  
    73  	lockIDA, err := lockerA.Lock(infoA)
    74  	if err != nil {
    75  		t.Fatal("unable to get initial lock:", err)
    76  	}
    77  
    78  	_, err = lockerB.Lock(infoB)
    79  	if err == nil {
    80  		lockerA.Unlock(lockIDA)
    81  		t.Fatal("client B obtained lock while held by client A")
    82  	}
    83  	if _, ok := err.(*statemgr.LockError); !ok {
    84  		t.Errorf("expected a LockError, but was %t: %s", err, err)
    85  	}
    86  
    87  	if err := lockerA.Unlock(lockIDA); err != nil {
    88  		t.Fatal("error unlocking client A", err)
    89  	}
    90  
    91  	lockIDB, err := lockerB.Lock(infoB)
    92  	if err != nil {
    93  		t.Fatal("unable to obtain lock from client B")
    94  	}
    95  
    96  	if lockIDB == lockIDA {
    97  		t.Fatalf("duplicate lock IDs: %q", lockIDB)
    98  	}
    99  
   100  	if err = lockerB.Unlock(lockIDB); err != nil {
   101  		t.Fatal("error unlocking client B:", err)
   102  	}
   103  
   104  	// TODO: Should we enforce that Unlock requires the correct ID?
   105  }