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  }