go.temporal.io/server@v1.23.0/common/persistence/client/fault_injection_test.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package client_test 26 27 import ( 28 "context" 29 "errors" 30 "testing" 31 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 35 "go.temporal.io/server/common/config" 36 "go.temporal.io/server/common/persistence" 37 "go.temporal.io/server/common/persistence/client" 38 ) 39 40 type ( 41 testDataStoreFactory struct { 42 client.DataStoreFactory 43 err error 44 enqueueRequests int 45 readRequests int 46 createRequests int 47 rangeDeleteRequests int 48 } 49 testQueueV2 struct { 50 enqueueRequests *int 51 readRequests *int 52 createRequests *int 53 rangeDeleteRequests *int 54 listQueuesRequests *int 55 } 56 ) 57 58 func (t *testQueueV2) EnqueueMessage( 59 context.Context, 60 *persistence.InternalEnqueueMessageRequest, 61 ) (*persistence.InternalEnqueueMessageResponse, error) { 62 *t.enqueueRequests++ 63 return nil, nil 64 } 65 66 func (t *testQueueV2) ReadMessages( 67 context.Context, 68 *persistence.InternalReadMessagesRequest, 69 ) (*persistence.InternalReadMessagesResponse, error) { 70 *t.readRequests++ 71 return nil, nil 72 } 73 74 func (t *testQueueV2) CreateQueue( 75 context.Context, 76 *persistence.InternalCreateQueueRequest, 77 ) (*persistence.InternalCreateQueueResponse, error) { 78 *t.createRequests++ 79 return nil, nil 80 } 81 82 func (t *testQueueV2) RangeDeleteMessages( 83 context.Context, 84 *persistence.InternalRangeDeleteMessagesRequest, 85 ) (*persistence.InternalRangeDeleteMessagesResponse, error) { 86 *t.rangeDeleteRequests++ 87 return nil, nil 88 } 89 90 func (t *testQueueV2) ListQueues( 91 context.Context, 92 *persistence.InternalListQueuesRequest, 93 ) (*persistence.InternalListQueuesResponse, error) { 94 *t.listQueuesRequests++ 95 return nil, nil 96 } 97 98 func (t *testDataStoreFactory) NewQueueV2() (persistence.QueueV2, error) { 99 if t.err != nil { 100 return nil, t.err 101 } 102 return &testQueueV2{ 103 enqueueRequests: &t.enqueueRequests, 104 readRequests: &t.readRequests, 105 createRequests: &t.createRequests, 106 rangeDeleteRequests: &t.rangeDeleteRequests, 107 }, nil 108 } 109 110 func TestFaultInjectionDataStoreFactory_NewQueueV2_CreateErr(t *testing.T) { 111 // Tests that we propagate errors from the base data store factory when creating a QueueV2 112 113 t.Parallel() 114 115 errCreate := errors.New("error creating QueueV2") 116 dataStoreFactory := &testDataStoreFactory{ 117 err: errCreate, 118 } 119 factory := client.NewFaultInjectionDatastoreFactory(&config.FaultInjection{ 120 Rate: 1.0, 121 Targets: config.FaultInjectionTargets{}, 122 }, dataStoreFactory) 123 124 _, err := factory.NewQueueV2() 125 assert.ErrorIs(t, err, errCreate) 126 } 127 128 func TestFaultInjectionDataStoreFactory_NewQueueV2_FlatErrorRate(t *testing.T) { 129 // Tests fault injection for QueueV2 with a flat error rate across all methods. 130 131 t.Parallel() 132 133 for _, tc := range []struct { 134 name string 135 errorRate float64 136 expectErr bool 137 }{ 138 { 139 name: "No errors", 140 errorRate: 0.0, 141 expectErr: false, 142 }, 143 { 144 name: "All errors", 145 errorRate: 1.0, 146 expectErr: true, 147 }, 148 } { 149 tc := tc 150 t.Run(tc.name, func(t *testing.T) { 151 t.Parallel() 152 faultInjectionConfig := &config.FaultInjection{ 153 Rate: tc.errorRate, 154 Targets: config.FaultInjectionTargets{}, 155 } 156 expectErr := tc.expectErr 157 158 testFaultInjectionConfig(t, faultInjectionConfig, expectErr) 159 }) 160 } 161 } 162 163 func TestFaultInjectionDataStoreFactory_NewQueueV2_MethodConfig(t *testing.T) { 164 // Tests fault injection for QueueV2 with a per-method error config and no flat error rate. 165 166 t.Parallel() 167 168 for _, tc := range []struct { 169 name string 170 errorRate float64 171 expectErr bool 172 }{ 173 { 174 name: "No errors", 175 errorRate: 0.0, 176 expectErr: false, 177 }, 178 { 179 name: "All errors", 180 errorRate: 1.0, 181 expectErr: true, 182 }, 183 } { 184 tc := tc 185 t.Run(tc.name, func(t *testing.T) { 186 t.Parallel() 187 188 methodConfig := config.FaultInjectionMethodConfig{ 189 Errors: map[string]float64{ 190 "ResourceExhausted": tc.errorRate, 191 }, 192 } 193 faultInjectionConfig := &config.FaultInjection{ 194 Rate: 0.0, 195 Targets: config.FaultInjectionTargets{ 196 DataStores: map[config.DataStoreName]config.FaultInjectionDataStoreConfig{ 197 config.QueueV2Name: { 198 Methods: map[string]config.FaultInjectionMethodConfig{ 199 "EnqueueMessage": methodConfig, 200 "ReadMessages": methodConfig, 201 "CreateQueue": methodConfig, 202 "RangeDeleteMessages": methodConfig, 203 }, 204 }, 205 }, 206 }, 207 } 208 expectErr := tc.expectErr 209 210 testFaultInjectionConfig(t, faultInjectionConfig, expectErr) 211 }) 212 } 213 } 214 215 func testFaultInjectionConfig(t *testing.T, faultInjectionConfig *config.FaultInjection, expectErr bool) { 216 t.Helper() 217 218 dataStoreFactory := &testDataStoreFactory{} 219 factory := client.NewFaultInjectionDatastoreFactory(faultInjectionConfig, dataStoreFactory) 220 221 q, err := factory.NewQueueV2() 222 require.NoError(t, err) 223 224 _, err = q.EnqueueMessage(context.Background(), nil) 225 verifyMethod(t, expectErr, err, dataStoreFactory.enqueueRequests) 226 227 _, err = q.ReadMessages(context.Background(), nil) 228 verifyMethod(t, expectErr, err, dataStoreFactory.readRequests) 229 230 _, err = q.CreateQueue(context.Background(), nil) 231 verifyMethod(t, expectErr, err, dataStoreFactory.createRequests) 232 233 _, err = q.RangeDeleteMessages(context.Background(), nil) 234 verifyMethod(t, expectErr, err, dataStoreFactory.rangeDeleteRequests) 235 236 q2, err := factory.NewQueueV2() 237 require.NoError(t, err) 238 assert.Equal(t, q, q2, "NewQueueV2 should cache previous result") 239 } 240 241 func verifyMethod(t *testing.T, expectErr bool, err error, requests int) { 242 t.Helper() 243 if expectErr { 244 assert.Error(t, err) 245 assert.Zero(t, requests) 246 } else { 247 assert.NoError(t, err) 248 assert.Equal(t, 1, requests) 249 } 250 }