go.uber.org/cadence@v1.2.9/test/workflow_test.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 test
    23  
    24  import (
    25  	"errors"
    26  	"fmt"
    27  	"math/rand"
    28  	"time"
    29  
    30  	"go.uber.org/cadence"
    31  	"go.uber.org/cadence/.gen/go/shared"
    32  	"go.uber.org/cadence/client"
    33  	"go.uber.org/cadence/internal"
    34  	"go.uber.org/cadence/worker"
    35  	"go.uber.org/cadence/workflow"
    36  )
    37  
    38  const (
    39  	consistentQuerySignalCh = "consistent-query-signal-chan"
    40  )
    41  
    42  type Workflows struct {
    43  	nonDeterminismSimulatorWorkflowCallCount int
    44  }
    45  
    46  func (w *Workflows) Basic(ctx workflow.Context) ([]string, error) {
    47  	ctx = workflow.WithActivityOptions(ctx, w.defaultActivityOptions())
    48  	var ans1 string
    49  	workflow.GetLogger(ctx).Info("calling ExecuteActivity")
    50  	err := workflow.ExecuteActivity(ctx, "Prefix_ToUpperWithDelay", "hello", time.Second).Get(ctx, &ans1)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	var ans2 string
    55  	if err := workflow.ExecuteActivity(ctx, "Prefix_ToUpper", ans1).Get(ctx, &ans2); err != nil {
    56  		return nil, err
    57  	}
    58  	if ans2 != "HELLO" {
    59  		return nil, fmt.Errorf("incorrect return value from activity: expected=%v,got=%v", "HELLO", ans2)
    60  	}
    61  	return []string{"toUpperWithDelay", "toUpper"}, nil
    62  }
    63  
    64  func (w *Workflows) ActivityRetryOnError(ctx workflow.Context) ([]string, error) {
    65  	ctx = workflow.WithActivityOptions(ctx, w.defaultActivityOptionsWithRetry())
    66  	startTime := workflow.Now(ctx)
    67  	err := workflow.ExecuteActivity(ctx, "Fail").Get(ctx, nil)
    68  	if err == nil {
    69  		return nil, fmt.Errorf("expected activity to fail but succeeded")
    70  	}
    71  
    72  	elapsed := workflow.Now(ctx).Sub(startTime)
    73  	if elapsed < 2*time.Second {
    74  		return nil, fmt.Errorf("expected activity to be retried on failure, but it was not")
    75  	}
    76  
    77  	cerr, ok := err.(*cadence.CustomError)
    78  	if !ok {
    79  		return nil, fmt.Errorf("activity failed with unexpected error: %v", err)
    80  	}
    81  	if cerr.Reason() != errFailOnPurpose.Reason() {
    82  		return nil, fmt.Errorf("activity failed with unexpected error reason: %v", cerr.Reason())
    83  	}
    84  
    85  	return []string{"fail", "fail", "fail"}, nil
    86  }
    87  
    88  func (w *Workflows) ActivityRetryOptionsChange(ctx workflow.Context) ([]string, error) {
    89  	opts := w.defaultActivityOptionsWithRetry()
    90  	opts.RetryPolicy.MaximumAttempts = 2
    91  	if workflow.IsReplaying(ctx) {
    92  		opts.RetryPolicy.MaximumAttempts = 3
    93  	}
    94  	ctx = workflow.WithActivityOptions(ctx, opts)
    95  	err := workflow.ExecuteActivity(ctx, "Fail").Get(ctx, nil)
    96  	if err == nil {
    97  		return nil, fmt.Errorf("expected activity to fail but succeeded")
    98  	}
    99  	return []string{"fail", "fail"}, nil
   100  }
   101  
   102  func (w *Workflows) ActivityRetryOnTimeout(ctx workflow.Context, timeoutType shared.TimeoutType) ([]string, error) {
   103  	opts := w.defaultActivityOptionsWithRetry()
   104  	switch timeoutType {
   105  	case shared.TimeoutTypeScheduleToClose:
   106  		opts.ScheduleToCloseTimeout = time.Second
   107  	case shared.TimeoutTypeStartToClose:
   108  		opts.StartToCloseTimeout = time.Second
   109  	}
   110  
   111  	ctx = workflow.WithActivityOptions(ctx, opts)
   112  
   113  	startTime := workflow.Now(ctx)
   114  	err := workflow.ExecuteActivity(ctx, "Activities_Sleep", 2*time.Second).Get(ctx, nil)
   115  	if err == nil {
   116  		return nil, fmt.Errorf("expected activity to fail but succeeded")
   117  	}
   118  
   119  	elapsed := workflow.Now(ctx).Sub(startTime)
   120  	if elapsed < 5*time.Second {
   121  		return nil, fmt.Errorf("expected activity to be retried on failure, but it was not: %v", elapsed)
   122  	}
   123  
   124  	terr, ok := err.(*workflow.TimeoutError)
   125  	if !ok {
   126  		return nil, fmt.Errorf("activity failed with unexpected error: %v", err)
   127  	}
   128  
   129  	if terr.TimeoutType() != timeoutType {
   130  		return nil, fmt.Errorf("activity failed due to unexpected timeout %v", terr.TimeoutType())
   131  	}
   132  
   133  	return []string{"sleep", "sleep", "sleep"}, nil
   134  }
   135  
   136  func (w *Workflows) ActivityRetryOnHBTimeout(ctx workflow.Context) ([]string, error) {
   137  	opts := w.defaultActivityOptionsWithRetry()
   138  	opts.HeartbeatTimeout = time.Second
   139  	ctx = workflow.WithActivityOptions(ctx, opts)
   140  
   141  	var result int
   142  	startTime := workflow.Now(ctx)
   143  	err := workflow.ExecuteActivity(ctx, "Activities_HeartbeatAndSleep", 0, 2*time.Second).Get(ctx, &result)
   144  	if err == nil {
   145  		return nil, fmt.Errorf("expected activity to fail but succeeded")
   146  	}
   147  
   148  	elapsed := workflow.Now(ctx).Sub(startTime)
   149  	if elapsed < 5*time.Second {
   150  		return nil, fmt.Errorf("expected activity to be retried on failure, but it was not")
   151  	}
   152  
   153  	terr, ok := err.(*workflow.TimeoutError)
   154  	if !ok {
   155  		return nil, fmt.Errorf("activity failed with unexpected error: %v", err)
   156  	}
   157  
   158  	if terr.TimeoutType() != shared.TimeoutTypeHeartbeat {
   159  		return nil, fmt.Errorf("activity failed due to unexpected timeout %v", terr.TimeoutType())
   160  	}
   161  
   162  	if !terr.HasDetails() {
   163  		return nil, fmt.Errorf("timeout missing last heartbeat details")
   164  	}
   165  
   166  	if err := terr.Details(&result); err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	if result != 3 {
   171  		return nil, fmt.Errorf("invalid heartbeat details: %v", result)
   172  	}
   173  
   174  	return []string{"heartbeatAndSleep", "heartbeatAndSleep", "heartbeatAndSleep"}, nil
   175  }
   176  
   177  func (w *Workflows) ActivityAutoHeartbeat(ctx workflow.Context) ([]string, error) {
   178  	opts := workflow.ActivityOptions{
   179  		ScheduleToStartTimeout: time.Second,
   180  		ScheduleToCloseTimeout: 10 * time.Second,
   181  		StartToCloseTimeout:    10 * time.Second,
   182  		HeartbeatTimeout:       2 * time.Second,
   183  	}
   184  	ctx = workflow.WithActivityOptions(ctx, opts)
   185  
   186  	var result int
   187  	err := workflow.ExecuteActivity(ctx, "HeartbeatAndSleep", 0, 5*time.Second).Get(ctx, &result)
   188  	if err != nil {
   189  		return nil, fmt.Errorf("expected activity to succed but failed:%v", err)
   190  	}
   191  	if result != 1 {
   192  		return nil, fmt.Errorf("activity should only be executed once")
   193  	}
   194  
   195  	return []string{"heartbeatAndSleep"}, nil
   196  }
   197  
   198  func (w *Workflows) ContinueAsNew(ctx workflow.Context, count int, taskList string) (int, error) {
   199  	tl := workflow.GetInfo(ctx).TaskListName
   200  	if tl != taskList {
   201  		return -1, fmt.Errorf("invalid taskListName name, expected=%v, got=%v", taskList, tl)
   202  	}
   203  	if count == 0 {
   204  		return 999, nil
   205  	}
   206  	ctx = workflow.WithTaskList(ctx, taskList)
   207  	return -1, workflow.NewContinueAsNewError(ctx, w.ContinueAsNew, count-1, taskList)
   208  }
   209  
   210  func (w *Workflows) ContinueAsNewWithOptions(ctx workflow.Context, count int, taskList string) (string, error) {
   211  	info := workflow.GetInfo(ctx)
   212  	tl := info.TaskListName
   213  	if tl != taskList {
   214  		return "", fmt.Errorf("invalid taskListName name, expected=%v, got=%v", taskList, tl)
   215  	}
   216  
   217  	if info.Memo == nil || info.SearchAttributes == nil {
   218  		return "", errors.New("memo or search attributes are not carried over")
   219  	}
   220  	var memoVal, searchAttrVal string
   221  	err := client.NewValue(info.Memo.Fields["memoKey"]).Get(&memoVal)
   222  	if err != nil {
   223  		return "", errors.New("error when get memo value")
   224  	}
   225  	err = client.NewValue(info.SearchAttributes.IndexedFields["CustomKeywordField"]).Get(&searchAttrVal)
   226  	if err != nil {
   227  		return "", errors.New("error when get search attributes value")
   228  	}
   229  
   230  	if count == 0 {
   231  		return memoVal + "," + searchAttrVal, nil
   232  	}
   233  	ctx = workflow.WithTaskList(ctx, taskList)
   234  
   235  	return "", workflow.NewContinueAsNewError(ctx, w.ContinueAsNewWithOptions, count-1, taskList)
   236  }
   237  
   238  func (w *Workflows) IDReusePolicy(
   239  	ctx workflow.Context,
   240  	childWFID string,
   241  	policy client.WorkflowIDReusePolicy,
   242  	parallel bool,
   243  	failFirstChild bool) (string, error) {
   244  
   245  	ctx = workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
   246  		WorkflowID:                   childWFID,
   247  		ExecutionStartToCloseTimeout: 9 * time.Second,
   248  		TaskStartToCloseTimeout:      5 * time.Second,
   249  		WorkflowIDReusePolicy:        policy,
   250  	})
   251  
   252  	var ans1 string
   253  	child1 := workflow.ExecuteChildWorkflow(ctx, w.child, "hello", failFirstChild)
   254  	if !parallel {
   255  		err := child1.Get(ctx, &ans1)
   256  		if failFirstChild && err == nil {
   257  			return "", fmt.Errorf("child1 succeeded when it was expected to fail")
   258  		}
   259  		if !failFirstChild && err != nil {
   260  			return "", fmt.Errorf("child1 failed when it was expected to succeed")
   261  		}
   262  	}
   263  
   264  	var ans2 string
   265  	if err := workflow.ExecuteChildWorkflow(ctx, w.child, "world", false).Get(ctx, &ans2); err != nil {
   266  		return "", err
   267  	}
   268  
   269  	if parallel {
   270  		err := child1.Get(ctx, &ans1)
   271  		if failFirstChild && err == nil {
   272  			return "", fmt.Errorf("child1 succeeded when it was expected to fail")
   273  		}
   274  		if !failFirstChild && err != nil {
   275  			return "", fmt.Errorf("child1 failed when it was expected to succeed")
   276  		}
   277  	}
   278  
   279  	return ans1 + ans2, nil
   280  }
   281  
   282  func (w *Workflows) ChildWorkflowRetryOnError(ctx workflow.Context) error {
   283  	opts := workflow.ChildWorkflowOptions{
   284  		TaskStartToCloseTimeout:      5 * time.Second,
   285  		ExecutionStartToCloseTimeout: 9 * time.Second,
   286  		RetryPolicy: &cadence.RetryPolicy{
   287  			InitialInterval:    time.Second,
   288  			BackoffCoefficient: 2.0,
   289  			MaximumInterval:    time.Second,
   290  			ExpirationInterval: 100 * time.Second,
   291  			MaximumAttempts:    3,
   292  		},
   293  	}
   294  	ctx = workflow.WithChildOptions(ctx, opts)
   295  	var result string
   296  	return workflow.ExecuteChildWorkflow(ctx, w.child, "hello", true).Get(ctx, &result)
   297  }
   298  
   299  func (w *Workflows) ChildWorkflowRetryOnTimeout(ctx workflow.Context) error {
   300  	opts := workflow.ChildWorkflowOptions{
   301  		TaskStartToCloseTimeout:      time.Second,
   302  		ExecutionStartToCloseTimeout: time.Second,
   303  		RetryPolicy: &cadence.RetryPolicy{
   304  			InitialInterval:    time.Second,
   305  			BackoffCoefficient: 2.0,
   306  			MaximumInterval:    time.Second,
   307  			ExpirationInterval: 100 * time.Second,
   308  			MaximumAttempts:    3,
   309  		},
   310  	}
   311  	ctx = workflow.WithChildOptions(ctx, opts)
   312  	return workflow.ExecuteChildWorkflow(ctx, w.sleep, 2*time.Second).Get(ctx, nil)
   313  }
   314  
   315  func (w *Workflows) ChildWorkflowSuccess(ctx workflow.Context) (result string, err error) {
   316  	opts := workflow.ChildWorkflowOptions{
   317  		TaskStartToCloseTimeout:      5 * time.Second,
   318  		ExecutionStartToCloseTimeout: 10 * time.Second,
   319  		Memo:                         map[string]interface{}{"memoKey": "memoVal"},
   320  		SearchAttributes:             map[string]interface{}{"CustomKeywordField": "searchAttrVal"},
   321  	}
   322  	ctx = workflow.WithChildOptions(ctx, opts)
   323  	err = workflow.ExecuteChildWorkflow(ctx, w.childForMemoAndSearchAttr).Get(ctx, &result)
   324  	return
   325  }
   326  
   327  func (w *Workflows) ChildWorkflowSuccessWithParentClosePolicyTerminate(ctx workflow.Context) (result string, err error) {
   328  	opts := workflow.ChildWorkflowOptions{
   329  		TaskStartToCloseTimeout:      5 * time.Second,
   330  		ExecutionStartToCloseTimeout: 30 * time.Second,
   331  	}
   332  	ctx = workflow.WithChildOptions(ctx, opts)
   333  	ft := workflow.ExecuteChildWorkflow(ctx, w.sleep, 20*time.Second)
   334  	err = workflow.Sleep(ctx, 5*time.Second)
   335  	if err != nil {
   336  		return "", err
   337  	}
   338  	var childWE internal.WorkflowExecution
   339  	err = ft.GetChildWorkflowExecution().Get(ctx, &childWE)
   340  	return childWE.ID, err
   341  }
   342  
   343  func (w *Workflows) ChildWorkflowSuccessWithParentClosePolicyAbandon(ctx workflow.Context) (result string, err error) {
   344  	opts := workflow.ChildWorkflowOptions{
   345  		TaskStartToCloseTimeout:      5 * time.Second,
   346  		ExecutionStartToCloseTimeout: 30 * time.Second,
   347  		ParentClosePolicy:            client.ParentClosePolicyAbandon,
   348  	}
   349  	ctx = workflow.WithChildOptions(ctx, opts)
   350  	ft := workflow.ExecuteChildWorkflow(ctx, w.sleep, 20*time.Second)
   351  	err = workflow.Sleep(ctx, 5*time.Second)
   352  	if err != nil {
   353  		return "", err
   354  	}
   355  	var childWE internal.WorkflowExecution
   356  	err = ft.GetChildWorkflowExecution().Get(ctx, &childWE)
   357  	return childWE.ID, err
   358  }
   359  
   360  func (w *Workflows) ChildWorkflowCancel(ctx workflow.Context) (result string, err error) {
   361  	opts := workflow.ChildWorkflowOptions{
   362  		TaskStartToCloseTimeout:      5 * time.Second,
   363  		ExecutionStartToCloseTimeout: 30 * time.Second,
   364  	}
   365  	childCtx := workflow.WithChildOptions(ctx, opts)
   366  	childCtx, cancel := workflow.WithCancel(childCtx)
   367  	ft := workflow.ExecuteChildWorkflow(childCtx, w.sleep, 20*time.Second)
   368  	err = workflow.Sleep(ctx, time.Second)
   369  	if err != nil {
   370  		return "", err
   371  	}
   372  	cancel()
   373  	err = workflow.Sleep(ctx, time.Second)
   374  	if err != nil {
   375  		return "", err
   376  	}
   377  	var childWE internal.WorkflowExecution
   378  	err = ft.GetChildWorkflowExecution().Get(ctx, &childWE)
   379  	return childWE.ID, err
   380  }
   381  
   382  func (w *Workflows) ActivityCancelRepro(ctx workflow.Context) ([]string, error) {
   383  	ctx, cancelFunc := workflow.WithCancel(ctx)
   384  
   385  	// First go-routine which triggers cancellation on completion of first activity
   386  	workflow.Go(ctx, func(ctx1 workflow.Context) {
   387  		activityCtx := workflow.WithActivityOptions(ctx1, workflow.ActivityOptions{
   388  			ScheduleToStartTimeout: 10 * time.Second,
   389  			ScheduleToCloseTimeout: 10 * time.Second,
   390  			StartToCloseTimeout:    9 * time.Second,
   391  		})
   392  
   393  		activityF := workflow.ExecuteActivity(activityCtx, "Prefix_ToUpperWithDelay", "hello", 1*time.Second)
   394  		var ans string
   395  		err := activityF.Get(activityCtx, &ans)
   396  		if err != nil {
   397  			workflow.GetLogger(activityCtx).Sugar().Infof("Activity Failed: Err: %v", err)
   398  			return
   399  		}
   400  
   401  		// Trigger cancellation of root context
   402  		cancelFunc()
   403  	})
   404  
   405  	// Second go-routine which get blocked on ActivitySchedule and not started
   406  	workflow.Go(ctx, func(ctx1 workflow.Context) {
   407  		activityCtx := workflow.WithActivityOptions(ctx1, workflow.ActivityOptions{
   408  			ScheduleToStartTimeout: 10 * time.Second,
   409  			ScheduleToCloseTimeout: 10 * time.Second,
   410  			StartToCloseTimeout:    1 * time.Second,
   411  			TaskList:               "bad_tl",
   412  		})
   413  
   414  		activityF := workflow.ExecuteActivity(activityCtx, "Prefix_ToUpper", "hello")
   415  		var ans string
   416  		err := activityF.Get(activityCtx, &ans)
   417  		if err != nil {
   418  			workflow.GetLogger(activityCtx).Sugar().Infof("Activity Failed: Err: %v", err)
   419  		}
   420  	})
   421  
   422  	// Third go-routine which get blocked on ActivitySchedule and not started
   423  	workflow.Go(ctx, func(ctx1 workflow.Context) {
   424  		activityCtx := workflow.WithActivityOptions(ctx1, workflow.ActivityOptions{
   425  			ScheduleToStartTimeout: 10 * time.Second,
   426  			ScheduleToCloseTimeout: 10 * time.Second,
   427  			StartToCloseTimeout:    1 * time.Second,
   428  			TaskList:               "bad_tl",
   429  		})
   430  
   431  		activityF := workflow.ExecuteActivity(activityCtx, "Prefix_ToUpper", "hello")
   432  		var ans string
   433  		err := activityF.Get(activityCtx, &ans)
   434  		if err != nil {
   435  			workflow.GetLogger(activityCtx).Sugar().Infof("Activity Failed: Err: %v", err)
   436  		}
   437  	})
   438  
   439  	// Cause the workflow to block on sleep
   440  	workflow.Sleep(ctx, 10*time.Second)
   441  
   442  	return []string{"toUpperWithDelay"}, nil
   443  }
   444  
   445  func (w *Workflows) SimplestWorkflow(ctx workflow.Context) (string, error) {
   446  	return "hello", nil
   447  }
   448  
   449  func (w *Workflows) LargeQueryResultWorkflow(ctx workflow.Context) (string, error) {
   450  	err := workflow.SetQueryHandler(ctx, "large_query", func() ([]byte, error) {
   451  		result := make([]byte, 3000000)
   452  		rand.Read(result)
   453  		return result, nil
   454  	})
   455  
   456  	if err != nil {
   457  		return "", errors.New("failed to register query handler")
   458  	}
   459  
   460  	return "hello", nil
   461  }
   462  
   463  func (w *Workflows) ConsistentQueryWorkflow(ctx workflow.Context, delay time.Duration) error {
   464  	queryResult := "starting-value"
   465  	err := workflow.SetQueryHandler(ctx, "consistent_query", func() (string, error) {
   466  		return queryResult, nil
   467  	})
   468  	if err != nil {
   469  		return errors.New("failed to register query handler")
   470  	}
   471  	ch := workflow.GetSignalChannel(ctx, consistentQuerySignalCh)
   472  	var signalData string
   473  	ch.Receive(ctx, &signalData)
   474  	laCtx := workflow.WithLocalActivityOptions(ctx, workflow.LocalActivityOptions{
   475  		ScheduleToCloseTimeout: 5 * time.Second,
   476  	})
   477  
   478  	workflowInfo := internal.GetWorkflowInfo(laCtx)
   479  	if &workflowInfo.WorkflowType == nil {
   480  		return errors.New("failed to get work flow type")
   481  	}
   482  
   483  	workflow.ExecuteLocalActivity(laCtx, LocalSleep, delay).Get(laCtx, nil)
   484  	queryResult = signalData
   485  	return nil
   486  }
   487  
   488  func (w *Workflows) RetryTimeoutStableErrorWorkflow(ctx workflow.Context) ([]string, error) {
   489  	ao := workflow.ActivityOptions{
   490  		ScheduleToStartTimeout: time.Second * 2,
   491  		StartToCloseTimeout:    time.Second * 6,
   492  		RetryPolicy: &cadence.RetryPolicy{
   493  			InitialInterval:    time.Second,
   494  			BackoffCoefficient: 1.0,
   495  			MaximumInterval:    time.Second,
   496  			ExpirationInterval: time.Second * 5,
   497  		},
   498  	}
   499  	ctx = workflow.WithActivityOptions(ctx, ao)
   500  	// Test calling activity by method pointer
   501  	// As Go allows nil receiver pointers it works fine
   502  	var a *Activities
   503  	err := workflow.ExecuteActivity(ctx, a.RetryTimeoutStableErrorActivity).Get(ctx, nil)
   504  
   505  	cerr, ok := err.(*cadence.CustomError)
   506  	if !ok {
   507  		return []string{}, fmt.Errorf("activity failed with unexpected error: %v", err)
   508  	}
   509  	if cerr.Reason() != errFailOnPurpose.Reason() {
   510  		return []string{}, fmt.Errorf("activity failed with unexpected error reason: %v", cerr.Reason())
   511  	}
   512  	return []string{}, nil
   513  }
   514  
   515  func (w *Workflows) child(ctx workflow.Context, arg string, mustFail bool) (string, error) {
   516  	var result string
   517  	ctx = workflow.WithActivityOptions(ctx, w.defaultActivityOptions())
   518  	err := workflow.ExecuteActivity(ctx, "Prefix_ToUpper", arg).Get(ctx, &result)
   519  	if mustFail {
   520  		return "", fmt.Errorf("failing-on-purpose")
   521  	}
   522  	return result, err
   523  }
   524  
   525  func (w *Workflows) childForMemoAndSearchAttr(ctx workflow.Context) (result string, err error) {
   526  	info := workflow.GetInfo(ctx)
   527  	var memo, searchAttr string
   528  	err = client.NewValue(info.Memo.Fields["memoKey"]).Get(&memo)
   529  	if err != nil {
   530  		return
   531  	}
   532  	err = client.NewValue(info.SearchAttributes.IndexedFields["CustomKeywordField"]).Get(&searchAttr)
   533  	if err != nil {
   534  		return
   535  	}
   536  	ctx = workflow.WithActivityOptions(ctx, w.defaultActivityOptions())
   537  	err = workflow.ExecuteActivity(ctx, "Activities_GetMemoAndSearchAttr", memo, searchAttr).Get(ctx, &result)
   538  	return
   539  }
   540  
   541  func (w *Workflows) sleep(ctx workflow.Context, d time.Duration) error {
   542  	ctx = workflow.WithActivityOptions(ctx, w.defaultActivityOptions())
   543  	return workflow.ExecuteActivity(ctx, "Activities_Sleep", d).Get(ctx, nil)
   544  }
   545  
   546  func (w *Workflows) InspectActivityInfo(ctx workflow.Context) error {
   547  	info := workflow.GetInfo(ctx)
   548  	domain := info.Domain
   549  	wfType := info.WorkflowType.Name
   550  	taskList := info.TaskListName
   551  	ctx = workflow.WithActivityOptions(ctx, w.defaultActivityOptions())
   552  	return workflow.ExecuteActivity(ctx, "inspectActivityInfo", domain, taskList, wfType).Get(ctx, nil)
   553  }
   554  
   555  func (w *Workflows) InspectLocalActivityInfo(ctx workflow.Context) error {
   556  	info := workflow.GetInfo(ctx)
   557  	domain := info.Domain
   558  	wfType := info.WorkflowType.Name
   559  	taskList := info.TaskListName
   560  	ctx = workflow.WithLocalActivityOptions(ctx, w.defaultLocalActivityOptions())
   561  	activities := Activities{}
   562  	return workflow.ExecuteLocalActivity(
   563  		ctx, activities.InspectActivityInfo, domain, taskList, wfType).Get(ctx, nil)
   564  }
   565  
   566  func (w *Workflows) WorkflowWithLocalActivityCtxPropagation(ctx workflow.Context) (string, error) {
   567  	ctx = workflow.WithLocalActivityOptions(ctx, w.defaultLocalActivityOptions())
   568  	ctx = workflow.WithValue(ctx, contextKey(testContextKey), "test-data-in-context")
   569  	activities := Activities{}
   570  	var result string
   571  	err := workflow.ExecuteLocalActivity(ctx, activities.DuplicateStringInContext).Get(ctx, &result)
   572  	if err != nil {
   573  		return "", err
   574  	}
   575  	return result, nil
   576  }
   577  
   578  func (w *Workflows) NonDeterminismSimulatorWorkflow(ctx workflow.Context) ([]string, error) {
   579  	logger := workflow.GetLogger(ctx)
   580  	ctx = workflow.WithActivityOptions(ctx, w.defaultActivityOptions())
   581  
   582  	workflow.SetQueryHandler(ctx, "custom_query", func() (int, error) {
   583  		return w.nonDeterminismSimulatorWorkflowCallCount, nil
   584  	})
   585  
   586  	var res []string
   587  
   588  	// Mimic non-deterministic behavior with alternating calls to activity here.
   589  	// Workflow functon is invoked multiple times at every decision point and each time it will behave differently due to this optional activity call.
   590  	// This will be considered as non-deterministic error
   591  	if w.nonDeterminismSimulatorWorkflowCallCount%2 == 0 {
   592  		var ans string
   593  		if err := workflow.ExecuteActivity(ctx, "Prefix_ToUpper", "hello").Get(ctx, &ans); err != nil {
   594  			return nil, err
   595  		}
   596  		res = append(res, ans)
   597  	}
   598  
   599  	w.nonDeterminismSimulatorWorkflowCallCount++
   600  
   601  	selector := workflow.NewSelector(ctx)
   602  	timer := workflow.NewTimer(ctx, 5*time.Second)
   603  	selector.AddFuture(timer, func(workflow.Future) {
   604  		logger.Info("Timer future is called")
   605  	})
   606  
   607  	logger.Info("Workflow will wait on timer")
   608  	selector.Select(ctx)
   609  	logger.Info("Timer returned. calling another activity")
   610  
   611  	var ans string
   612  	if err := workflow.ExecuteActivity(ctx, "Prefix_ToUpper", "hello").Get(ctx, &ans); err != nil {
   613  		return nil, err
   614  	}
   615  	res = append(res, ans)
   616  
   617  	return res, nil
   618  }
   619  
   620  func (w *Workflows) register(worker worker.Worker) {
   621  	// Kept to verify backward compatibility of workflow registration.
   622  	workflow.RegisterWithOptions(w.Basic, workflow.RegisterOptions{DisableAlreadyRegisteredCheck: true})
   623  	worker.RegisterWorkflow(w.ActivityRetryOnError)
   624  	worker.RegisterWorkflow(w.ActivityRetryOnHBTimeout)
   625  	worker.RegisterWorkflow(w.ActivityAutoHeartbeat)
   626  	worker.RegisterWorkflow(w.ActivityRetryOnTimeout)
   627  	worker.RegisterWorkflow(w.ActivityRetryOptionsChange)
   628  	worker.RegisterWorkflow(w.ContinueAsNew)
   629  	worker.RegisterWorkflow(w.ContinueAsNewWithOptions)
   630  	worker.RegisterWorkflow(w.IDReusePolicy)
   631  	worker.RegisterWorkflow(w.ChildWorkflowRetryOnError)
   632  	worker.RegisterWorkflow(w.ChildWorkflowRetryOnTimeout)
   633  	worker.RegisterWorkflow(w.ChildWorkflowSuccess)
   634  	worker.RegisterWorkflow(w.ChildWorkflowSuccessWithParentClosePolicyTerminate)
   635  	worker.RegisterWorkflow(w.ChildWorkflowSuccessWithParentClosePolicyAbandon)
   636  	worker.RegisterWorkflow(w.ChildWorkflowCancel)
   637  	worker.RegisterWorkflow(w.InspectActivityInfo)
   638  	worker.RegisterWorkflow(w.InspectLocalActivityInfo)
   639  	worker.RegisterWorkflow(w.sleep)
   640  	worker.RegisterWorkflow(w.child)
   641  	worker.RegisterWorkflow(w.childForMemoAndSearchAttr)
   642  	worker.RegisterWorkflow(w.ActivityCancelRepro)
   643  	worker.RegisterWorkflow(w.SimplestWorkflow)
   644  	worker.RegisterWorkflow(w.LargeQueryResultWorkflow)
   645  	worker.RegisterWorkflow(w.RetryTimeoutStableErrorWorkflow)
   646  	worker.RegisterWorkflow(w.ConsistentQueryWorkflow)
   647  	worker.RegisterWorkflow(w.WorkflowWithLocalActivityCtxPropagation)
   648  	worker.RegisterWorkflow(w.NonDeterminismSimulatorWorkflow)
   649  
   650  }
   651  
   652  func (w *Workflows) defaultActivityOptions() workflow.ActivityOptions {
   653  	return workflow.ActivityOptions{
   654  		ScheduleToStartTimeout: 5 * time.Second,
   655  		ScheduleToCloseTimeout: 5 * time.Second,
   656  		StartToCloseTimeout:    9 * time.Second,
   657  	}
   658  }
   659  
   660  func (w *Workflows) defaultLocalActivityOptions() workflow.LocalActivityOptions {
   661  	return workflow.LocalActivityOptions{
   662  		ScheduleToCloseTimeout: 5 * time.Second,
   663  	}
   664  }
   665  
   666  func (w *Workflows) defaultActivityOptionsWithRetry() workflow.ActivityOptions {
   667  	return workflow.ActivityOptions{
   668  		ScheduleToStartTimeout: 5 * time.Second,
   669  		ScheduleToCloseTimeout: 5 * time.Second,
   670  		StartToCloseTimeout:    9 * time.Second,
   671  		RetryPolicy: &cadence.RetryPolicy{
   672  			InitialInterval:    time.Second,
   673  			BackoffCoefficient: 2.0,
   674  			MaximumInterval:    time.Second,
   675  			ExpirationInterval: 100 * time.Second,
   676  			MaximumAttempts:    3,
   677  		},
   678  	}
   679  }