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 }