github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/meta/internal/etcdkv/namespace/kv_test.go (about) 1 // Copyright 2022 PingCAP, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package namespace 15 16 import ( 17 "context" 18 "testing" 19 "time" 20 21 "github.com/pingcap/tiflow/engine/pkg/meta/mock" 22 metaModel "github.com/pingcap/tiflow/engine/pkg/meta/model" 23 "github.com/stretchr/testify/require" 24 ) 25 26 type kv struct { 27 key string 28 value string 29 } 30 31 type prepare struct { 32 kvs []kv 33 } 34 35 type optype int 36 37 const ( 38 tNone optype = iota 39 tGet 40 tPut 41 tDel 42 tTxn 43 ) 44 45 type query struct { 46 key string 47 opts []metaModel.OpOption 48 err error 49 // for txn: we only use expected 50 expected []kv 51 } 52 53 type action struct { 54 t optype 55 // do action 56 do kv 57 opts []metaModel.OpOption 58 // query action 59 q query 60 } 61 62 type txnAction struct { 63 acts []action 64 // return error 65 err error 66 } 67 68 func prepareData(ctx context.Context, t *testing.T, cli metaModel.KV, p prepare) { 69 if p.kvs != nil { 70 for _, kv := range p.kvs { 71 prsp, perr := cli.Put(ctx, kv.key, kv.value) 72 require.Nil(t, perr) 73 require.NotNil(t, prsp) 74 } 75 } 76 } 77 78 func testAction(ctx context.Context, t *testing.T, cli metaModel.KV, acts []action) { 79 for _, act := range acts { 80 switch act.t { 81 case tGet: 82 rsp, err := cli.Get(ctx, act.do.key, act.opts...) 83 require.Nil(t, err) 84 require.NotNil(t, rsp) 85 case tPut: 86 rsp, err := cli.Put(ctx, act.do.key, act.do.value) 87 require.Nil(t, err) 88 require.NotNil(t, rsp) 89 case tDel: 90 rsp, err := cli.Delete(ctx, act.do.key, act.opts...) 91 require.Nil(t, err) 92 require.NotNil(t, rsp) 93 case tTxn: 94 require.FailNow(t, "unexpected txn action") 95 case tNone: 96 // do nothing 97 default: 98 require.FailNow(t, "unexpected action type") 99 } 100 101 rsp, err := cli.Get(ctx, act.q.key, act.q.opts...) 102 if act.q.err != nil { 103 require.Error(t, err) 104 continue 105 } 106 require.Nil(t, err) 107 require.NotNil(t, rsp) 108 expected := act.q.expected 109 require.Len(t, rsp.Kvs, len(expected)) 110 for i, kv := range rsp.Kvs { 111 require.Equal(t, string(kv.Key), expected[i].key) 112 require.Equal(t, string(kv.Value), expected[i].value) 113 } 114 } 115 } 116 117 func testTxnAction(ctx context.Context, t *testing.T, cli metaModel.KV, txns []txnAction) { 118 for _, txn := range txns { 119 ops := make([]metaModel.Op, 0, len(txn.acts)) 120 for _, act := range txn.acts { 121 switch act.t { 122 case tGet: 123 ops = append(ops, metaModel.OpGet(act.do.key, act.opts...)) 124 case tPut: 125 ops = append(ops, metaModel.OpPut(act.do.key, act.do.value)) 126 case tDel: 127 ops = append(ops, metaModel.OpDelete(act.do.key, act.opts...)) 128 default: 129 require.FailNow(t, "unexpected action type") 130 } 131 } 132 tx := cli.Txn(ctx) 133 tx.Do(ops...) 134 rsp, err := tx.Commit() 135 // test txn rsp 136 if txn.err != nil { 137 require.Error(t, err) 138 continue 139 } 140 require.Nil(t, err) 141 require.Len(t, rsp.Responses, len(txn.acts)) 142 for i, r := range rsp.Responses { 143 act := txn.acts[i] 144 switch act.t { 145 case tGet: 146 rr := r.GetResponseGet() 147 require.NotNil(t, rr) 148 expected := act.q.expected 149 require.Len(t, rr.Kvs, len(expected)) 150 for i, kv := range rr.Kvs { 151 require.Equal(t, string(kv.Key), expected[i].key) 152 require.Equal(t, string(kv.Value), expected[i].value) 153 } 154 case tPut: 155 rr := r.GetResponsePut() 156 require.NotNil(t, rr) 157 case tDel: 158 rr := r.GetResponseDelete() 159 require.NotNil(t, rr) 160 default: 161 require.FailNow(t, "unexpected action type") 162 } 163 } 164 } 165 } 166 167 func TestPrefixBasicKV(t *testing.T) { 168 t.Parallel() 169 170 mock := mock.NewMetaMock() 171 defer mock.Close() 172 cli := NewPrefixKV(mock, "test") 173 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 174 defer cancel() 175 176 input := prepare{ 177 kvs: []kv{}, 178 } 179 actions := []action{ 180 { 181 t: tNone, 182 q: query{ 183 key: "hello", 184 opts: []metaModel.OpOption{}, 185 expected: []kv{}, 186 }, 187 }, 188 { 189 t: tPut, 190 do: kv{"hello", "world"}, 191 opts: []metaModel.OpOption{}, 192 q: query{ 193 key: "hello", 194 opts: []metaModel.OpOption{}, 195 expected: []kv{ 196 {"hello", "world"}, 197 }, 198 }, 199 }, 200 { 201 t: tDel, 202 do: kv{"hello", ""}, 203 opts: []metaModel.OpOption{}, 204 q: query{ 205 key: "hello", 206 opts: []metaModel.OpOption{}, 207 expected: []kv{}, 208 }, 209 }, 210 { 211 t: tPut, 212 do: kv{"hello", "new world"}, 213 opts: []metaModel.OpOption{}, 214 q: query{ 215 key: "hello", 216 opts: []metaModel.OpOption{}, 217 expected: []kv{ 218 {"hello", "new world"}, 219 }, 220 }, 221 }, 222 } 223 224 // prepare data and test query 225 prepareData(ctx, t, cli, input) 226 testAction(ctx, t, cli, actions) 227 } 228 229 func TestTxn(t *testing.T) { 230 t.Parallel() 231 232 mock := mock.NewMetaMock() 233 defer mock.Close() 234 cli := NewPrefixKV(mock, "test") 235 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 236 defer cancel() 237 238 input := prepare{ 239 kvs: []kv{ 240 {"hello1", "world1"}, 241 {"hello2", "world2"}, 242 {"interesting", "world"}, 243 {"dataflow", "engine"}, 244 {"TiDB", "component"}, 245 }, 246 } 247 txns := []txnAction{ 248 { 249 acts: []action{ 250 { 251 t: tGet, 252 do: kv{"hello1", ""}, 253 opts: []metaModel.OpOption{}, 254 q: query{ 255 expected: []kv{ 256 {"hello1", "world1"}, 257 }, 258 }, 259 }, 260 { 261 t: tPut, 262 do: kv{"hello3", "world3"}, 263 opts: []metaModel.OpOption{}, 264 q: query{ 265 expected: []kv{}, 266 }, 267 }, 268 { 269 t: tPut, 270 do: kv{"dataflow2", "engine2"}, 271 opts: []metaModel.OpOption{}, 272 q: query{ 273 expected: []kv{}, 274 }, 275 }, 276 { 277 t: tDel, 278 do: kv{"dataflow3", ""}, 279 opts: []metaModel.OpOption{}, 280 q: query{ 281 expected: []kv{}, 282 }, 283 }, 284 }, 285 }, 286 } 287 288 prepareData(ctx, t, cli, input) 289 testTxnAction(ctx, t, cli, txns) 290 }