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 }