github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/orchestrator/reactor_state_tester.go (about)

     1  // Copyright 2021 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 orchestrator
    15  
    16  import (
    17  	"testing"
    18  
    19  	"github.com/pingcap/errors"
    20  	cerrors "github.com/pingcap/tiflow/pkg/errors"
    21  	"github.com/pingcap/tiflow/pkg/orchestrator/util"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  // ReactorStateTester is a helper struct for unit-testing an implementer of ReactorState
    26  type ReactorStateTester struct {
    27  	t         *testing.T
    28  	state     ReactorState
    29  	kvEntries map[string]string
    30  }
    31  
    32  // NewReactorStateTester creates a new ReactorStateTester
    33  func NewReactorStateTester(t *testing.T, state ReactorState, initKVEntries map[string]string) *ReactorStateTester {
    34  	if initKVEntries == nil {
    35  		initKVEntries = make(map[string]string)
    36  	}
    37  	for k, v := range initKVEntries {
    38  		err := state.Update(util.NewEtcdKey(k), []byte(v), true)
    39  		require.NoError(t, err)
    40  	}
    41  	return &ReactorStateTester{
    42  		t:         t,
    43  		state:     state,
    44  		kvEntries: initKVEntries,
    45  	}
    46  }
    47  
    48  // Update is used to update keys in the mocked kv-store.
    49  func (r *ReactorStateTester) Update(key string, value []byte) error {
    50  	k := util.NewEtcdKey(key)
    51  	err := r.state.Update(k, value, false)
    52  	if err != nil {
    53  		return errors.Trace(err)
    54  	}
    55  	if value != nil {
    56  		r.kvEntries[key] = string(value)
    57  	} else {
    58  		delete(r.kvEntries, key)
    59  	}
    60  	return nil
    61  }
    62  
    63  // ApplyPatches calls the GetPatches method on the ReactorState and apply the changes to the mocked kv-store.
    64  func (r *ReactorStateTester) ApplyPatches() error {
    65  	patchGroups := r.state.GetPatches()
    66  	for _, patches := range patchGroups {
    67  		err := r.applyPatches(patches)
    68  		if err != nil {
    69  			return err
    70  		}
    71  	}
    72  	return nil
    73  }
    74  
    75  func (r *ReactorStateTester) applyPatches(patches []DataPatch) error {
    76  RetryLoop:
    77  	for {
    78  		tmpKVEntries := make(map[util.EtcdKey][]byte)
    79  		for k, v := range r.kvEntries {
    80  			tmpKVEntries[util.NewEtcdKey(k)] = []byte(v)
    81  		}
    82  		changedSet := make(map[util.EtcdKey]struct{})
    83  		for _, patch := range patches {
    84  			err := patch.Patch(tmpKVEntries, changedSet)
    85  			if cerrors.ErrEtcdIgnore.Equal(errors.Cause(err)) {
    86  				continue
    87  			} else if cerrors.ErrEtcdTryAgain.Equal(errors.Cause(err)) {
    88  				continue RetryLoop
    89  			} else if err != nil {
    90  				return errors.Trace(err)
    91  			}
    92  		}
    93  		for k := range changedSet {
    94  			err := r.state.Update(k, tmpKVEntries[k], false)
    95  			if err != nil {
    96  				return err
    97  			}
    98  			if value := tmpKVEntries[k]; value != nil {
    99  				r.kvEntries[k.String()] = string(value)
   100  			} else {
   101  				delete(r.kvEntries, k.String())
   102  			}
   103  		}
   104  		return nil
   105  	}
   106  }
   107  
   108  // MustApplyPatches calls ApplyPatches and must successfully
   109  func (r *ReactorStateTester) MustApplyPatches() {
   110  	require.Nil(r.t, r.ApplyPatches())
   111  }
   112  
   113  // MustUpdate calls Update and must successfully
   114  func (r *ReactorStateTester) MustUpdate(key string, value []byte) {
   115  	require.Nil(r.t, r.Update(key, value))
   116  }
   117  
   118  // KVEntries returns the contents of the mocked KV store.
   119  func (r *ReactorStateTester) KVEntries() map[string]string {
   120  	return r.kvEntries
   121  }