go.uber.org/cadence@v1.2.9/internal/workflow_shadower_activities_test.go (about) 1 // Copyright (c) 2017-2021 Uber Technologies Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package internal 22 23 import ( 24 "context" 25 "math" 26 "math/rand" 27 "testing" 28 "time" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/require" 32 "github.com/stretchr/testify/suite" 33 "go.uber.org/zap/zaptest" 34 35 "go.uber.org/cadence/.gen/go/cadence/workflowservicetest" 36 "go.uber.org/cadence/.gen/go/shadower" 37 "go.uber.org/cadence/.gen/go/shared" 38 "go.uber.org/cadence/internal/common" 39 ) 40 41 type workflowShadowerActivitiesSuite struct { 42 *require.Assertions 43 suite.Suite 44 WorkflowTestSuite 45 46 controller *gomock.Controller 47 mockService *workflowservicetest.MockClient 48 49 env *TestActivityEnvironment 50 testReplayer *WorkflowReplayer 51 testWorkflowHistory *shared.History 52 } 53 54 func TestWorkflowShadowerActivitiesSuite(t *testing.T) { 55 s := new(workflowShadowerActivitiesSuite) 56 suite.Run(t, s) 57 } 58 59 func (s *workflowShadowerActivitiesSuite) SetupTest() { 60 s.Assertions = require.New(s.T()) 61 62 s.controller = gomock.NewController(s.T()) 63 s.mockService = workflowservicetest.NewMockClient(s.controller) 64 s.SetLogger(zaptest.NewLogger(s.T())) 65 66 s.env = s.NewTestActivityEnvironment() 67 s.testReplayer = NewWorkflowReplayer() 68 s.testReplayer.RegisterWorkflow(testReplayWorkflow) 69 s.testWorkflowHistory = getTestReplayWorkflowFullHistory(s.T()) 70 71 activityContext := context.Background() 72 activityContext = context.WithValue(activityContext, serviceClientContextKey, s.mockService) 73 activityContext = context.WithValue(activityContext, workflowReplayerContextKey, s.testReplayer) 74 s.env.SetWorkerOptions(WorkerOptions{ 75 BackgroundActivityContext: activityContext, 76 Logger: zaptest.NewLogger(s.T()), 77 }) 78 s.env.RegisterActivityWithOptions(scanWorkflowActivity, RegisterActivityOptions{ 79 Name: shadower.ScanWorkflowActivityName, 80 }) 81 s.env.RegisterActivityWithOptions(replayWorkflowActivity, RegisterActivityOptions{ 82 Name: shadower.ReplayWorkflowActivityName, 83 }) 84 } 85 86 func (s *workflowShadowerActivitiesSuite) TearDownTest() { 87 s.controller.Finish() 88 } 89 90 func (s *workflowShadowerActivitiesSuite) TestScanWorkflowActivity_Succeed() { 91 numExecutions := 1000 92 s.mockService.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.ListWorkflowExecutionsResponse{ 93 Executions: newTestWorkflowExecutions(numExecutions), 94 NextPageToken: []byte{1, 2, 3}, 95 }, nil).Times(1) 96 97 params := shadower.ScanWorkflowActivityParams{ 98 Domain: common.StringPtr(defaultTestDomain), 99 WorkflowQuery: common.StringPtr("some random workflow visibility query"), 100 SamplingRate: common.Float64Ptr(0.5), 101 } 102 103 resultValue, err := s.env.ExecuteActivity(shadower.ScanWorkflowActivityName, params) 104 s.NoError(err) 105 106 var result shadower.ScanWorkflowActivityResult 107 s.NoError(resultValue.Get(&result)) 108 s.NotNil(result.NextPageToken) 109 s.True(len(result.Executions) > 0) 110 s.True(len(result.Executions) < numExecutions) 111 } 112 113 func (s *workflowShadowerActivitiesSuite) TestScanWorkflowActivity_MinResultSize() { 114 numExecutionsPerScan := 3 115 s.mockService.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.ListWorkflowExecutionsResponse{ 116 Executions: newTestWorkflowExecutions(numExecutionsPerScan), 117 NextPageToken: []byte{1, 2, 3}, 118 }, nil).Times(int(math.Ceil(float64(minScanWorkflowResultSize) / float64(numExecutionsPerScan)))) 119 120 params := shadower.ScanWorkflowActivityParams{ 121 Domain: common.StringPtr(defaultTestDomain), 122 WorkflowQuery: common.StringPtr("some random workflow visibility query"), 123 SamplingRate: common.Float64Ptr(1), 124 } 125 126 resultValue, err := s.env.ExecuteActivity(shadower.ScanWorkflowActivityName, params) 127 s.NoError(err) 128 129 var result shadower.ScanWorkflowActivityResult 130 s.NoError(resultValue.Get(&result)) 131 s.NotNil(result.NextPageToken) 132 s.True(len(result.Executions) >= minScanWorkflowResultSize) 133 } 134 135 func (s *workflowShadowerActivitiesSuite) TestScanWorkflowActivity_CompletionTime() { 136 activityTimeoutSeconds := int32(1) 137 s.mockService.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.ListWorkflowExecutionsResponse{ 138 Executions: newTestWorkflowExecutions(1), 139 NextPageToken: []byte{1, 2, 3}, 140 }, nil).MaxTimes(int(time.Duration(activityTimeoutSeconds) * time.Second / scanWorkflowWaitPeriod)) 141 142 params := shadower.ScanWorkflowActivityParams{ 143 Domain: common.StringPtr(defaultTestDomain), 144 WorkflowQuery: common.StringPtr("some random workflow visibility query"), 145 SamplingRate: common.Float64Ptr(0.00000001), 146 } 147 148 resultValue, err := s.env.impl.executeActivityWithOptions( 149 activityOptions{ 150 ScheduleToCloseTimeoutSeconds: activityTimeoutSeconds, 151 StartToCloseTimeoutSeconds: activityTimeoutSeconds, 152 }, 153 shadower.ScanWorkflowActivityName, 154 params, 155 ) 156 s.NoError(err) 157 158 var result shadower.ScanWorkflowActivityResult 159 s.NoError(resultValue.Get(&result)) 160 s.NotNil(result.NextPageToken) 161 s.Empty(result.Executions) 162 } 163 164 func (s *workflowShadowerActivitiesSuite) TestScanWorkflowActivity_InvalidQuery() { 165 s.mockService.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any(), callOptions()...).Return(nil, &shared.BadRequestError{ 166 Message: "invalid query", 167 }).Times(1) 168 169 params := shadower.ScanWorkflowActivityParams{ 170 Domain: common.StringPtr(defaultTestDomain), 171 WorkflowQuery: common.StringPtr("invalid workflow visibility query"), 172 } 173 174 _, err := s.env.ExecuteActivity(shadower.ScanWorkflowActivityName, params) 175 s.Error(err) 176 s.Equal(shadower.ErrReasonInvalidQuery, err.Error()) 177 } 178 179 func (s *workflowShadowerActivitiesSuite) TestReplayWorkflowExecutionActivity_NoPreviousProgress() { 180 numExecutions := 10 181 s.mockService.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.GetWorkflowExecutionHistoryResponse{ 182 History: s.testWorkflowHistory, 183 }, nil).Times(numExecutions) 184 185 params := newTestReplayWorkflowActivityParams(numExecutions) 186 187 resultValue, err := s.env.ExecuteActivity(shadower.ReplayWorkflowActivityName, params) 188 s.NoError(err) 189 190 var result shadower.ReplayWorkflowActivityResult 191 s.NoError(resultValue.Get(&result)) 192 s.Equal(int32(numExecutions), result.GetSucceeded()) 193 s.Zero(result.GetSkipped()) 194 s.Zero(result.GetFailed()) 195 } 196 197 func (s *workflowShadowerActivitiesSuite) TestReplayWorkflowExecutionActivity_WithPreviousProgress() { 198 progress := replayWorkflowActivityProgress{ 199 Result: shadower.ReplayWorkflowActivityResult{ 200 Succeeded: common.Int32Ptr(3), 201 Skipped: common.Int32Ptr(2), 202 Failed: common.Int32Ptr(0), 203 }, 204 NextExecutionIdx: 5, 205 } 206 s.env.SetHeartbeatDetails(progress) 207 208 numExecutions := 10 209 s.mockService.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.GetWorkflowExecutionHistoryResponse{ 210 History: s.testWorkflowHistory, 211 }, nil).Times(numExecutions - progress.NextExecutionIdx) 212 213 params := newTestReplayWorkflowActivityParams(numExecutions) 214 215 resultValue, err := s.env.ExecuteActivity(shadower.ReplayWorkflowActivityName, params) 216 s.NoError(err) 217 218 var result shadower.ReplayWorkflowActivityResult 219 s.NoError(resultValue.Get(&result)) 220 s.Equal(progress.Result.GetSucceeded()+int32(numExecutions-progress.NextExecutionIdx), result.GetSucceeded()) 221 s.Equal(progress.Result.GetSkipped(), result.GetSkipped()) 222 s.Equal(progress.Result.GetFailed(), result.GetFailed()) 223 } 224 225 func (s *workflowShadowerActivitiesSuite) TestReplayWorkflowExecutionActivity_RandomReplayResult() { 226 numSucceed := int32(0) 227 numFailed := int32(0) 228 numSkipped := int32(0) 229 numExecutions := 100 230 231 mismatchWorkflowHistory := getTestReplayWorkflowMismatchHistory(s.T()) 232 for i := 0; i != numExecutions; i++ { 233 switch rand.Intn(3) { 234 case 0: 235 numSucceed++ 236 s.mockService.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.GetWorkflowExecutionHistoryResponse{ 237 History: s.testWorkflowHistory, 238 }, nil).Times(1) 239 case 1: 240 numSkipped++ 241 s.mockService.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(nil, &shared.EntityNotExistsError{}).Times(1) 242 case 2: 243 numFailed++ 244 s.mockService.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.GetWorkflowExecutionHistoryResponse{ 245 History: mismatchWorkflowHistory, 246 }, nil).Times(1) 247 } 248 } 249 250 params := newTestReplayWorkflowActivityParams(numExecutions) 251 252 resultValue, err := s.env.ExecuteActivity(shadower.ReplayWorkflowActivityName, params) 253 s.NoError(err) 254 255 var result shadower.ReplayWorkflowActivityResult 256 s.NoError(resultValue.Get(&result)) 257 s.Equal(numSucceed, result.GetSucceeded()) 258 s.Equal(numSkipped, result.GetSkipped()) 259 s.Equal(numFailed, result.GetFailed()) 260 } 261 262 func (s *workflowShadowerActivitiesSuite) TestReplayWorkflowExecutionActivity_WorkflowNotRegistered() { 263 s.mockService.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(&shared.GetWorkflowExecutionHistoryResponse{ 264 History: getTestReplayWorkflowLocalActivityHistory(s.T()), // this workflow type is not registered 265 }, nil).Times(1) 266 267 params := newTestReplayWorkflowActivityParams(5) 268 269 _, err := s.env.ExecuteActivity(shadower.ReplayWorkflowActivityName, params) 270 s.Error(err) 271 s.Equal(shadower.ErrReasonWorkflowTypeNotRegistered, err.Error()) 272 } 273 274 func newTestReplayWorkflowActivityParams(numExecutions int) shadower.ReplayWorkflowActivityParams { 275 params := shadower.ReplayWorkflowActivityParams{ 276 Domain: common.StringPtr(defaultTestDomain), 277 Executions: make([]*shared.WorkflowExecution, 0, numExecutions), 278 } 279 for i := 0; i != numExecutions; i++ { 280 params.Executions = append(params.Executions, &shared.WorkflowExecution{}) 281 } 282 return params 283 }