go.uber.org/cadence@v1.2.9/internal/workflow_testsuite.go (about) 1 // Copyright (c) 2017-2020 Uber Technologies Inc. 2 // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 package internal 23 24 import ( 25 "context" 26 "fmt" 27 "reflect" 28 "time" 29 30 "github.com/stretchr/testify/mock" 31 "github.com/uber-go/tally" 32 "go.uber.org/zap" 33 34 "go.uber.org/cadence/.gen/go/shared" 35 ) 36 37 type ( 38 // EncodedValues is a type alias used to encapsulate/extract encoded arguments from workflow/activity. 39 EncodedValues struct { 40 values []byte 41 dataConverter DataConverter 42 } 43 44 // WorkflowTestSuite is the test suite to run unit tests for workflow/activity. 45 WorkflowTestSuite struct { 46 logger *zap.Logger 47 scope tally.Scope 48 ctxProps []ContextPropagator 49 header *shared.Header 50 } 51 52 // TestWorkflowEnvironment is the environment that you use to test workflow 53 TestWorkflowEnvironment struct { 54 mock.Mock 55 impl *testWorkflowEnvironmentImpl 56 } 57 58 // TestActivityEnvironment is the environment that you use to test activity 59 TestActivityEnvironment struct { 60 impl *testWorkflowEnvironmentImpl 61 } 62 63 // MockCallWrapper is a wrapper to mock.Call. It offers the ability to wait on workflow's clock instead of wall clock. 64 MockCallWrapper struct { 65 call *mock.Call 66 env *TestWorkflowEnvironment 67 68 runFn func(args mock.Arguments) 69 waitDuration func() time.Duration 70 } 71 ) 72 73 func newEncodedValues(values []byte, dc DataConverter) Values { 74 if dc == nil { 75 dc = getDefaultDataConverter() 76 } 77 return &EncodedValues{values, dc} 78 } 79 80 // Get extract data from encoded data to desired value type. valuePtr is pointer to the actual value type. 81 func (b EncodedValues) Get(valuePtr ...interface{}) error { 82 if !b.HasValues() { 83 return ErrNoData 84 } 85 return b.dataConverter.FromData(b.values, valuePtr...) 86 } 87 88 // HasValues return whether there are values 89 func (b EncodedValues) HasValues() bool { 90 return b.values != nil 91 } 92 93 // NewTestWorkflowEnvironment creates a new instance of TestWorkflowEnvironment. Use the returned TestWorkflowEnvironment 94 // to run your workflow in the test environment. 95 func (s *WorkflowTestSuite) NewTestWorkflowEnvironment() *TestWorkflowEnvironment { 96 return &TestWorkflowEnvironment{impl: newTestWorkflowEnvironmentImpl(s, nil)} 97 } 98 99 // NewTestActivityEnvironment creates a new instance of TestActivityEnvironment. Use the returned TestActivityEnvironment 100 // to run your activity in the test environment. 101 func (s *WorkflowTestSuite) NewTestActivityEnvironment() *TestActivityEnvironment { 102 return &TestActivityEnvironment{impl: newTestWorkflowEnvironmentImpl(s, nil)} 103 } 104 105 // SetLogger sets the logger for this WorkflowTestSuite. If you don't set logger, test suite will create a default logger 106 // with Debug level logging enabled. 107 func (s *WorkflowTestSuite) SetLogger(logger *zap.Logger) { 108 s.logger = logger 109 } 110 111 // GetLogger gets the logger for this WorkflowTestSuite. 112 func (s *WorkflowTestSuite) GetLogger() *zap.Logger { 113 return s.logger 114 } 115 116 // SetMetricsScope sets the metrics scope for this WorkflowTestSuite. If you don't set scope, test suite will use 117 // tally.NoopScope 118 func (s *WorkflowTestSuite) SetMetricsScope(scope tally.Scope) { 119 s.scope = scope 120 } 121 122 // SetContextPropagators sets the context propagators for this WorkflowTestSuite. If you don't set context propagators, 123 // test suite will not use context propagators 124 func (s *WorkflowTestSuite) SetContextPropagators(ctxProps []ContextPropagator) { 125 s.ctxProps = ctxProps 126 } 127 128 // SetHeader sets the headers for this WorkflowTestSuite. If you don't set header, test suite will not pass headers to 129 // the workflow 130 func (s *WorkflowTestSuite) SetHeader(header *shared.Header) { 131 s.header = header 132 } 133 134 // RegisterActivity registers activity implementation with TestWorkflowEnvironment 135 func (t *TestActivityEnvironment) RegisterActivity(a interface{}) { 136 t.impl.RegisterActivity(a) 137 } 138 139 // RegisterActivityWithOptions registers activity implementation with TestWorkflowEnvironment 140 func (t *TestActivityEnvironment) RegisterActivityWithOptions(a interface{}, options RegisterActivityOptions) { 141 t.impl.RegisterActivityWithOptions(a, options) 142 } 143 144 // ExecuteActivity executes an activity. The tested activity will be executed synchronously in the calling goroutinue. 145 // Caller should use Value.Get() to extract strong typed result value. 146 func (t *TestActivityEnvironment) ExecuteActivity(activityFn interface{}, args ...interface{}) (Value, error) { 147 return t.impl.executeActivity(activityFn, args...) 148 } 149 150 // ExecuteLocalActivity executes a local activity. The tested activity will be executed synchronously in the calling goroutinue. 151 // Caller should use Value.Get() to extract strong typed result value. 152 func (t *TestActivityEnvironment) ExecuteLocalActivity(activityFn interface{}, args ...interface{}) (val Value, err error) { 153 return t.impl.executeLocalActivity(activityFn, args...) 154 } 155 156 // SetWorkerOptions sets the WorkerOptions that will be use by TestActivityEnvironment. TestActivityEnvironment will 157 // use options of Identity, MetricsScope and BackgroundActivityContext on the WorkerOptions. Other options are ignored. 158 // Note: WorkerOptions is defined in internal package, use public type worker.Options instead. 159 func (t *TestActivityEnvironment) SetWorkerOptions(options WorkerOptions) *TestActivityEnvironment { 160 t.impl.setWorkerOptions(options) 161 return t 162 } 163 164 // SetTestTimeout sets the wall clock timeout for this activity test run. When test timeout happen, it means activity is 165 // taking too long. 166 func (t *TestActivityEnvironment) SetTestTimeout(idleTimeout time.Duration) *TestActivityEnvironment { 167 t.impl.testTimeout = idleTimeout 168 return t 169 } 170 171 // SetHeartbeatDetails sets the heartbeat details to be returned from activity.GetHeartbeatDetails() 172 func (t *TestActivityEnvironment) SetHeartbeatDetails(details interface{}) { 173 t.impl.setHeartbeatDetails(details) 174 } 175 176 // SetWorkerStopChannel sets the worker stop channel to be returned from activity.GetWorkerStopChannel(context) 177 // To test your activity on worker stop, you can provide a go channel with this function and call ExecuteActivity(). 178 // Then call close(channel) to test the activity worker stop logic. 179 func (t *TestActivityEnvironment) SetWorkerStopChannel(c chan struct{}) { 180 t.impl.setWorkerStopChannel(c) 181 } 182 183 // RegisterWorkflow register workflows 184 func (t *TestWorkflowEnvironment) RegisterWorkflow(w interface{}) { 185 t.impl.RegisterWorkflow(w) 186 } 187 188 // RegisterWorkflowWithOptions register workflows 189 func (t *TestWorkflowEnvironment) RegisterWorkflowWithOptions(w interface{}, options RegisterWorkflowOptions) { 190 if len(t.ExpectedCalls) > 0 { 191 panic("RegisterWorkflow calls cannot follow mock related ones like OnWorkflow or similar") 192 } 193 t.impl.RegisterWorkflowWithOptions(w, options) 194 } 195 196 // RegisterActivity registers activity 197 func (t *TestWorkflowEnvironment) RegisterActivity(a interface{}) { 198 t.impl.RegisterActivity(a) 199 } 200 201 // RegisterActivityWithOptions registers activity 202 func (t *TestWorkflowEnvironment) RegisterActivityWithOptions(a interface{}, options RegisterActivityOptions) { 203 if len(t.ExpectedCalls) > 0 { 204 panic("RegisterActivity calls cannot follow mock related ones like OnActivity or similar") 205 } 206 t.impl.RegisterActivityWithOptions(a, options) 207 } 208 209 // SetStartTime sets the start time of the workflow. This is optional, default start time will be the wall clock time when 210 // workflow starts. Start time is the workflow.Now(ctx) time at the beginning of the workflow. 211 func (t *TestWorkflowEnvironment) SetStartTime(startTime time.Time) { 212 t.impl.setStartTime(startTime) 213 } 214 215 // OnActivity setup a mock call for activity. Parameter activity must be activity function (func) or activity name (string). 216 // You must call Return() with appropriate parameters on the returned *MockCallWrapper instance. The supplied parameters to 217 // the Return() call should either be a function that has exact same signature as the mocked activity, or it should be 218 // mock values with the same types as the mocked activity function returns. 219 // Example: assume the activity you want to mock has function signature as: 220 // 221 // func MyActivity(ctx context.Context, msg string) (string, error) 222 // 223 // You can mock it by return a function with exact same signature: 224 // 225 // t.OnActivity(MyActivity, mock.Anything, mock.Anything).Return(func(ctx context.Context, msg string) (string, error) { 226 // // your mock function implementation 227 // return "", nil 228 // }) 229 // 230 // OR return mock values with same types as activity function's return types: 231 // 232 // t.OnActivity(MyActivity, mock.Anything, mock.Anything).Return("mock_result", nil) 233 func (t *TestWorkflowEnvironment) OnActivity(activity interface{}, args ...interface{}) *MockCallWrapper { 234 fType := reflect.TypeOf(activity) 235 var call *mock.Call 236 switch fType.Kind() { 237 case reflect.Func: 238 fnType := reflect.TypeOf(activity) 239 if err := validateFnFormat(fnType, false); err != nil { 240 panic(err) 241 } 242 fnName := getActivityFunctionName(t.impl.registry, activity) 243 call = t.Mock.On(fnName, args...) 244 245 case reflect.String: 246 call = t.Mock.On(activity.(string), args...) 247 default: 248 panic("activity must be function or string") 249 } 250 251 return t.wrapCall(call) 252 } 253 254 // ErrMockStartChildWorkflowFailed is special error used to indicate the mocked child workflow should fail to start. 255 // This error is also exposed as public as testsuite.ErrMockStartChildWorkflowFailed 256 var ErrMockStartChildWorkflowFailed = fmt.Errorf("start child workflow failed: %v", shared.ChildWorkflowExecutionFailedCauseWorkflowAlreadyRunning) 257 258 // OnWorkflow setup a mock call for workflow. Parameter workflow must be workflow function (func) or workflow name (string). 259 // You must call Return() with appropriate parameters on the returned *MockCallWrapper instance. The supplied parameters to 260 // the Return() call should either be a function that has exact same signature as the mocked workflow, or it should be 261 // mock values with the same types as the mocked workflow function returns. 262 // Example: assume the workflow you want to mock has function signature as: 263 // 264 // func MyChildWorkflow(ctx workflow.Context, msg string) (string, error) 265 // 266 // You can mock it by return a function with exact same signature: 267 // 268 // t.OnWorkflow(MyChildWorkflow, mock.Anything, mock.Anything).Return(func(ctx workflow.Context, msg string) (string, error) { 269 // // your mock function implementation 270 // return "", nil 271 // }) 272 // 273 // OR return mock values with same types as workflow function's return types: 274 // 275 // t.OnWorkflow(MyChildWorkflow, mock.Anything, mock.Anything).Return("mock_result", nil) 276 // 277 // You could also setup mock to simulate start child workflow failure case by returning ErrMockStartChildWorkflowFailed 278 // as error. 279 func (t *TestWorkflowEnvironment) OnWorkflow(workflow interface{}, args ...interface{}) *MockCallWrapper { 280 fType := reflect.TypeOf(workflow) 281 var call *mock.Call 282 switch fType.Kind() { 283 case reflect.Func: 284 fnType := reflect.TypeOf(workflow) 285 if err := validateFnFormat(fnType, true); err != nil { 286 panic(err) 287 } 288 fnName := getWorkflowFunctionName(t.impl.registry, workflow) 289 if alias, ok := t.impl.registry.getWorkflowAlias(fnName); ok { 290 fnName = alias 291 } 292 call = t.Mock.On(fnName, args...) 293 case reflect.String: 294 call = t.Mock.On(workflow.(string), args...) 295 default: 296 panic("activity must be function or string") 297 } 298 299 return t.wrapCall(call) 300 } 301 302 const mockMethodForSignalExternalWorkflow = "workflow.SignalExternalWorkflow" 303 const mockMethodForRequestCancelExternalWorkflow = "workflow.RequestCancelExternalWorkflow" 304 const mockMethodForGetVersion = "workflow.GetVersion" 305 const mockMethodForUpsertSearchAttributes = "workflow.UpsertSearchAttributes" 306 307 // OnSignalExternalWorkflow setup a mock for sending signal to external workflow. 308 // This TestWorkflowEnvironment handles sending signals between the workflows that are started from the root workflow. 309 // For example, sending signals between parent and child workflows. Or sending signals between 2 child workflows. 310 // However, it does not know what to do if your tested workflow code is sending signal to external unknown workflows. 311 // In that case, you will need to setup mock for those signal calls. 312 // Some examples of how to setup mock: 313 // 314 // - mock for specific target workflow that matches specific signal name and signal data 315 // env.OnSignalExternalWorkflow("test-domain", "test-workflow-id1", "test-runid1", "test-signal", "test-data").Return(nil).Once() 316 // - mock for anything and succeed the send 317 // env.OnSignalExternalWorkflow(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() 318 // - mock for anything and fail the send 319 // env.OnSignalExternalWorkflow(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("unknown external workflow")).Once() 320 // - mock function for SignalExternalWorkflow 321 // env.OnSignalExternalWorkflow(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return( 322 // func(domainName, workflowID, runID, signalName string, arg interface{}) error { 323 // // you can do differently based on the parameters 324 // return nil 325 // }) 326 func (t *TestWorkflowEnvironment) OnSignalExternalWorkflow(domainName, workflowID, runID, signalName, arg interface{}) *MockCallWrapper { 327 call := t.Mock.On(mockMethodForSignalExternalWorkflow, domainName, workflowID, runID, signalName, arg) 328 return t.wrapCall(call) 329 } 330 331 // OnRequestCancelExternalWorkflow setup a mock for cancellation of external workflow. 332 // This TestWorkflowEnvironment handles cancellation of workflows that are started from the root workflow. 333 // For example, cancellation sent from parent to child workflows. Or cancellation between 2 child workflows. 334 // However, it does not know what to do if your tested workflow code is sending cancellation to external unknown workflows. 335 // In that case, you will need to setup mock for those cancel calls. 336 // Some examples of how to setup mock: 337 // 338 // - mock for specific target workflow that matches specific workflow ID and run ID 339 // env.OnRequestCancelExternalWorkflow("test-domain", "test-workflow-id1", "test-runid1").Return(nil).Once() 340 // - mock for anything and succeed the cancellation 341 // env.OnRequestCancelExternalWorkflow(mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() 342 // - mock for anything and fail the cancellation 343 // env.OnRequestCancelExternalWorkflow(mock.Anything, mock.Anything, mock.Anything).Return(errors.New("unknown external workflow")).Once() 344 // - mock function for RequestCancelExternalWorkflow 345 // env.OnRequestCancelExternalWorkflow(mock.Anything, mock.Anything, mock.Anything).Return( 346 // func(domainName, workflowID, runID) error { 347 // // you can do differently based on the parameters 348 // return nil 349 // }) 350 func (t *TestWorkflowEnvironment) OnRequestCancelExternalWorkflow(domainName, workflowID, runID string) *MockCallWrapper { 351 call := t.Mock.On(mockMethodForRequestCancelExternalWorkflow, domainName, workflowID, runID) 352 return t.wrapCall(call) 353 } 354 355 // OnGetVersion setup a mock for workflow.GetVersion() call. By default, if mock is not setup, the GetVersion call from 356 // workflow code will always return the maxSupported version. Make it not possible to test old version branch. With this 357 // mock support, it is possible to test code branch for different versions. 358 // 359 // Note: mock can be setup for a specific changeID. Or if mock.Anything is used as changeID then all calls to GetVersion 360 // will be mocked. Mock for a specific changeID has higher priority over mock.Anything. 361 func (t *TestWorkflowEnvironment) OnGetVersion(changeID string, minSupported, maxSupported Version) *MockCallWrapper { 362 call := t.Mock.On(getMockMethodForGetVersion(changeID), changeID, minSupported, maxSupported) 363 return t.wrapCall(call) 364 } 365 366 // OnUpsertSearchAttributes setup a mock for workflow.UpsertSearchAttributes call. 367 // If mock is not setup, the UpsertSearchAttributes call will only validate input attributes. 368 // If mock is setup, all UpsertSearchAttributes calls in workflow have to be mocked. 369 func (t *TestWorkflowEnvironment) OnUpsertSearchAttributes(attributes map[string]interface{}) *MockCallWrapper { 370 call := t.Mock.On(mockMethodForUpsertSearchAttributes, attributes) 371 return t.wrapCall(call) 372 } 373 374 func (t *TestWorkflowEnvironment) wrapCall(call *mock.Call) *MockCallWrapper { 375 callWrapper := &MockCallWrapper{call: call, env: t} 376 call.Run(t.impl.getMockRunFn(callWrapper)) 377 return callWrapper 378 } 379 380 // Once indicates that the mock should only return the value once. 381 func (c *MockCallWrapper) Once() *MockCallWrapper { 382 return c.Times(1) 383 } 384 385 // Twice indicates that the mock should only return the value twice. 386 func (c *MockCallWrapper) Twice() *MockCallWrapper { 387 return c.Times(2) 388 } 389 390 // Times indicates that the mock should only return the indicated number of times. 391 func (c *MockCallWrapper) Times(i int) *MockCallWrapper { 392 c.call.Times(i) 393 return c 394 } 395 396 // Run sets a handler to be called before returning. It can be used when mocking a method such as unmarshalers that 397 // takes a pointer to a struct and sets properties in such struct. 398 func (c *MockCallWrapper) Run(fn func(args mock.Arguments)) *MockCallWrapper { 399 c.runFn = fn 400 return c 401 } 402 403 // After sets how long to wait on workflow's clock before the mock call returns. 404 func (c *MockCallWrapper) After(d time.Duration) *MockCallWrapper { 405 c.waitDuration = func() time.Duration { return d } 406 return c 407 } 408 409 // AfterFn sets a function which will tell how long to wait on workflow's clock before the mock call returns. 410 func (c *MockCallWrapper) AfterFn(fn func() time.Duration) *MockCallWrapper { 411 c.waitDuration = fn 412 return c 413 } 414 415 // Return specifies the return arguments for the expectation. 416 func (c *MockCallWrapper) Return(returnArguments ...interface{}) *MockCallWrapper { 417 c.call.Return(returnArguments...) 418 return c 419 } 420 421 // ExecuteWorkflow executes a workflow, wait until workflow complete. It will fail the test if workflow is blocked and 422 // cannot complete within TestTimeout (set by SetTestTimeout()). 423 func (t *TestWorkflowEnvironment) ExecuteWorkflow(workflowFn interface{}, args ...interface{}) { 424 t.impl.mock = &t.Mock 425 t.impl.executeWorkflow(workflowFn, args...) 426 } 427 428 // Now returns the current workflow time (a.k.a workflow.Now() time) of this TestWorkflowEnvironment. 429 func (t *TestWorkflowEnvironment) Now() time.Time { 430 return t.impl.Now() 431 } 432 433 // SetWorkerOptions sets the WorkerOptions for TestWorkflowEnvironment. TestWorkflowEnvironment will use options set by 434 // use options of Identity, MetricsScope and BackgroundActivityContext on the WorkerOptions. Other options are ignored. 435 // Note: WorkerOptions is defined in internal package, use public type worker.Options instead. 436 func (t *TestWorkflowEnvironment) SetWorkerOptions(options WorkerOptions) *TestWorkflowEnvironment { 437 t.impl.setWorkerOptions(options) 438 return t 439 } 440 441 // SetWorkerStopChannel sets the activity worker stop channel to be returned from activity.GetWorkerStopChannel(context) 442 // You can use this function to set the activity worker stop channel and use close(channel) to test your activity execution 443 // from workflow execution. 444 func (t *TestWorkflowEnvironment) SetWorkerStopChannel(c chan struct{}) { 445 t.impl.setWorkerStopChannel(c) 446 } 447 448 // SetTestTimeout sets the idle timeout based on wall clock for this tested workflow. Idle is when workflow is blocked 449 // waiting on events (including timer, activity, child workflow, signal etc). If there is no event happening longer than 450 // this idle timeout, the test framework would stop the workflow and return timeout error. 451 // This is based on real wall clock time, not the workflow time (a.k.a workflow.Now() time). 452 func (t *TestWorkflowEnvironment) SetTestTimeout(idleTimeout time.Duration) *TestWorkflowEnvironment { 453 t.impl.testTimeout = idleTimeout 454 return t 455 } 456 457 // SetWorkflowTimeout sets the execution timeout for this tested workflow. This test framework uses mock clock internally 458 // and when workflow is blocked on timer, it will auto forward the mock clock. Use SetWorkflowTimeout() to enforce a 459 // workflow execution timeout to return timeout error when the workflow mock clock is moved head of the timeout. 460 // This is based on the workflow time (a.k.a workflow.Now() time). 461 func (t *TestWorkflowEnvironment) SetWorkflowTimeout(executionTimeout time.Duration) *TestWorkflowEnvironment { 462 t.impl.executionTimeout = executionTimeout 463 return t 464 } 465 466 // SetWorkflowCronSchedule sets the Cron schedule for this tested workflow. 467 // The first execution of the workflow will not adhere to the Cron schedule and will start executing immediately. 468 // Consecutive iterations will follow the specified schedule. 469 // Use SetWorkflowCronMaxIterations() to enforce a limit on the number of consecutive iterations after the initial 470 // execution. 471 func (t *TestWorkflowEnvironment) SetWorkflowCronSchedule(cron string) *TestWorkflowEnvironment { 472 t.impl.setCronSchedule(cron) 473 return t 474 } 475 476 // SetWorkflowCronMaxIterations sets the a limit on the number of Cron iterations, not including the first one 477 // of the tested workflow. 478 func (t *TestWorkflowEnvironment) SetWorkflowCronMaxIterations(maxIterations int) *TestWorkflowEnvironment { 479 t.impl.setCronMaxIterationas(maxIterations) 480 return t 481 } 482 483 // SetOnActivityStartedListener sets a listener that will be called before activity starts execution. 484 // Note: ActivityInfo is defined in internal package, use public type activity.Info instead. 485 func (t *TestWorkflowEnvironment) SetOnActivityStartedListener( 486 listener func(activityInfo *ActivityInfo, ctx context.Context, args Values)) *TestWorkflowEnvironment { 487 t.impl.onActivityStartedListener = listener 488 return t 489 } 490 491 // SetOnActivityCompletedListener sets a listener that will be called after an activity is completed. 492 // Note: ActivityInfo is defined in internal package, use public type activity.Info instead. 493 func (t *TestWorkflowEnvironment) SetOnActivityCompletedListener( 494 listener func(activityInfo *ActivityInfo, result Value, err error)) *TestWorkflowEnvironment { 495 t.impl.onActivityCompletedListener = listener 496 return t 497 } 498 499 // SetOnActivityCanceledListener sets a listener that will be called after an activity is canceled. 500 // Note: ActivityInfo is defined in internal package, use public type activity.Info instead. 501 func (t *TestWorkflowEnvironment) SetOnActivityCanceledListener( 502 listener func(activityInfo *ActivityInfo)) *TestWorkflowEnvironment { 503 t.impl.onActivityCanceledListener = listener 504 return t 505 } 506 507 // SetOnActivityHeartbeatListener sets a listener that will be called when activity heartbeat. 508 // Note: ActivityInfo is defined in internal package, use public type activity.Info instead. 509 func (t *TestWorkflowEnvironment) SetOnActivityHeartbeatListener( 510 listener func(activityInfo *ActivityInfo, details Values)) *TestWorkflowEnvironment { 511 t.impl.onActivityHeartbeatListener = listener 512 return t 513 } 514 515 // SetOnChildWorkflowStartedListener sets a listener that will be called before a child workflow starts execution. 516 // Note: WorkflowInfo is defined in internal package, use public type workflow.Info instead. 517 func (t *TestWorkflowEnvironment) SetOnChildWorkflowStartedListener( 518 listener func(workflowInfo *WorkflowInfo, ctx Context, args Values)) *TestWorkflowEnvironment { 519 t.impl.onChildWorkflowStartedListener = listener 520 return t 521 } 522 523 // SetOnChildWorkflowCompletedListener sets a listener that will be called after a child workflow is completed. 524 // Note: WorkflowInfo is defined in internal package, use public type workflow.Info instead. 525 func (t *TestWorkflowEnvironment) SetOnChildWorkflowCompletedListener( 526 listener func(workflowInfo *WorkflowInfo, result Value, err error)) *TestWorkflowEnvironment { 527 t.impl.onChildWorkflowCompletedListener = listener 528 return t 529 } 530 531 // SetOnChildWorkflowCanceledListener sets a listener that will be called when a child workflow is canceled. 532 // Note: WorkflowInfo is defined in internal package, use public type workflow.Info instead. 533 func (t *TestWorkflowEnvironment) SetOnChildWorkflowCanceledListener( 534 listener func(workflowInfo *WorkflowInfo)) *TestWorkflowEnvironment { 535 t.impl.onChildWorkflowCanceledListener = listener 536 return t 537 } 538 539 // SetOnTimerScheduledListener sets a listener that will be called before a timer is scheduled. 540 func (t *TestWorkflowEnvironment) SetOnTimerScheduledListener( 541 listener func(timerID string, duration time.Duration)) *TestWorkflowEnvironment { 542 t.impl.onTimerScheduledListener = listener 543 return t 544 } 545 546 // SetOnTimerFiredListener sets a listener that will be called after a timer is fired. 547 func (t *TestWorkflowEnvironment) SetOnTimerFiredListener(listener func(timerID string)) *TestWorkflowEnvironment { 548 t.impl.onTimerFiredListener = listener 549 return t 550 } 551 552 // SetOnTimerCancelledListener sets a listener that will be called after a timer is cancelled 553 func (t *TestWorkflowEnvironment) SetOnTimerCancelledListener(listener func(timerID string)) *TestWorkflowEnvironment { 554 t.impl.onTimerCancelledListener = listener 555 return t 556 } 557 558 // SetOnLocalActivityStartedListener sets a listener that will be called before local activity starts execution. 559 // Note: ActivityInfo is defined in internal package, use public type activity.Info instead. 560 func (t *TestWorkflowEnvironment) SetOnLocalActivityStartedListener( 561 listener func(activityInfo *ActivityInfo, ctx context.Context, args []interface{})) *TestWorkflowEnvironment { 562 t.impl.onLocalActivityStartedListener = listener 563 return t 564 } 565 566 // SetOnLocalActivityCompletedListener sets a listener that will be called after local activity is completed. 567 // Note: ActivityInfo is defined in internal package, use public type activity.Info instead. 568 func (t *TestWorkflowEnvironment) SetOnLocalActivityCompletedListener( 569 listener func(activityInfo *ActivityInfo, result Value, err error)) *TestWorkflowEnvironment { 570 t.impl.onLocalActivityCompletedListener = listener 571 return t 572 } 573 574 // SetOnLocalActivityCanceledListener sets a listener that will be called after local activity is canceled. 575 // Note: ActivityInfo is defined in internal package, use public type activity.Info instead. 576 func (t *TestWorkflowEnvironment) SetOnLocalActivityCanceledListener( 577 listener func(activityInfo *ActivityInfo)) *TestWorkflowEnvironment { 578 t.impl.onLocalActivityCanceledListener = listener 579 return t 580 } 581 582 // IsWorkflowCompleted check if test is completed or not 583 func (t *TestWorkflowEnvironment) IsWorkflowCompleted() bool { 584 return t.impl.isTestCompleted 585 } 586 587 // GetWorkflowResult extracts the encoded result from test workflow, it also returns error from test workflow. 588 func (t *TestWorkflowEnvironment) GetWorkflowResult(valuePtr interface{}) error { 589 if !t.impl.isTestCompleted { 590 panic("workflow is not completed") 591 } 592 if t.impl.testError != nil || t.impl.testResult == nil || t.impl.testResult.HasValue() == false || valuePtr == nil { 593 return t.impl.testError 594 } 595 return t.impl.testResult.Get(valuePtr) 596 } 597 598 // GetWorkflowError return the error from test workflow 599 func (t *TestWorkflowEnvironment) GetWorkflowError() error { 600 return t.impl.testError 601 } 602 603 // CompleteActivity complete an activity that had returned activity.ErrResultPending error 604 func (t *TestWorkflowEnvironment) CompleteActivity(taskToken []byte, result interface{}, err error) error { 605 return t.impl.CompleteActivity(taskToken, result, err) 606 } 607 608 // CancelWorkflow requests cancellation (through workflow Context) to the currently running test workflow. 609 func (t *TestWorkflowEnvironment) CancelWorkflow() { 610 t.impl.cancelWorkflow(func(result []byte, err error) {}) 611 } 612 613 // SignalWorkflow sends signal to the currently running test workflow. 614 func (t *TestWorkflowEnvironment) SignalWorkflow(name string, input interface{}) { 615 t.impl.signalWorkflow(name, input, true) 616 } 617 618 // SignalWorkflowSkippingDecision sends signal to the currently running test workflow without invoking workflow code. 619 // Used to test processing of multiple buffered signals before completing workflow. 620 // It must be followed by SignalWorkflow, CancelWorkflow or CompleteActivity to force a decision. 621 func (t *TestWorkflowEnvironment) SignalWorkflowSkippingDecision(name string, input interface{}) { 622 t.impl.signalWorkflow(name, input, false) 623 } 624 625 // SignalWorkflowByID sends signal to the currently running test workflow. 626 func (t *TestWorkflowEnvironment) SignalWorkflowByID(workflowID, signalName string, input interface{}) error { 627 return t.impl.signalWorkflowByID(workflowID, signalName, input) 628 } 629 630 // QueryWorkflow queries to the currently running test workflow and returns result synchronously. 631 func (t *TestWorkflowEnvironment) QueryWorkflow(queryType string, args ...interface{}) (Value, error) { 632 return t.impl.queryWorkflow(queryType, args...) 633 } 634 635 // RegisterDelayedCallback creates a new timer with specified delayDuration using workflow clock (not wall clock). When 636 // the timer fires, the callback will be called. By default, this test suite uses mock clock which automatically move 637 // forward to fire next timer when workflow is blocked. Use this API to make some event (like activity completion, 638 // signal or workflow cancellation) at desired time. 639 // Use 0 delayDuration to send a signal to simulate SignalWithStart. 640 func (t *TestWorkflowEnvironment) RegisterDelayedCallback(callback func(), delayDuration time.Duration) { 641 t.impl.registerDelayedCallback(callback, delayDuration) 642 } 643 644 // SetActivityTaskList set the affinity between activity and tasklist. By default, activity can be invoked by any tasklist 645 // in this test environment. Use this SetActivityTaskList() to set affinity between activity and a tasklist. Once 646 // activity is set to a particular tasklist, that activity will only be available to that tasklist. 647 func (t *TestWorkflowEnvironment) SetActivityTaskList(tasklist string, activityFn ...interface{}) { 648 t.impl.setActivityTaskList(tasklist, activityFn...) 649 } 650 651 // SetLastCompletionResult sets the result to be returned from workflow.GetLastCompletionResult(). 652 func (t *TestWorkflowEnvironment) SetLastCompletionResult(result interface{}) { 653 t.impl.setLastCompletionResult(result) 654 } 655 656 // SetMemoOnStart sets the memo when start workflow. 657 func (t *TestWorkflowEnvironment) SetMemoOnStart(memo map[string]interface{}) error { 658 memoStruct, err := getWorkflowMemo(memo, t.impl.GetDataConverter()) 659 if err != nil { 660 return err 661 } 662 t.impl.workflowInfo.Memo = memoStruct 663 return nil 664 } 665 666 // SetSearchAttributesOnStart sets the search attributes when start workflow. 667 func (t *TestWorkflowEnvironment) SetSearchAttributesOnStart(searchAttributes map[string]interface{}) error { 668 attr, err := serializeSearchAttributes(searchAttributes) 669 if err != nil { 670 return err 671 } 672 t.impl.workflowInfo.SearchAttributes = attr 673 return nil 674 }