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  }