github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/integration/clientv3/txn_test.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package clientv3test
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/lfch/etcd-io/api/v3/v3rpc/rpctypes"
    24  	"github.com/lfch/etcd-io/client/v3"
    25  	"github.com/lfch/etcd-io/server/v3/embed"
    26  	integration2 "github.com/lfch/etcd-io/tests/v3/framework/integration"
    27  )
    28  
    29  func TestTxnError(t *testing.T) {
    30  	integration2.BeforeTest(t)
    31  
    32  	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
    33  	defer clus.Terminate(t)
    34  
    35  	kv := clus.RandClient()
    36  	ctx := context.TODO()
    37  
    38  	_, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar1"), clientv3.OpPut("foo", "bar2")).Commit()
    39  	if err != rpctypes.ErrDuplicateKey {
    40  		t.Fatalf("expected %v, got %v", rpctypes.ErrDuplicateKey, err)
    41  	}
    42  
    43  	ops := make([]clientv3.Op, int(embed.DefaultMaxTxnOps+10))
    44  	for i := range ops {
    45  		ops[i] = clientv3.OpPut(fmt.Sprintf("foo%d", i), "")
    46  	}
    47  	_, err = kv.Txn(ctx).Then(ops...).Commit()
    48  	if err != rpctypes.ErrTooManyOps {
    49  		t.Fatalf("expected %v, got %v", rpctypes.ErrTooManyOps, err)
    50  	}
    51  }
    52  
    53  func TestTxnWriteFail(t *testing.T) {
    54  	integration2.BeforeTest(t)
    55  
    56  	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
    57  	defer clus.Terminate(t)
    58  
    59  	kv := clus.Client(0)
    60  
    61  	clus.Members[0].Stop(t)
    62  
    63  	txnc, getc := make(chan struct{}), make(chan struct{})
    64  	go func() {
    65  		ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
    66  		defer cancel()
    67  		resp, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar")).Commit()
    68  		if err == nil {
    69  			t.Errorf("expected error, got response %v", resp)
    70  		}
    71  		close(txnc)
    72  	}()
    73  
    74  	go func() {
    75  		defer close(getc)
    76  		select {
    77  		case <-time.After(5 * time.Second):
    78  			t.Errorf("timed out waiting for txn fail")
    79  		case <-txnc:
    80  		}
    81  		// and ensure the put didn't take
    82  		gresp, gerr := clus.Client(1).Get(context.TODO(), "foo")
    83  		if gerr != nil {
    84  			t.Error(gerr)
    85  		}
    86  		if len(gresp.Kvs) != 0 {
    87  			t.Errorf("expected no keys, got %v", gresp.Kvs)
    88  		}
    89  	}()
    90  
    91  	select {
    92  	case <-time.After(5 * clus.Members[1].ServerConfig.ReqTimeout()):
    93  		t.Fatalf("timed out waiting for get")
    94  	case <-getc:
    95  	}
    96  
    97  	// reconnect so terminate doesn't complain about double-close
    98  	clus.Members[0].Restart(t)
    99  }
   100  
   101  func TestTxnReadRetry(t *testing.T) {
   102  	t.Skipf("skipping txn read retry test: re-enable after we do retry on txn read request")
   103  
   104  	integration2.BeforeTest(t)
   105  
   106  	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
   107  	defer clus.Terminate(t)
   108  
   109  	kv := clus.Client(0)
   110  
   111  	thenOps := [][]clientv3.Op{
   112  		{clientv3.OpGet("foo")},
   113  		{clientv3.OpTxn(nil, []clientv3.Op{clientv3.OpGet("foo")}, nil)},
   114  		{clientv3.OpTxn(nil, nil, nil)},
   115  		{},
   116  	}
   117  	for i := range thenOps {
   118  		clus.Members[0].Stop(t)
   119  		<-clus.Members[0].StopNotify()
   120  
   121  		donec := make(chan struct{}, 1)
   122  		go func() {
   123  			_, err := kv.Txn(context.TODO()).Then(thenOps[i]...).Commit()
   124  			if err != nil {
   125  				t.Errorf("expected response, got error %v", err)
   126  			}
   127  			donec <- struct{}{}
   128  		}()
   129  		// wait for txn to fail on disconnect
   130  		time.Sleep(100 * time.Millisecond)
   131  
   132  		// restart node; client should resume
   133  		clus.Members[0].Restart(t)
   134  		select {
   135  		case <-donec:
   136  		case <-time.After(2 * clus.Members[1].ServerConfig.ReqTimeout()):
   137  			t.Fatalf("waited too long")
   138  		}
   139  	}
   140  }
   141  
   142  func TestTxnSuccess(t *testing.T) {
   143  	integration2.BeforeTest(t)
   144  
   145  	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
   146  	defer clus.Terminate(t)
   147  
   148  	kv := clus.Client(0)
   149  	ctx := context.TODO()
   150  
   151  	_, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar")).Commit()
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	resp, err := kv.Get(ctx, "foo")
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  	if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "foo" {
   161  		t.Fatalf("unexpected Get response %v", resp)
   162  	}
   163  }
   164  
   165  func TestTxnCompareRange(t *testing.T) {
   166  	integration2.BeforeTest(t)
   167  
   168  	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
   169  	defer clus.Terminate(t)
   170  
   171  	kv := clus.Client(0)
   172  	fooResp, err := kv.Put(context.TODO(), "foo/", "bar")
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  	if _, err = kv.Put(context.TODO(), "foo/a", "baz"); err != nil {
   177  		t.Fatal(err)
   178  	}
   179  	tresp, terr := kv.Txn(context.TODO()).If(
   180  		clientv3.Compare(
   181  			clientv3.CreateRevision("foo/"), "=", fooResp.Header.Revision).
   182  			WithPrefix(),
   183  	).Commit()
   184  	if terr != nil {
   185  		t.Fatal(terr)
   186  	}
   187  	if tresp.Succeeded {
   188  		t.Fatal("expected prefix compare to false, got compares as true")
   189  	}
   190  }
   191  
   192  func TestTxnNested(t *testing.T) {
   193  	integration2.BeforeTest(t)
   194  
   195  	clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
   196  	defer clus.Terminate(t)
   197  
   198  	kv := clus.Client(0)
   199  
   200  	tresp, err := kv.Txn(context.TODO()).
   201  		If(clientv3.Compare(clientv3.Version("foo"), "=", 0)).
   202  		Then(
   203  			clientv3.OpPut("foo", "bar"),
   204  			clientv3.OpTxn(nil, []clientv3.Op{clientv3.OpPut("abc", "123")}, nil)).
   205  		Else(clientv3.OpPut("foo", "baz")).Commit()
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	if len(tresp.Responses) != 2 {
   210  		t.Errorf("expected 2 top-level txn responses, got %+v", tresp.Responses)
   211  	}
   212  
   213  	// check txn writes were applied
   214  	resp, err := kv.Get(context.TODO(), "foo")
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "bar" {
   219  		t.Errorf("unexpected Get response %+v", resp)
   220  	}
   221  	resp, err = kv.Get(context.TODO(), "abc")
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "123" {
   226  		t.Errorf("unexpected Get response %+v", resp)
   227  	}
   228  }