go.uber.org/cadence@v1.2.9/internal/internal_task_handlers_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 internal
    23  
    24  import (
    25  	"context"
    26  	"encoding/json"
    27  	"errors"
    28  	"fmt"
    29  	"reflect"
    30  	"strings"
    31  	"sync"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/golang/mock/gomock"
    36  	"github.com/opentracing/opentracing-go"
    37  	"github.com/pborman/uuid"
    38  	"github.com/stretchr/testify/require"
    39  	"github.com/stretchr/testify/suite"
    40  	"go.uber.org/goleak"
    41  	"go.uber.org/zap"
    42  	"go.uber.org/zap/zapcore"
    43  	"go.uber.org/zap/zaptest"
    44  	"go.uber.org/zap/zaptest/observer"
    45  
    46  	"go.uber.org/cadence/.gen/go/cadence/workflowservicetest"
    47  	s "go.uber.org/cadence/.gen/go/shared"
    48  	"go.uber.org/cadence/internal/common"
    49  )
    50  
    51  const (
    52  	testDomain = "test-domain"
    53  )
    54  
    55  type (
    56  	TaskHandlersTestSuite struct {
    57  		suite.Suite
    58  		logger   *zap.Logger
    59  		service  *workflowservicetest.MockClient
    60  		registry *registry
    61  	}
    62  )
    63  
    64  func registerWorkflows(r *registry) {
    65  	r.RegisterWorkflowWithOptions(
    66  		helloWorldWorkflowFunc,
    67  		RegisterWorkflowOptions{Name: "HelloWorld_Workflow"},
    68  	)
    69  	r.RegisterWorkflowWithOptions(
    70  		helloWorldWorkflowCancelFunc,
    71  		RegisterWorkflowOptions{Name: "HelloWorld_WorkflowCancel"},
    72  	)
    73  	r.RegisterWorkflowWithOptions(
    74  		returnPanicWorkflowFunc,
    75  		RegisterWorkflowOptions{Name: "ReturnPanicWorkflow"},
    76  	)
    77  	r.RegisterWorkflowWithOptions(
    78  		panicWorkflowFunc,
    79  		RegisterWorkflowOptions{Name: "PanicWorkflow"},
    80  	)
    81  	r.RegisterWorkflowWithOptions(
    82  		getWorkflowInfoWorkflowFunc,
    83  		RegisterWorkflowOptions{Name: "GetWorkflowInfoWorkflow"},
    84  	)
    85  	r.RegisterWorkflowWithOptions(
    86  		querySignalWorkflowFunc,
    87  		RegisterWorkflowOptions{Name: "QuerySignalWorkflow"},
    88  	)
    89  	r.RegisterActivityWithOptions(
    90  		greeterActivityFunc,
    91  		RegisterActivityOptions{Name: "Greeter_Activity"},
    92  	)
    93  	r.RegisterWorkflowWithOptions(
    94  		binaryChecksumWorkflowFunc,
    95  		RegisterWorkflowOptions{Name: "BinaryChecksumWorkflow"},
    96  	)
    97  }
    98  
    99  func returnPanicWorkflowFunc(ctx Context, input []byte) error {
   100  	return newPanicError("panicError", "stackTrace")
   101  }
   102  
   103  func panicWorkflowFunc(ctx Context, input []byte) error {
   104  	panic("panicError")
   105  }
   106  
   107  func getWorkflowInfoWorkflowFunc(ctx Context, expectedLastCompletionResult string) (info *WorkflowInfo, err error) {
   108  	result := GetWorkflowInfo(ctx)
   109  	var lastCompletionResult string
   110  	err = getDefaultDataConverter().FromData(result.lastCompletionResult, &lastCompletionResult)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	if lastCompletionResult != expectedLastCompletionResult {
   115  		return nil, errors.New("lastCompletionResult is not " + expectedLastCompletionResult)
   116  	}
   117  	return result, nil
   118  }
   119  
   120  // Test suite.
   121  func (t *TaskHandlersTestSuite) SetupTest() {
   122  	t.logger = zaptest.NewLogger(t.T())
   123  }
   124  
   125  func (t *TaskHandlersTestSuite) SetupSuite() {
   126  	t.logger = zaptest.NewLogger(t.T())
   127  	registerWorkflows(t.registry)
   128  }
   129  
   130  func TestTaskHandlersTestSuite(t *testing.T) {
   131  	suite.Run(t, &TaskHandlersTestSuite{
   132  		registry: newRegistry(),
   133  	})
   134  }
   135  
   136  func createTestEventWorkflowExecutionCompleted(eventID int64, attr *s.WorkflowExecutionCompletedEventAttributes) *s.HistoryEvent {
   137  	return &s.HistoryEvent{EventId: common.Int64Ptr(eventID), EventType: common.EventTypePtr(s.EventTypeWorkflowExecutionCompleted), WorkflowExecutionCompletedEventAttributes: attr}
   138  }
   139  
   140  func createTestEventWorkflowExecutionContinuedAsNew(eventID int64, attr *s.WorkflowExecutionContinuedAsNewEventAttributes) *s.HistoryEvent {
   141  	return &s.HistoryEvent{EventId: common.Int64Ptr(eventID), EventType: common.EventTypePtr(s.EventTypeWorkflowExecutionContinuedAsNew), WorkflowExecutionContinuedAsNewEventAttributes: attr}
   142  }
   143  
   144  func createTestEventWorkflowExecutionStarted(eventID int64, attr *s.WorkflowExecutionStartedEventAttributes) *s.HistoryEvent {
   145  	return &s.HistoryEvent{EventId: common.Int64Ptr(eventID), EventType: common.EventTypePtr(s.EventTypeWorkflowExecutionStarted), WorkflowExecutionStartedEventAttributes: attr}
   146  }
   147  
   148  func createTestEventLocalActivity(eventID int64, attr *s.MarkerRecordedEventAttributes) *s.HistoryEvent {
   149  	return &s.HistoryEvent{
   150  		EventId:                       common.Int64Ptr(eventID),
   151  		EventType:                     common.EventTypePtr(s.EventTypeMarkerRecorded),
   152  		MarkerRecordedEventAttributes: attr}
   153  }
   154  
   155  func createTestEventActivityTaskScheduled(eventID int64, attr *s.ActivityTaskScheduledEventAttributes) *s.HistoryEvent {
   156  	return &s.HistoryEvent{
   157  		EventId:                              common.Int64Ptr(eventID),
   158  		EventType:                            common.EventTypePtr(s.EventTypeActivityTaskScheduled),
   159  		ActivityTaskScheduledEventAttributes: attr}
   160  }
   161  
   162  func createTestEventActivityTaskStarted(eventID int64, attr *s.ActivityTaskStartedEventAttributes) *s.HistoryEvent {
   163  	return &s.HistoryEvent{
   164  		EventId:                            common.Int64Ptr(eventID),
   165  		EventType:                          common.EventTypePtr(s.EventTypeActivityTaskStarted),
   166  		ActivityTaskStartedEventAttributes: attr}
   167  }
   168  
   169  func createTestEventActivityTaskCompleted(eventID int64, attr *s.ActivityTaskCompletedEventAttributes) *s.HistoryEvent {
   170  	return &s.HistoryEvent{
   171  		EventId:                              common.Int64Ptr(eventID),
   172  		EventType:                            common.EventTypePtr(s.EventTypeActivityTaskCompleted),
   173  		ActivityTaskCompletedEventAttributes: attr}
   174  }
   175  
   176  func createTestEventActivityTaskTimedOut(eventID int64, attr *s.ActivityTaskTimedOutEventAttributes) *s.HistoryEvent {
   177  	return &s.HistoryEvent{
   178  		EventId:                             common.Int64Ptr(eventID),
   179  		EventType:                           common.EventTypePtr(s.EventTypeActivityTaskTimedOut),
   180  		ActivityTaskTimedOutEventAttributes: attr}
   181  }
   182  
   183  func createTestEventDecisionTaskScheduled(eventID int64, attr *s.DecisionTaskScheduledEventAttributes) *s.HistoryEvent {
   184  	return &s.HistoryEvent{
   185  		EventId:                              common.Int64Ptr(eventID),
   186  		EventType:                            common.EventTypePtr(s.EventTypeDecisionTaskScheduled),
   187  		DecisionTaskScheduledEventAttributes: attr}
   188  }
   189  
   190  func createTestEventDecisionTaskStarted(eventID int64) *s.HistoryEvent {
   191  	return &s.HistoryEvent{
   192  		EventId:   common.Int64Ptr(eventID),
   193  		EventType: common.EventTypePtr(s.EventTypeDecisionTaskStarted)}
   194  }
   195  
   196  func createTestEventWorkflowExecutionSignaled(eventID int64, signalName string) *s.HistoryEvent {
   197  	return createTestEventWorkflowExecutionSignaledWithPayload(eventID, signalName, nil)
   198  }
   199  
   200  func createTestEventWorkflowExecutionSignaledWithPayload(eventID int64, signalName string, payload []byte) *s.HistoryEvent {
   201  	return &s.HistoryEvent{
   202  		EventId:   common.Int64Ptr(eventID),
   203  		EventType: common.EventTypePtr(s.EventTypeWorkflowExecutionSignaled),
   204  		WorkflowExecutionSignaledEventAttributes: &s.WorkflowExecutionSignaledEventAttributes{
   205  			SignalName: common.StringPtr(signalName),
   206  			Input:      payload,
   207  			Identity:   common.StringPtr("test-identity"),
   208  		},
   209  	}
   210  }
   211  
   212  func createTestEventDecisionTaskCompleted(eventID int64, attr *s.DecisionTaskCompletedEventAttributes) *s.HistoryEvent {
   213  	return &s.HistoryEvent{
   214  		EventId:                              common.Int64Ptr(eventID),
   215  		EventType:                            common.EventTypePtr(s.EventTypeDecisionTaskCompleted),
   216  		DecisionTaskCompletedEventAttributes: attr}
   217  }
   218  
   219  func createTestEventDecisionTaskFailed(eventID int64, attr *s.DecisionTaskFailedEventAttributes) *s.HistoryEvent {
   220  	return &s.HistoryEvent{
   221  		EventId:                           common.Int64Ptr(eventID),
   222  		EventType:                         common.EventTypePtr(s.EventTypeDecisionTaskFailed),
   223  		DecisionTaskFailedEventAttributes: attr}
   224  }
   225  
   226  func createTestEventSignalExternalWorkflowExecutionFailed(eventID int64, attr *s.SignalExternalWorkflowExecutionFailedEventAttributes) *s.HistoryEvent {
   227  	return &s.HistoryEvent{
   228  		EventId:   common.Int64Ptr(eventID),
   229  		EventType: common.EventTypePtr(s.EventTypeSignalExternalWorkflowExecutionFailed),
   230  		SignalExternalWorkflowExecutionFailedEventAttributes: attr,
   231  	}
   232  }
   233  
   234  func createWorkflowTask(
   235  	events []*s.HistoryEvent,
   236  	previousStartEventID int64,
   237  	workflowName string,
   238  ) *s.PollForDecisionTaskResponse {
   239  	return createWorkflowTaskWithQueries(events, previousStartEventID, workflowName, nil)
   240  }
   241  
   242  func createWorkflowTaskWithQueries(
   243  	events []*s.HistoryEvent,
   244  	previousStartEventID int64,
   245  	workflowName string,
   246  	queries map[string]*s.WorkflowQuery,
   247  ) *s.PollForDecisionTaskResponse {
   248  	eventsCopy := make([]*s.HistoryEvent, len(events))
   249  	copy(eventsCopy, events)
   250  	return &s.PollForDecisionTaskResponse{
   251  		PreviousStartedEventId: common.Int64Ptr(previousStartEventID),
   252  		WorkflowType:           workflowTypePtr(WorkflowType{workflowName}),
   253  		History:                &s.History{Events: eventsCopy},
   254  		WorkflowExecution: &s.WorkflowExecution{
   255  			WorkflowId: common.StringPtr("fake-workflow-id"),
   256  			RunId:      common.StringPtr(uuid.New()),
   257  		},
   258  		Queries: queries,
   259  	}
   260  }
   261  
   262  func createQueryTask(
   263  	events []*s.HistoryEvent,
   264  	previousStartEventID int64,
   265  	workflowName string,
   266  	queryType string,
   267  ) *s.PollForDecisionTaskResponse {
   268  	task := createWorkflowTask(events, previousStartEventID, workflowName)
   269  	task.Query = &s.WorkflowQuery{
   270  		QueryType: common.StringPtr(queryType),
   271  	}
   272  	return task
   273  }
   274  
   275  func createTestEventTimerStarted(eventID int64, id int) *s.HistoryEvent {
   276  	timerID := fmt.Sprintf("%v", id)
   277  	attr := &s.TimerStartedEventAttributes{
   278  		TimerId:                      common.StringPtr(timerID),
   279  		StartToFireTimeoutSeconds:    nil,
   280  		DecisionTaskCompletedEventId: nil,
   281  	}
   282  	return &s.HistoryEvent{
   283  		EventId:                     common.Int64Ptr(eventID),
   284  		EventType:                   common.EventTypePtr(s.EventTypeTimerStarted),
   285  		TimerStartedEventAttributes: attr}
   286  }
   287  
   288  func createTestEventTimerFired(eventID int64, id int) *s.HistoryEvent {
   289  	timerID := fmt.Sprintf("%v", id)
   290  	attr := &s.TimerFiredEventAttributes{
   291  		TimerId: common.StringPtr(timerID),
   292  	}
   293  
   294  	return &s.HistoryEvent{
   295  		EventId:                   common.Int64Ptr(eventID),
   296  		EventType:                 common.EventTypePtr(s.EventTypeTimerFired),
   297  		TimerFiredEventAttributes: attr}
   298  }
   299  
   300  func findLogField(entry observer.LoggedEntry, fieldName string) *zapcore.Field {
   301  	for _, field := range entry.Context {
   302  		if field.Key == fieldName {
   303  			return &field
   304  		}
   305  	}
   306  	return nil
   307  }
   308  
   309  var testWorkflowTaskTasklist = "tl1"
   310  
   311  func (t *TaskHandlersTestSuite) testWorkflowTaskWorkflowExecutionStartedHelper(params workerExecutionParameters) {
   312  	testEvents := []*s.HistoryEvent{
   313  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &testWorkflowTaskTasklist}}),
   314  	}
   315  	task := createWorkflowTask(testEvents, 0, "HelloWorld_Workflow")
   316  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   317  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   318  	response := request.(*s.RespondDecisionTaskCompletedRequest)
   319  	t.NoError(err)
   320  	t.NotNil(response)
   321  	t.Equal(1, len(response.Decisions))
   322  	t.Equal(s.DecisionTypeScheduleActivityTask, response.Decisions[0].GetDecisionType())
   323  	t.NotNil(response.Decisions[0].ScheduleActivityTaskDecisionAttributes)
   324  }
   325  
   326  func (t *TaskHandlersTestSuite) TestWorkflowTask_WorkflowExecutionStarted() {
   327  	params := workerExecutionParameters{
   328  		TaskList: testWorkflowTaskTasklist,
   329  		WorkerOptions: WorkerOptions{
   330  			Identity: "test-id-1",
   331  			Logger:   t.logger,
   332  		},
   333  	}
   334  	t.testWorkflowTaskWorkflowExecutionStartedHelper(params)
   335  }
   336  
   337  func (t *TaskHandlersTestSuite) TestWorkflowTask_WorkflowExecutionStarted_WithDataConverter() {
   338  	params := workerExecutionParameters{
   339  		TaskList: testWorkflowTaskTasklist,
   340  		WorkerOptions: WorkerOptions{
   341  			Identity:      "test-id-1",
   342  			Logger:        t.logger,
   343  			DataConverter: newTestDataConverter(),
   344  		},
   345  	}
   346  	t.testWorkflowTaskWorkflowExecutionStartedHelper(params)
   347  }
   348  
   349  func (t *TaskHandlersTestSuite) TestWorkflowTask_BinaryChecksum() {
   350  	taskList := "tl1"
   351  	checksum1 := "chck1"
   352  	checksum2 := "chck2"
   353  	testEvents := []*s.HistoryEvent{
   354  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   355  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   356  		createTestEventDecisionTaskStarted(3),
   357  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{
   358  			ScheduledEventId: common.Int64Ptr(2), BinaryChecksum: common.StringPtr(checksum1)}),
   359  		createTestEventTimerStarted(5, 0),
   360  		createTestEventTimerFired(6, 0),
   361  		createTestEventDecisionTaskScheduled(7, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   362  		createTestEventDecisionTaskStarted(8),
   363  		createTestEventDecisionTaskCompleted(9, &s.DecisionTaskCompletedEventAttributes{
   364  			ScheduledEventId: common.Int64Ptr(7), BinaryChecksum: common.StringPtr(checksum2)}),
   365  		createTestEventTimerStarted(10, 1),
   366  		createTestEventTimerFired(11, 1),
   367  		createTestEventDecisionTaskScheduled(12, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   368  		createTestEventDecisionTaskStarted(13),
   369  	}
   370  	task := createWorkflowTask(testEvents, 8, "BinaryChecksumWorkflow")
   371  	params := workerExecutionParameters{
   372  		TaskList: taskList,
   373  		WorkerOptions: WorkerOptions{
   374  			Identity: "test-id-1",
   375  			Logger:   t.logger,
   376  		},
   377  	}
   378  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   379  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   380  	response := request.(*s.RespondDecisionTaskCompletedRequest)
   381  
   382  	t.NoError(err)
   383  	t.NotNil(response)
   384  	t.Equal(1, len(response.Decisions))
   385  	t.Equal(s.DecisionTypeCompleteWorkflowExecution, response.Decisions[0].GetDecisionType())
   386  	checksumsBytes := response.Decisions[0].CompleteWorkflowExecutionDecisionAttributes.Result
   387  	var checksums []string
   388  	json.Unmarshal(checksumsBytes, &checksums)
   389  	t.Equal(3, len(checksums))
   390  	t.Equal("chck1", checksums[0])
   391  	t.Equal("chck2", checksums[1])
   392  	t.Equal(getBinaryChecksum(), checksums[2])
   393  }
   394  
   395  func (t *TaskHandlersTestSuite) TestWorkflowTask_ActivityTaskScheduled() {
   396  	// Schedule an activity and see if we complete workflow.
   397  	taskList := "tl1"
   398  	testEvents := []*s.HistoryEvent{
   399  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   400  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   401  		createTestEventDecisionTaskStarted(3),
   402  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   403  		createTestEventActivityTaskScheduled(5, &s.ActivityTaskScheduledEventAttributes{
   404  			ActivityId:   common.StringPtr("0"),
   405  			ActivityType: &s.ActivityType{Name: common.StringPtr("Greeter_Activity")},
   406  			TaskList:     &s.TaskList{Name: &taskList},
   407  		}),
   408  		createTestEventActivityTaskStarted(6, &s.ActivityTaskStartedEventAttributes{}),
   409  		createTestEventActivityTaskCompleted(7, &s.ActivityTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(5)}),
   410  		createTestEventDecisionTaskStarted(8),
   411  	}
   412  	task := createWorkflowTask(testEvents[0:3], 0, "HelloWorld_Workflow")
   413  	params := workerExecutionParameters{
   414  		TaskList: taskList,
   415  		WorkerOptions: WorkerOptions{
   416  			Identity: "test-id-1",
   417  			Logger:   t.logger,
   418  		},
   419  	}
   420  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   421  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   422  	response := request.(*s.RespondDecisionTaskCompletedRequest)
   423  
   424  	t.NoError(err)
   425  	t.NotNil(response)
   426  	t.Equal(1, len(response.Decisions))
   427  	t.Equal(s.DecisionTypeScheduleActivityTask, response.Decisions[0].GetDecisionType())
   428  	t.NotNil(response.Decisions[0].ScheduleActivityTaskDecisionAttributes)
   429  
   430  	// Schedule an activity and see if we complete workflow, Having only one last decision.
   431  	task = createWorkflowTask(testEvents, 3, "HelloWorld_Workflow")
   432  	request, err = taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   433  	response = request.(*s.RespondDecisionTaskCompletedRequest)
   434  	t.NoError(err)
   435  	t.NotNil(response)
   436  	t.Equal(1, len(response.Decisions))
   437  	t.Equal(s.DecisionTypeCompleteWorkflowExecution, response.Decisions[0].GetDecisionType())
   438  	t.NotNil(response.Decisions[0].CompleteWorkflowExecutionDecisionAttributes)
   439  }
   440  
   441  func (t *TaskHandlersTestSuite) TestWorkflowTask_QueryWorkflow() {
   442  	// Schedule an activity and see if we complete workflow.
   443  	taskList := "sticky-tl"
   444  	execution := &s.WorkflowExecution{
   445  		WorkflowId: common.StringPtr("fake-workflow-id"),
   446  		RunId:      common.StringPtr(uuid.New()),
   447  	}
   448  	testEvents := []*s.HistoryEvent{
   449  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   450  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   451  		createTestEventDecisionTaskStarted(3),
   452  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   453  		createTestEventActivityTaskScheduled(5, &s.ActivityTaskScheduledEventAttributes{
   454  			ActivityId:   common.StringPtr("0"),
   455  			ActivityType: &s.ActivityType{Name: common.StringPtr("Greeter_Activity")},
   456  			TaskList:     &s.TaskList{Name: &taskList},
   457  		}),
   458  		createTestEventActivityTaskStarted(6, &s.ActivityTaskStartedEventAttributes{}),
   459  		createTestEventActivityTaskCompleted(7, &s.ActivityTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(5)}),
   460  	}
   461  	params := workerExecutionParameters{
   462  		TaskList: taskList,
   463  		WorkerOptions: WorkerOptions{
   464  			Identity: "test-id-1",
   465  			Logger:   t.logger,
   466  		},
   467  	}
   468  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   469  
   470  	// first make progress on the workflow
   471  	task := createWorkflowTask(testEvents[0:1], 0, "HelloWorld_Workflow")
   472  	task.StartedEventId = common.Int64Ptr(1)
   473  	task.WorkflowExecution = execution
   474  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   475  	response := request.(*s.RespondDecisionTaskCompletedRequest)
   476  	t.NoError(err)
   477  	t.NotNil(response)
   478  	t.Equal(1, len(response.Decisions))
   479  	t.Equal(s.DecisionTypeScheduleActivityTask, response.Decisions[0].GetDecisionType())
   480  	t.NotNil(response.Decisions[0].ScheduleActivityTaskDecisionAttributes)
   481  
   482  	// then check the current state using query task
   483  	task = createQueryTask([]*s.HistoryEvent{}, 6, "HelloWorld_Workflow", queryType)
   484  	task.WorkflowExecution = execution
   485  	queryResp, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   486  	t.NoError(err)
   487  	t.verifyQueryResult(queryResp, "waiting-activity-result")
   488  }
   489  
   490  func (t *TaskHandlersTestSuite) TestWorkflowTask_QueryWorkflow_2() {
   491  	// Schedule an activity and see if we complete workflow.
   492  
   493  	// This test appears to be just a finer-grained version of TestWorkflowTask_QueryWorkflow, though the older names
   494  	// for them implied entirely different purposes.  Likely it can be combined with TestWorkflowTask_QueryWorkflow
   495  	// without losing anything useful.
   496  	taskList := "tl1"
   497  	testEvents := []*s.HistoryEvent{
   498  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   499  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   500  		createTestEventDecisionTaskStarted(3),
   501  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   502  		createTestEventActivityTaskScheduled(5, &s.ActivityTaskScheduledEventAttributes{
   503  			ActivityId:   common.StringPtr("0"),
   504  			ActivityType: &s.ActivityType{Name: common.StringPtr("Greeter_Activity")},
   505  			TaskList:     &s.TaskList{Name: &taskList},
   506  		}),
   507  		createTestEventActivityTaskStarted(6, &s.ActivityTaskStartedEventAttributes{}),
   508  		createTestEventActivityTaskCompleted(7, &s.ActivityTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(5)}),
   509  		// TODO: below seems irrational.  there's a start without a schedule, and this workflow does not respond to signals.
   510  		// aside from this, the list of tasks is the same as TestWorkflowTask_QueryWorkflow
   511  		createTestEventDecisionTaskStarted(8),
   512  		createTestEventWorkflowExecutionSignaled(9, "test-signal"),
   513  	}
   514  	params := workerExecutionParameters{
   515  		TaskList: taskList,
   516  		WorkerOptions: WorkerOptions{
   517  			Identity: "test-id-1",
   518  			Logger:   t.logger,
   519  		},
   520  	}
   521  
   522  	// TODO: the following comment is not true, previousStartEventID is either 0 or points to a valid decisionTaskStartedID
   523  	// we need to fix the test
   524  	//
   525  	// query after first decision task (notice the previousStartEventID is always the last eventID for query task)
   526  	task := createQueryTask(testEvents[0:3], 3, "HelloWorld_Workflow", queryType)
   527  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   528  	response, _ := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   529  	t.verifyQueryResult(response, "waiting-activity-result")
   530  
   531  	// query after activity task complete but before second decision task started
   532  	task = createQueryTask(testEvents[0:7], 7, "HelloWorld_Workflow", queryType)
   533  	taskHandler = newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   534  	response, _ = taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   535  	t.verifyQueryResult(response, "waiting-activity-result")
   536  
   537  	// query after second decision task
   538  	task = createQueryTask(testEvents[0:8], 8, "HelloWorld_Workflow", queryType)
   539  	taskHandler = newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   540  	response, _ = taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   541  	t.verifyQueryResult(response, "done")
   542  
   543  	// query after second decision task with extra events
   544  	task = createQueryTask(testEvents[0:9], 9, "HelloWorld_Workflow", queryType)
   545  	taskHandler = newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   546  	response, _ = taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   547  	t.verifyQueryResult(response, "done")
   548  
   549  	task = createQueryTask(testEvents[0:9], 9, "HelloWorld_Workflow", "invalid-query-type")
   550  	taskHandler = newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   551  	response, _ = taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   552  	t.NotNil(response)
   553  	queryResp, ok := response.(*s.RespondQueryTaskCompletedRequest)
   554  	t.True(ok)
   555  	t.NotNil(queryResp.ErrorMessage)
   556  	t.Contains(*queryResp.ErrorMessage, "unknown queryType")
   557  }
   558  
   559  func (t *TaskHandlersTestSuite) verifyQueryResult(response interface{}, expectedResult string) {
   560  	t.NotNil(response)
   561  	queryResp, ok := response.(*s.RespondQueryTaskCompletedRequest)
   562  	t.True(ok)
   563  	t.Nil(queryResp.ErrorMessage)
   564  	t.NotNil(queryResp.QueryResult)
   565  	encodedValue := newEncodedValue(queryResp.QueryResult, nil)
   566  	var queryResult string
   567  	err := encodedValue.Get(&queryResult)
   568  	t.NoError(err)
   569  	t.Equal(expectedResult, queryResult)
   570  }
   571  
   572  func (t *TaskHandlersTestSuite) TestCacheEvictionWhenErrorOccurs() {
   573  	taskList := "taskList"
   574  	testEvents := []*s.HistoryEvent{
   575  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   576  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   577  		createTestEventDecisionTaskStarted(3),
   578  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   579  		createTestEventActivityTaskScheduled(5, &s.ActivityTaskScheduledEventAttributes{
   580  			ActivityId:   common.StringPtr("0"),
   581  			ActivityType: &s.ActivityType{Name: common.StringPtr("pkg.Greeter_Activity")},
   582  			TaskList:     &s.TaskList{Name: &taskList},
   583  		}),
   584  	}
   585  	params := workerExecutionParameters{
   586  		TaskList: taskList,
   587  		WorkerOptions: WorkerOptions{
   588  			Identity:                       "test-id-1",
   589  			Logger:                         zap.NewNop(),
   590  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
   591  		},
   592  	}
   593  
   594  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   595  	// now change the history event so it does not match to decision produced via replay
   596  	testEvents[4].ActivityTaskScheduledEventAttributes.ActivityType.Name = common.StringPtr("some-other-activity")
   597  	task := createWorkflowTask(testEvents, 3, "HelloWorld_Workflow")
   598  	// newWorkflowTaskWorkerInternal will set the laTunnel in taskHandler, without it, ProcessWorkflowTask()
   599  	// will fail as it can't find laTunnel in getWorkflowCache().
   600  	newWorkflowTaskWorkerInternal(taskHandler, t.service, testDomain, params, make(chan struct{}), nil)
   601  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   602  
   603  	t.Error(err)
   604  	t.Nil(request)
   605  	t.Contains(err.Error(), "nondeterministic")
   606  
   607  	// There should be nothing in the cache.
   608  	t.EqualValues(getWorkflowCache().Size(), 0)
   609  }
   610  
   611  func (t *TaskHandlersTestSuite) TestWithMissingHistoryEvents() {
   612  	taskList := "taskList"
   613  	testEvents := []*s.HistoryEvent{
   614  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   615  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   616  		createTestEventDecisionTaskStarted(3),
   617  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   618  		createTestEventDecisionTaskScheduled(6, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   619  		createTestEventDecisionTaskStarted(7),
   620  	}
   621  	params := workerExecutionParameters{
   622  		TaskList: taskList,
   623  		WorkerOptions: WorkerOptions{
   624  			Identity:                       "test-id-1",
   625  			Logger:                         zap.NewNop(),
   626  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
   627  		},
   628  	}
   629  
   630  	for _, startEventID := range []int64{0, 3} {
   631  		taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   632  		task := createWorkflowTask(testEvents, startEventID, "HelloWorld_Workflow")
   633  		// newWorkflowTaskWorkerInternal will set the laTunnel in taskHandler, without it, ProcessWorkflowTask()
   634  		// will fail as it can't find laTunnel in getWorkflowCache().
   635  		newWorkflowTaskWorkerInternal(taskHandler, t.service, testDomain, params, make(chan struct{}), nil)
   636  		request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   637  
   638  		t.Error(err)
   639  		t.Nil(request)
   640  		t.Contains(err.Error(), "missing history events")
   641  
   642  		// There should be nothing in the cache.
   643  		t.EqualValues(getWorkflowCache().Size(), 0)
   644  	}
   645  }
   646  
   647  func (t *TaskHandlersTestSuite) TestWithTruncatedHistory() {
   648  	taskList := "taskList"
   649  	testEvents := []*s.HistoryEvent{
   650  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   651  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   652  		createTestEventDecisionTaskStarted(3),
   653  		createTestEventDecisionTaskFailed(4, &s.DecisionTaskFailedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   654  		createTestEventDecisionTaskScheduled(5, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   655  		createTestEventDecisionTaskStarted(6),
   656  		createTestEventDecisionTaskCompleted(7, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(5)}),
   657  		createTestEventActivityTaskScheduled(8, &s.ActivityTaskScheduledEventAttributes{
   658  			ActivityId:   common.StringPtr("0"),
   659  			ActivityType: &s.ActivityType{Name: common.StringPtr("pkg.Greeter_Activity")},
   660  			TaskList:     &s.TaskList{Name: &taskList},
   661  		}),
   662  	}
   663  	params := workerExecutionParameters{
   664  		TaskList: taskList,
   665  		WorkerOptions: WorkerOptions{
   666  			Identity:                       "test-id-1",
   667  			Logger:                         zap.NewNop(),
   668  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
   669  		},
   670  	}
   671  
   672  	testCases := []struct {
   673  		startedEventID         int64
   674  		previousStartedEventID int64
   675  		isResultErr            bool
   676  	}{
   677  		{0, 6, false},
   678  		{10, 0, true},
   679  		{10, 6, true},
   680  	}
   681  
   682  	for i, tc := range testCases {
   683  		taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   684  		task := createWorkflowTask(testEvents, tc.previousStartedEventID, "HelloWorld_Workflow")
   685  		task.StartedEventId = common.Int64Ptr(tc.startedEventID)
   686  		// newWorkflowTaskWorkerInternal will set the laTunnel in taskHandler, without it, ProcessWorkflowTask()
   687  		// will fail as it can't find laTunnel in getWorkflowCache().
   688  		newWorkflowTaskWorkerInternal(taskHandler, t.service, testDomain, params, make(chan struct{}), nil)
   689  		request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   690  
   691  		if tc.isResultErr {
   692  			t.Error(err, "testcase %v failed", i)
   693  			t.Nil(request)
   694  			t.Contains(err.Error(), "premature end of stream")
   695  			t.EqualValues(getWorkflowCache().Size(), 1)
   696  			continue
   697  		}
   698  
   699  		t.NoError(err, "testcase %v failed", i)
   700  	}
   701  }
   702  
   703  func (t *TaskHandlersTestSuite) TestSideEffectDefer_Sticky() {
   704  	t.testSideEffectDeferHelper(false)
   705  }
   706  
   707  func (t *TaskHandlersTestSuite) TestSideEffectDefer_NonSticky() {
   708  	t.testSideEffectDeferHelper(true)
   709  }
   710  
   711  func (t *TaskHandlersTestSuite) testSideEffectDeferHelper(disableSticky bool) {
   712  	value := "should not be modified"
   713  	expectedValue := value
   714  	doneCh := make(chan struct{})
   715  
   716  	workflowFunc := func(ctx Context) error {
   717  		defer func() {
   718  			if !IsReplaying(ctx) {
   719  				// This is an side effect op
   720  				value = ""
   721  			}
   722  			close(doneCh)
   723  		}()
   724  		Sleep(ctx, 1*time.Second)
   725  		return nil
   726  	}
   727  	workflowName := fmt.Sprintf("SideEffectDeferWorkflow-Sticky=%v", disableSticky)
   728  	t.registry.RegisterWorkflowWithOptions(
   729  		workflowFunc,
   730  		RegisterWorkflowOptions{Name: workflowName},
   731  	)
   732  
   733  	taskList := "taskList"
   734  	testEvents := []*s.HistoryEvent{
   735  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   736  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   737  		createTestEventDecisionTaskStarted(3),
   738  	}
   739  
   740  	params := workerExecutionParameters{
   741  		TaskList: taskList,
   742  		WorkerOptions: WorkerOptions{
   743  			Identity:               "test-id-1",
   744  			Logger:                 zap.NewNop(),
   745  			DisableStickyExecution: disableSticky,
   746  		},
   747  	}
   748  
   749  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   750  	task := createWorkflowTask(testEvents, 0, workflowName)
   751  	_, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   752  	t.Nil(err)
   753  
   754  	if !params.DisableStickyExecution {
   755  		// 1. We can't set cache size in the test to 1, otherwise other tests will break.
   756  		// 2. We need to make sure cache is empty when the test is completed,
   757  		// So manually trigger a delete.
   758  		getWorkflowCache().Delete(task.WorkflowExecution.GetRunId())
   759  	}
   760  	// Make sure the workflow coroutine has exited.
   761  	<-doneCh
   762  	// The side effect op should not be executed.
   763  	t.Equal(expectedValue, value)
   764  
   765  	// There should be nothing in the cache.
   766  	t.EqualValues(0, getWorkflowCache().Size())
   767  }
   768  
   769  func (t *TaskHandlersTestSuite) TestWorkflowTask_NondeterministicDetection() {
   770  	taskList := "taskList"
   771  	testEvents := []*s.HistoryEvent{
   772  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   773  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   774  		createTestEventDecisionTaskStarted(3),
   775  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   776  		createTestEventActivityTaskScheduled(5, &s.ActivityTaskScheduledEventAttributes{
   777  			ActivityId:   common.StringPtr("0"),
   778  			ActivityType: &s.ActivityType{Name: common.StringPtr("pkg.Greeter_Activity")},
   779  			TaskList:     &s.TaskList{Name: &taskList},
   780  		}),
   781  	}
   782  	task := createWorkflowTask(testEvents, 3, "HelloWorld_Workflow")
   783  	stopC := make(chan struct{})
   784  	params := workerExecutionParameters{
   785  		TaskList: taskList,
   786  		WorkerOptions: WorkerOptions{
   787  			Identity:                       "test-id-1",
   788  			Logger:                         zap.NewNop(),
   789  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
   790  		},
   791  		WorkerStopChannel: stopC,
   792  	}
   793  
   794  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   795  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   796  	response := request.(*s.RespondDecisionTaskCompletedRequest)
   797  	// there should be no error as the history events matched the decisions.
   798  	t.NoError(err)
   799  	t.NotNil(response)
   800  
   801  	// now change the history event so it does not match to decision produced via replay
   802  	testEvents[4].ActivityTaskScheduledEventAttributes.ActivityType.Name = common.StringPtr("some-other-activity")
   803  	task = createWorkflowTask(testEvents, 3, "HelloWorld_Workflow")
   804  	// newWorkflowTaskWorkerInternal will set the laTunnel in taskHandler, without it, ProcessWorkflowTask()
   805  	// will fail as it can't find laTunnel in getWorkflowCache().
   806  	newWorkflowTaskWorkerInternal(taskHandler, t.service, testDomain, params, stopC, nil)
   807  	request, err = taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   808  	t.Error(err)
   809  	t.Nil(request)
   810  	t.Contains(err.Error(), "nondeterministic")
   811  
   812  	// now, create a new task handler with fail nondeterministic workflow policy
   813  	// and verify that it handles the mismatching history correctly.
   814  	params.NonDeterministicWorkflowPolicy = NonDeterministicWorkflowPolicyFailWorkflow
   815  	failOnNondeterminismTaskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   816  	task = createWorkflowTask(testEvents, 3, "HelloWorld_Workflow")
   817  	request, err = failOnNondeterminismTaskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   818  	// When FailWorkflow policy is set, task handler does not return an error,
   819  	// because it will indicate non determinism in the request.
   820  	t.NoError(err)
   821  	// Verify that request is a RespondDecisionTaskCompleteRequest
   822  	response, ok := request.(*s.RespondDecisionTaskCompletedRequest)
   823  	t.True(ok)
   824  	// Verify there's at least 1 decision
   825  	// and the last last decision is to fail workflow
   826  	// and contains proper justification.(i.e. nondeterminism).
   827  	t.True(len(response.Decisions) > 0)
   828  	closeDecision := response.Decisions[len(response.Decisions)-1]
   829  	t.Equal(*closeDecision.DecisionType, s.DecisionTypeFailWorkflowExecution)
   830  	t.Contains(*closeDecision.FailWorkflowExecutionDecisionAttributes.Reason, "NonDeterministicWorkflowPolicyFailWorkflow")
   831  
   832  	// now with different package name to activity type
   833  	testEvents[4].ActivityTaskScheduledEventAttributes.ActivityType.Name = common.StringPtr("new-package.Greeter_Activity")
   834  	task = createWorkflowTask(testEvents, 3, "HelloWorld_Workflow")
   835  	request, err = taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   836  	t.NoError(err)
   837  	t.NotNil(request)
   838  }
   839  
   840  func (t *TaskHandlersTestSuite) TestWorkflowTask_NondeterministicLogNonexistingID() {
   841  	taskList := "taskList"
   842  	testEvents := []*s.HistoryEvent{
   843  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   844  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   845  		createTestEventDecisionTaskStarted(3),
   846  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
   847  		createTestEventActivityTaskScheduled(5, &s.ActivityTaskScheduledEventAttributes{
   848  			// Insert an ID which does not exist
   849  			ActivityId:   common.StringPtr("NotAnActivityID"),
   850  			ActivityType: &s.ActivityType{Name: common.StringPtr("pkg.Greeter_Activity")},
   851  			TaskList:     &s.TaskList{Name: &taskList},
   852  		}),
   853  	}
   854  
   855  	obs, logs := observer.New(zap.ErrorLevel)
   856  	logger := zap.New(obs)
   857  
   858  	task := createWorkflowTask(testEvents, 3, "HelloWorld_Workflow")
   859  	stopC := make(chan struct{})
   860  	params := workerExecutionParameters{
   861  		TaskList: taskList,
   862  		WorkerOptions: WorkerOptions{
   863  			Identity: "test-id-1",
   864  			Logger:   logger,
   865  		},
   866  		WorkerStopChannel: stopC,
   867  	}
   868  
   869  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   870  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   871  
   872  	t.Nil(request)
   873  	t.ErrorContains(err, "nondeterministic workflow")
   874  
   875  	// Check that the error was logged
   876  	illegalPanicLogs := logs.FilterMessage("Illegal state caused panic")
   877  	require.Len(t.T(), illegalPanicLogs.All(), 1)
   878  
   879  	replayErrorField := findLogField(illegalPanicLogs.All()[0], "ReplayError")
   880  	require.NotNil(t.T(), replayErrorField)
   881  	require.Equal(t.T(), zapcore.ErrorType, replayErrorField.Type)
   882  	require.ErrorContains(t.T(), replayErrorField.Interface.(error),
   883  		"nondeterministic workflow: "+
   884  			"history event is ActivityTaskScheduled: (ActivityId:NotAnActivityID, ActivityType:(Name:pkg.Greeter_Activity), TaskList:(Name:taskList), Input:[]), "+
   885  			"replay decision is ScheduleActivityTask: (ActivityId:0, ActivityType:(Name:Greeter_Activity), TaskList:(Name:taskList)")
   886  }
   887  
   888  func (t *TaskHandlersTestSuite) TestWorkflowTask_WorkflowReturnsPanicError() {
   889  	taskList := "taskList"
   890  	testEvents := []*s.HistoryEvent{
   891  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   892  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   893  		createTestEventDecisionTaskStarted(3),
   894  	}
   895  	task := createWorkflowTask(testEvents, 3, "ReturnPanicWorkflow")
   896  	params := workerExecutionParameters{
   897  		TaskList: taskList,
   898  		WorkerOptions: WorkerOptions{
   899  			Identity:                       "test-id-1",
   900  			Logger:                         zap.NewNop(),
   901  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
   902  		},
   903  	}
   904  
   905  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   906  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   907  	t.NoError(err)
   908  	t.NotNil(request)
   909  	r, ok := request.(*s.RespondDecisionTaskCompletedRequest)
   910  	t.True(ok)
   911  	t.EqualValues(s.DecisionTypeFailWorkflowExecution, r.Decisions[0].GetDecisionType())
   912  	attr := r.Decisions[0].FailWorkflowExecutionDecisionAttributes
   913  	t.EqualValues("cadenceInternal:Panic", attr.GetReason())
   914  	details := string(attr.Details)
   915  	t.True(strings.HasPrefix(details, "\"panicError"), details)
   916  }
   917  
   918  func (t *TaskHandlersTestSuite) TestWorkflowTask_WorkflowPanics() {
   919  	taskList := "taskList"
   920  	testEvents := []*s.HistoryEvent{
   921  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   922  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   923  		createTestEventDecisionTaskStarted(3),
   924  	}
   925  
   926  	obs, logs := observer.New(zap.ErrorLevel)
   927  	logger := zap.New(obs)
   928  
   929  	task := createWorkflowTask(testEvents, 3, "PanicWorkflow")
   930  	params := workerExecutionParameters{
   931  		TaskList: taskList,
   932  		WorkerOptions: WorkerOptions{
   933  			Identity:                       "test-id-1",
   934  			Logger:                         logger,
   935  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
   936  		},
   937  	}
   938  
   939  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
   940  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
   941  	t.NoError(err)
   942  	t.NotNil(request)
   943  	r, ok := request.(*s.RespondDecisionTaskFailedRequest)
   944  	t.True(ok)
   945  	t.EqualValues("WORKFLOW_WORKER_UNHANDLED_FAILURE", r.Cause.String())
   946  	t.EqualValues("panicError", string(r.Details))
   947  
   948  	// Check that the error was logged
   949  	panicLogs := logs.FilterMessage("Workflow panic.")
   950  	require.Len(t.T(), panicLogs.All(), 1)
   951  
   952  	wfTypeField := findLogField(panicLogs.All()[0], tagWorkflowType)
   953  	require.NotNil(t.T(), wfTypeField)
   954  	require.Equal(t.T(), "PanicWorkflow", wfTypeField.String)
   955  }
   956  
   957  func (t *TaskHandlersTestSuite) TestGetWorkflowInfo() {
   958  	taskList := "taskList"
   959  	parentID := "parentID"
   960  	parentRunID := "parentRun"
   961  	cronSchedule := "5 4 * * *"
   962  	continuedRunID := uuid.New()
   963  	parentExecution := &s.WorkflowExecution{
   964  		WorkflowId: &parentID,
   965  		RunId:      &parentRunID,
   966  	}
   967  	parentDomain := "parentDomain"
   968  	var attempt int32 = 123
   969  	var executionTimeout int32 = 213456
   970  	var taskTimeout int32 = 21
   971  	workflowType := "GetWorkflowInfoWorkflow"
   972  	lastCompletionResult, err := getDefaultDataConverter().ToData("lastCompletionData")
   973  	t.NoError(err)
   974  	retryPolicy := &s.RetryPolicy{
   975  		InitialIntervalInSeconds: common.Int32Ptr(1),
   976  		BackoffCoefficient:       common.Float64Ptr(1.0),
   977  		MaximumIntervalInSeconds: common.Int32Ptr(1),
   978  		MaximumAttempts:          common.Int32Ptr(3),
   979  	}
   980  	startedEventAttributes := &s.WorkflowExecutionStartedEventAttributes{
   981  		Input:                               lastCompletionResult,
   982  		TaskList:                            &s.TaskList{Name: &taskList},
   983  		ParentWorkflowExecution:             parentExecution,
   984  		CronSchedule:                        &cronSchedule,
   985  		ContinuedExecutionRunId:             &continuedRunID,
   986  		ParentWorkflowDomain:                &parentDomain,
   987  		Attempt:                             &attempt,
   988  		ExecutionStartToCloseTimeoutSeconds: &executionTimeout,
   989  		TaskStartToCloseTimeoutSeconds:      &taskTimeout,
   990  		LastCompletionResult:                lastCompletionResult,
   991  		RetryPolicy:                         retryPolicy,
   992  	}
   993  	testEvents := []*s.HistoryEvent{
   994  		createTestEventWorkflowExecutionStarted(1, startedEventAttributes),
   995  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   996  		createTestEventDecisionTaskStarted(3),
   997  	}
   998  	task := createWorkflowTask(testEvents, 3, workflowType)
   999  	params := workerExecutionParameters{
  1000  		TaskList: taskList,
  1001  		WorkerOptions: WorkerOptions{
  1002  			Identity:                       "test-id-1",
  1003  			Logger:                         zap.NewNop(),
  1004  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
  1005  		},
  1006  	}
  1007  
  1008  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
  1009  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
  1010  	t.NoError(err)
  1011  	t.NotNil(request)
  1012  	r, ok := request.(*s.RespondDecisionTaskCompletedRequest)
  1013  	t.True(ok)
  1014  	t.EqualValues(s.DecisionTypeCompleteWorkflowExecution, r.Decisions[0].GetDecisionType())
  1015  	attr := r.Decisions[0].CompleteWorkflowExecutionDecisionAttributes
  1016  	var result WorkflowInfo
  1017  	t.NoError(getDefaultDataConverter().FromData(attr.Result, &result))
  1018  	t.EqualValues(taskList, result.TaskListName)
  1019  	t.EqualValues(parentID, result.ParentWorkflowExecution.ID)
  1020  	t.EqualValues(parentRunID, result.ParentWorkflowExecution.RunID)
  1021  	t.EqualValues(cronSchedule, *result.CronSchedule)
  1022  	t.EqualValues(continuedRunID, *result.ContinuedExecutionRunID)
  1023  	t.EqualValues(parentDomain, *result.ParentWorkflowDomain)
  1024  	t.EqualValues(attempt, result.Attempt)
  1025  	t.EqualValues(executionTimeout, result.ExecutionStartToCloseTimeoutSeconds)
  1026  	t.EqualValues(taskTimeout, result.TaskStartToCloseTimeoutSeconds)
  1027  	t.EqualValues(workflowType, result.WorkflowType.Name)
  1028  	t.EqualValues(testDomain, result.Domain)
  1029  	t.EqualValues(retryPolicy, result.RetryPolicy)
  1030  }
  1031  
  1032  func (t *TaskHandlersTestSuite) TestConsistentQuery_InvalidQueryTask() {
  1033  	taskList := "taskList"
  1034  	params := workerExecutionParameters{
  1035  		TaskList: taskList,
  1036  		WorkerOptions: WorkerOptions{
  1037  			Identity:                       "test-id-1",
  1038  			Logger:                         zap.NewNop(),
  1039  			NonDeterministicWorkflowPolicy: NonDeterministicWorkflowPolicyBlockWorkflow,
  1040  		},
  1041  	}
  1042  
  1043  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
  1044  	task := createWorkflowTask(nil, 3, "HelloWorld_Workflow")
  1045  	task.Query = &s.WorkflowQuery{}
  1046  	task.Queries = map[string]*s.WorkflowQuery{"query_id": {}}
  1047  	newWorkflowTaskWorkerInternal(taskHandler, t.service, testDomain, params, make(chan struct{}), nil)
  1048  	// query and queries are both specified so this is an invalid task
  1049  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
  1050  
  1051  	t.Error(err)
  1052  	t.Nil(request)
  1053  	t.Contains(err.Error(), "invalid query decision task")
  1054  
  1055  	// There should be nothing in the cache.
  1056  	t.EqualValues(getWorkflowCache().Size(), 0)
  1057  }
  1058  
  1059  func (t *TaskHandlersTestSuite) TestConsistentQuery_Success() {
  1060  	taskList := "tl1"
  1061  	checksum1 := "chck1"
  1062  	numberOfSignalsToComplete, err := getDefaultDataConverter().ToData(2)
  1063  	t.NoError(err)
  1064  	signal, err := getDefaultDataConverter().ToData("signal data")
  1065  	t.NoError(err)
  1066  	testEvents := []*s.HistoryEvent{
  1067  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{
  1068  			TaskList: &s.TaskList{Name: &taskList},
  1069  			Input:    numberOfSignalsToComplete,
  1070  		}),
  1071  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{}),
  1072  		createTestEventDecisionTaskStarted(3),
  1073  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{
  1074  			ScheduledEventId: common.Int64Ptr(2), BinaryChecksum: common.StringPtr(checksum1)}),
  1075  		createTestEventWorkflowExecutionSignaledWithPayload(5, signalCh, signal),
  1076  		createTestEventDecisionTaskScheduled(6, &s.DecisionTaskScheduledEventAttributes{}),
  1077  		createTestEventDecisionTaskStarted(7),
  1078  	}
  1079  
  1080  	queries := map[string]*s.WorkflowQuery{
  1081  		"id1": {QueryType: common.StringPtr(queryType)},
  1082  		"id2": {QueryType: common.StringPtr(errQueryType)},
  1083  	}
  1084  	task := createWorkflowTaskWithQueries(testEvents[0:3], 0, "QuerySignalWorkflow", queries)
  1085  
  1086  	params := workerExecutionParameters{
  1087  		TaskList: taskList,
  1088  		WorkerOptions: WorkerOptions{
  1089  			Identity: "test-id-1",
  1090  			Logger:   t.logger,
  1091  		},
  1092  	}
  1093  
  1094  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
  1095  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
  1096  	response := request.(*s.RespondDecisionTaskCompletedRequest)
  1097  	t.NoError(err)
  1098  	t.NotNil(response)
  1099  	t.Len(response.Decisions, 0)
  1100  	expectedQueryResults := map[string]*s.WorkflowQueryResult{
  1101  		"id1": {
  1102  			ResultType: common.QueryResultTypePtr(s.QueryResultTypeAnswered),
  1103  			Answer:     []byte(fmt.Sprintf("\"%v\"\n", startingQueryValue)),
  1104  		},
  1105  		"id2": {
  1106  			ResultType:   common.QueryResultTypePtr(s.QueryResultTypeFailed),
  1107  			ErrorMessage: common.StringPtr(queryErr),
  1108  		},
  1109  	}
  1110  	t.assertQueryResultsEqual(expectedQueryResults, response.QueryResults)
  1111  
  1112  	secondTask := createWorkflowTaskWithQueries(testEvents, 3, "QuerySignalWorkflow", queries)
  1113  	secondTask.WorkflowExecution.RunId = task.WorkflowExecution.RunId
  1114  	request, err = taskHandler.ProcessWorkflowTask(&workflowTask{task: secondTask}, nil)
  1115  	response = request.(*s.RespondDecisionTaskCompletedRequest)
  1116  	t.NoError(err)
  1117  	t.NotNil(response)
  1118  	t.Len(response.Decisions, 1)
  1119  	expectedQueryResults = map[string]*s.WorkflowQueryResult{
  1120  		"id1": {
  1121  			ResultType: common.QueryResultTypePtr(s.QueryResultTypeAnswered),
  1122  			Answer:     []byte(fmt.Sprintf("\"%v\"\n", "signal data")),
  1123  		},
  1124  		"id2": {
  1125  			ResultType:   common.QueryResultTypePtr(s.QueryResultTypeFailed),
  1126  			ErrorMessage: common.StringPtr(queryErr),
  1127  		},
  1128  	}
  1129  	t.assertQueryResultsEqual(expectedQueryResults, response.QueryResults)
  1130  
  1131  	// clean up workflow left in cache
  1132  	getWorkflowCache().Delete(*task.WorkflowExecution.RunId)
  1133  }
  1134  
  1135  func (t *TaskHandlersTestSuite) assertQueryResultsEqual(expected map[string]*s.WorkflowQueryResult, actual map[string]*s.WorkflowQueryResult) {
  1136  	t.Equal(len(expected), len(actual))
  1137  	for expectedID, expectedResult := range expected {
  1138  		t.Contains(actual, expectedID)
  1139  		t.True(expectedResult.Equals(actual[expectedID]))
  1140  	}
  1141  }
  1142  
  1143  func (t *TaskHandlersTestSuite) TestWorkflowTask_CancelActivityBeforeSent() {
  1144  	// Schedule an activity and see if we complete workflow.
  1145  	taskList := "tl1"
  1146  	testEvents := []*s.HistoryEvent{
  1147  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
  1148  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{}),
  1149  		createTestEventDecisionTaskStarted(3),
  1150  	}
  1151  	task := createWorkflowTask(testEvents, 0, "HelloWorld_WorkflowCancel")
  1152  
  1153  	params := workerExecutionParameters{
  1154  		TaskList: taskList,
  1155  		WorkerOptions: WorkerOptions{
  1156  			Identity: "test-id-1",
  1157  			Logger:   t.logger,
  1158  		},
  1159  	}
  1160  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
  1161  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task}, nil)
  1162  	response := request.(*s.RespondDecisionTaskCompletedRequest)
  1163  	t.NoError(err)
  1164  	t.NotNil(response)
  1165  	t.Equal(1, len(response.Decisions))
  1166  	t.Equal(s.DecisionTypeCompleteWorkflowExecution, response.Decisions[0].GetDecisionType())
  1167  	t.NotNil(response.Decisions[0].CompleteWorkflowExecutionDecisionAttributes)
  1168  }
  1169  
  1170  func (t *TaskHandlersTestSuite) TestWorkflowTask_PageToken() {
  1171  	// Schedule a decision activity and see if we complete workflow.
  1172  	taskList := "tl1"
  1173  	testEvents := []*s.HistoryEvent{
  1174  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
  1175  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{}),
  1176  	}
  1177  	task := createWorkflowTask(testEvents, 0, "HelloWorld_Workflow")
  1178  	task.NextPageToken = []byte("token")
  1179  
  1180  	params := workerExecutionParameters{
  1181  		TaskList: taskList,
  1182  		WorkerOptions: WorkerOptions{
  1183  			Identity: "test-id-1",
  1184  			Logger:   t.logger,
  1185  		},
  1186  	}
  1187  
  1188  	nextEvents := []*s.HistoryEvent{
  1189  		createTestEventDecisionTaskStarted(3),
  1190  	}
  1191  
  1192  	historyIterator := &historyIteratorImpl{
  1193  		iteratorFunc: func(nextToken []byte) (*s.History, []byte, error) {
  1194  			return &s.History{nextEvents}, nil, nil
  1195  		},
  1196  	}
  1197  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
  1198  	request, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: task, historyIterator: historyIterator}, nil)
  1199  	response := request.(*s.RespondDecisionTaskCompletedRequest)
  1200  	t.NoError(err)
  1201  	t.NotNil(response)
  1202  }
  1203  
  1204  func (t *TaskHandlersTestSuite) TestLocalActivityRetry_DecisionHeartbeatFail() {
  1205  	backoffIntervalInSeconds := int32(1)
  1206  	backoffDuration := time.Second * time.Duration(backoffIntervalInSeconds)
  1207  	workflowComplete := false
  1208  
  1209  	retryLocalActivityWorkflowFunc := func(ctx Context, intput []byte) error {
  1210  		ao := LocalActivityOptions{
  1211  			ScheduleToCloseTimeout: time.Minute,
  1212  			RetryPolicy: &RetryPolicy{
  1213  				InitialInterval:    backoffDuration,
  1214  				BackoffCoefficient: 1.1,
  1215  				MaximumInterval:    time.Minute,
  1216  				ExpirationInterval: time.Minute,
  1217  			},
  1218  		}
  1219  		ctx = WithLocalActivityOptions(ctx, ao)
  1220  
  1221  		err := ExecuteLocalActivity(ctx, func() error {
  1222  			return errors.New("some random error")
  1223  		}).Get(ctx, nil)
  1224  		workflowComplete = true
  1225  		return err
  1226  	}
  1227  	t.registry.RegisterWorkflowWithOptions(
  1228  		retryLocalActivityWorkflowFunc,
  1229  		RegisterWorkflowOptions{Name: "RetryLocalActivityWorkflow"},
  1230  	)
  1231  
  1232  	decisionTaskStartedEvent := createTestEventDecisionTaskStarted(3)
  1233  	decisionTaskStartedEvent.Timestamp = common.Int64Ptr(time.Now().UnixNano())
  1234  	testEvents := []*s.HistoryEvent{
  1235  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{
  1236  			// make sure the timeout is same as the backoff interval
  1237  			TaskStartToCloseTimeoutSeconds: common.Int32Ptr(backoffIntervalInSeconds),
  1238  			TaskList:                       &s.TaskList{Name: &testWorkflowTaskTasklist}},
  1239  		),
  1240  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{}),
  1241  		decisionTaskStartedEvent,
  1242  	}
  1243  
  1244  	task := createWorkflowTask(testEvents, 0, "RetryLocalActivityWorkflow")
  1245  	stopCh := make(chan struct{})
  1246  	params := workerExecutionParameters{
  1247  		TaskList: testWorkflowTaskTasklist,
  1248  		WorkerOptions: WorkerOptions{
  1249  			Identity: "test-id-1",
  1250  			Logger:   t.logger,
  1251  			Tracer:   opentracing.NoopTracer{},
  1252  		},
  1253  		WorkerStopChannel: stopCh,
  1254  	}
  1255  	defer close(stopCh)
  1256  
  1257  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
  1258  	laTunnel := newLocalActivityTunnel(params.WorkerStopChannel)
  1259  	taskHandlerImpl, ok := taskHandler.(*workflowTaskHandlerImpl)
  1260  	t.True(ok)
  1261  	taskHandlerImpl.laTunnel = laTunnel
  1262  
  1263  	laTaskPoller := newLocalActivityPoller(params, laTunnel)
  1264  	doneCh := make(chan struct{})
  1265  	go func() {
  1266  		// laTaskPoller needs to poll the local activity and process it
  1267  		task, err := laTaskPoller.PollTask()
  1268  		t.NoError(err)
  1269  		err = laTaskPoller.ProcessTask(task)
  1270  		t.NoError(err)
  1271  
  1272  		// before clearing workflow state, a reset sticky task will be sent to this chan,
  1273  		// drain the chan so that workflow state can be cleared
  1274  		<-laTunnel.resultCh
  1275  
  1276  		close(doneCh)
  1277  	}()
  1278  
  1279  	laResultCh := make(chan *localActivityResult)
  1280  	response, err := taskHandler.ProcessWorkflowTask(
  1281  		&workflowTask{
  1282  			task:       task,
  1283  			laResultCh: laResultCh,
  1284  		},
  1285  		func(response interface{}, startTime time.Time) (*workflowTask, error) {
  1286  			return nil, &s.EntityNotExistsError{Message: "Decision task not found."}
  1287  		})
  1288  	t.Nil(response)
  1289  	t.Error(err)
  1290  
  1291  	// wait for the retry timer to fire
  1292  	time.Sleep(backoffDuration)
  1293  	t.False(workflowComplete)
  1294  	<-doneCh
  1295  }
  1296  
  1297  func (t *TaskHandlersTestSuite) TestHeartBeat_NoError() {
  1298  	mockCtrl := gomock.NewController(t.T())
  1299  	mockService := workflowservicetest.NewMockClient(mockCtrl)
  1300  
  1301  	cancelRequested := false
  1302  	heartbeatResponse := s.RecordActivityTaskHeartbeatResponse{CancelRequested: &cancelRequested}
  1303  	mockService.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), gomock.Any(), callOptions()...).Return(&heartbeatResponse, nil)
  1304  
  1305  	cadenceInvoker := &cadenceInvoker{
  1306  		identity:  "Test_Cadence_Invoker",
  1307  		service:   mockService,
  1308  		taskToken: nil,
  1309  	}
  1310  
  1311  	heartbeatErr := cadenceInvoker.BatchHeartbeat(nil)
  1312  	t.NoError(heartbeatErr)
  1313  }
  1314  
  1315  func newHeartbeatRequestMatcher(details []byte) gomock.Matcher {
  1316  	return &recordHeartbeatRequestMatcher{details: details}
  1317  }
  1318  
  1319  type recordHeartbeatRequestMatcher struct {
  1320  	details []byte
  1321  }
  1322  
  1323  func (r *recordHeartbeatRequestMatcher) String() string {
  1324  	panic("implement me")
  1325  }
  1326  
  1327  func (r *recordHeartbeatRequestMatcher) Matches(x interface{}) bool {
  1328  	req, ok := x.(*s.RecordActivityTaskHeartbeatRequest)
  1329  	if !ok {
  1330  		return false
  1331  	}
  1332  
  1333  	return reflect.DeepEqual(r.details, req.Details)
  1334  }
  1335  
  1336  func (t *TaskHandlersTestSuite) TestHeartBeat_Interleaved() {
  1337  	mockCtrl := gomock.NewController(t.T())
  1338  	mockService := workflowservicetest.NewMockClient(mockCtrl)
  1339  
  1340  	cancelRequested := false
  1341  	heartbeatResponse := s.RecordActivityTaskHeartbeatResponse{CancelRequested: &cancelRequested}
  1342  	mockService.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), newHeartbeatRequestMatcher([]byte("1")), callOptions()...).Return(&heartbeatResponse, nil).Times(3)
  1343  	mockService.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), newHeartbeatRequestMatcher([]byte("2")), callOptions()...).Return(&heartbeatResponse, nil).Times(3)
  1344  
  1345  	cadenceInvoker := &cadenceInvoker{
  1346  		identity:              "Test_Cadence_Invoker",
  1347  		service:               mockService,
  1348  		taskToken:             nil,
  1349  		heartBeatTimeoutInSec: 3,
  1350  	}
  1351  
  1352  	heartbeatErr := cadenceInvoker.BatchHeartbeat([]byte("1"))
  1353  	t.NoError(heartbeatErr)
  1354  	time.Sleep(1000 * time.Millisecond)
  1355  
  1356  	for i := 0; i < 4; i++ {
  1357  		heartbeatErr = cadenceInvoker.BackgroundHeartbeat()
  1358  		t.NoError(heartbeatErr)
  1359  		time.Sleep(800 * time.Millisecond)
  1360  	}
  1361  
  1362  	time.Sleep(time.Second)
  1363  
  1364  	heartbeatErr = cadenceInvoker.BatchHeartbeat([]byte("2"))
  1365  	t.NoError(heartbeatErr)
  1366  	time.Sleep(1000 * time.Millisecond)
  1367  
  1368  	for i := 0; i < 4; i++ {
  1369  		heartbeatErr = cadenceInvoker.BackgroundHeartbeat()
  1370  		t.NoError(heartbeatErr)
  1371  		time.Sleep(800 * time.Millisecond)
  1372  	}
  1373  	time.Sleep(1 * time.Second)
  1374  }
  1375  
  1376  func (t *TaskHandlersTestSuite) TestHeartBeatLogNil() {
  1377  	core, obs := observer.New(zap.ErrorLevel)
  1378  	logger := zap.New(core)
  1379  
  1380  	cadenceInv := &cadenceInvoker{
  1381  		identity: "Test_Cadence_Invoker",
  1382  		logger:   logger,
  1383  	}
  1384  
  1385  	cadenceInv.logFailedHeartBeat(nil)
  1386  
  1387  	t.Empty(obs.All())
  1388  }
  1389  
  1390  func (t *TaskHandlersTestSuite) TestHeartBeatLogCanceledError() {
  1391  	core, obs := observer.New(zap.ErrorLevel)
  1392  	logger := zap.New(core)
  1393  
  1394  	cadenceInv := &cadenceInvoker{
  1395  		identity: "Test_Cadence_Invoker",
  1396  		logger:   logger,
  1397  	}
  1398  
  1399  	var workflowCompleatedErr CanceledError
  1400  	cadenceInv.logFailedHeartBeat(&workflowCompleatedErr)
  1401  
  1402  	t.Empty(obs.All())
  1403  }
  1404  
  1405  func (t *TaskHandlersTestSuite) TestHeartBeatLogNotCanceled() {
  1406  	core, obs := observer.New(zap.ErrorLevel)
  1407  	logger := zap.New(core)
  1408  
  1409  	cadenceInv := &cadenceInvoker{
  1410  		identity: "Test_Cadence_Invoker",
  1411  		logger:   logger,
  1412  	}
  1413  
  1414  	var workflowCompleatedErr s.WorkflowExecutionAlreadyCompletedError
  1415  	cadenceInv.logFailedHeartBeat(&workflowCompleatedErr)
  1416  
  1417  	t.Len(obs.All(), 1)
  1418  	t.Equal("Failed to send heartbeat", obs.All()[0].Message)
  1419  }
  1420  
  1421  func (t *TaskHandlersTestSuite) TestHeartBeat_NilResponseWithError() {
  1422  	mockCtrl := gomock.NewController(t.T())
  1423  	mockService := workflowservicetest.NewMockClient(mockCtrl)
  1424  	logger := zaptest.NewLogger(t.T())
  1425  
  1426  	entityNotExistsError := &s.EntityNotExistsError{}
  1427  	mockService.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), gomock.Any(), callOptions()...).Return(nil, entityNotExistsError)
  1428  
  1429  	cadenceInvoker := newServiceInvoker(
  1430  		nil,
  1431  		"Test_Cadence_Invoker",
  1432  		mockService,
  1433  		func() {},
  1434  		0,
  1435  		make(chan struct{}),
  1436  		FeatureFlags{},
  1437  		logger,
  1438  		testWorkflowType,
  1439  		testActivityType,
  1440  	)
  1441  
  1442  	heartbeatErr := cadenceInvoker.BatchHeartbeat(nil)
  1443  	t.NotNil(heartbeatErr)
  1444  	_, ok := (heartbeatErr).(*s.EntityNotExistsError)
  1445  	t.True(ok, "heartbeatErr must be EntityNotExistsError.")
  1446  }
  1447  
  1448  func (t *TaskHandlersTestSuite) TestHeartBeat_NilResponseWithDomainNotActiveError() {
  1449  	mockCtrl := gomock.NewController(t.T())
  1450  	mockService := workflowservicetest.NewMockClient(mockCtrl)
  1451  	logger := zaptest.NewLogger(t.T())
  1452  
  1453  	domainNotActiveError := &s.DomainNotActiveError{}
  1454  	mockService.EXPECT().RecordActivityTaskHeartbeat(gomock.Any(), gomock.Any(), callOptions()...).Return(nil, domainNotActiveError)
  1455  
  1456  	called := false
  1457  	cancelHandler := func() { called = true }
  1458  
  1459  	cadenceInvoker := newServiceInvoker(
  1460  		nil,
  1461  		"Test_Cadence_Invoker",
  1462  		mockService,
  1463  		cancelHandler,
  1464  		0,
  1465  		make(chan struct{}),
  1466  		FeatureFlags{},
  1467  		logger,
  1468  		testWorkflowType,
  1469  		testActivityType,
  1470  	)
  1471  
  1472  	heartbeatErr := cadenceInvoker.BatchHeartbeat(nil)
  1473  	t.NotNil(heartbeatErr)
  1474  	_, ok := (heartbeatErr).(*s.DomainNotActiveError)
  1475  	t.True(ok, "heartbeatErr must be DomainNotActiveError.")
  1476  	t.True(called)
  1477  }
  1478  
  1479  type testActivityDeadline struct {
  1480  	logger *zap.Logger
  1481  	d      time.Duration
  1482  }
  1483  
  1484  func (t *testActivityDeadline) Execute(ctx context.Context, input []byte) ([]byte, error) {
  1485  	if d, _ := ctx.Deadline(); d.IsZero() {
  1486  		panic("invalid deadline provided")
  1487  	}
  1488  	if t.d != 0 {
  1489  		// Wait till deadline expires.
  1490  		<-ctx.Done()
  1491  		return nil, ctx.Err()
  1492  	}
  1493  	return nil, nil
  1494  }
  1495  
  1496  func (t *testActivityDeadline) ActivityType() ActivityType {
  1497  	return ActivityType{Name: "test"}
  1498  }
  1499  
  1500  func (t *testActivityDeadline) GetFunction() interface{} {
  1501  	return t.Execute
  1502  }
  1503  
  1504  func (t *testActivityDeadline) GetOptions() RegisterActivityOptions {
  1505  	return RegisterActivityOptions{}
  1506  }
  1507  
  1508  type deadlineTest struct {
  1509  	actWaitDuration  time.Duration
  1510  	ScheduleTS       time.Time
  1511  	ScheduleDuration int32
  1512  	StartTS          time.Time
  1513  	StartDuration    int32
  1514  	err              error
  1515  }
  1516  
  1517  func (t *TaskHandlersTestSuite) TestActivityExecutionDeadline() {
  1518  	deadlineTests := []deadlineTest{
  1519  		{time.Duration(0), time.Now(), 3, time.Now(), 3, nil},
  1520  		{time.Duration(0), time.Now(), 4, time.Now(), 3, nil},
  1521  		{time.Duration(0), time.Now(), 3, time.Now(), 4, nil},
  1522  		{time.Duration(0), time.Now().Add(-1 * time.Second), 1, time.Now(), 1, context.DeadlineExceeded},
  1523  		{time.Duration(0), time.Now(), 1, time.Now().Add(-1 * time.Second), 1, context.DeadlineExceeded},
  1524  		{time.Duration(0), time.Now().Add(-1 * time.Second), 1, time.Now().Add(-1 * time.Second), 1, context.DeadlineExceeded},
  1525  		{time.Duration(1 * time.Second), time.Now(), 1, time.Now(), 1, context.DeadlineExceeded},
  1526  		{time.Duration(1 * time.Second), time.Now(), 2, time.Now(), 1, context.DeadlineExceeded},
  1527  		{time.Duration(1 * time.Second), time.Now(), 1, time.Now(), 2, context.DeadlineExceeded},
  1528  	}
  1529  	a := &testActivityDeadline{logger: t.logger}
  1530  	registry := t.registry
  1531  	registry.addActivityWithLock(a.ActivityType().Name, a)
  1532  
  1533  	mockCtrl := gomock.NewController(t.T())
  1534  	mockService := workflowservicetest.NewMockClient(mockCtrl)
  1535  
  1536  	for i, d := range deadlineTests {
  1537  		a.d = d.actWaitDuration
  1538  		wep := workerExecutionParameters{
  1539  			WorkerOptions: WorkerOptions{
  1540  				Logger:        t.logger,
  1541  				DataConverter: getDefaultDataConverter(),
  1542  				Tracer:        opentracing.NoopTracer{},
  1543  			},
  1544  		}
  1545  		activityHandler := newActivityTaskHandler(mockService, wep, registry)
  1546  		pats := &s.PollForActivityTaskResponse{
  1547  			TaskToken: []byte("token"),
  1548  			WorkflowExecution: &s.WorkflowExecution{
  1549  				WorkflowId: common.StringPtr("wID"),
  1550  				RunId:      common.StringPtr("rID")},
  1551  			ActivityType:                    &s.ActivityType{Name: common.StringPtr("test")},
  1552  			ActivityId:                      common.StringPtr(uuid.New()),
  1553  			ScheduledTimestamp:              common.Int64Ptr(d.ScheduleTS.UnixNano()),
  1554  			ScheduledTimestampOfThisAttempt: common.Int64Ptr(d.ScheduleTS.UnixNano()),
  1555  			ScheduleToCloseTimeoutSeconds:   common.Int32Ptr(d.ScheduleDuration),
  1556  			StartedTimestamp:                common.Int64Ptr(d.StartTS.UnixNano()),
  1557  			StartToCloseTimeoutSeconds:      common.Int32Ptr(d.StartDuration),
  1558  			WorkflowType: &s.WorkflowType{
  1559  				Name: common.StringPtr("wType"),
  1560  			},
  1561  			WorkflowDomain: common.StringPtr("domain"),
  1562  		}
  1563  		td := fmt.Sprintf("testIndex: %v, testDetails: %v", i, d)
  1564  		r, err := activityHandler.Execute(tasklist, pats)
  1565  		t.logger.Info(fmt.Sprintf("test: %v, result: %v err: %v", td, r, err))
  1566  		t.Equal(d.err, err, td)
  1567  		if err != nil {
  1568  			t.Nil(r, td)
  1569  		}
  1570  	}
  1571  }
  1572  
  1573  func activityWithWorkerStop(ctx context.Context) error {
  1574  	fmt.Println("Executing Activity with worker stop")
  1575  	workerStopCh := GetWorkerStopChannel(ctx)
  1576  
  1577  	select {
  1578  	case <-workerStopCh:
  1579  		return nil
  1580  	case <-time.NewTimer(time.Second * 5).C:
  1581  		return fmt.Errorf("Activity failed to handle worker stop event")
  1582  	}
  1583  }
  1584  
  1585  func (t *TaskHandlersTestSuite) TestActivityExecutionWorkerStop() {
  1586  	a := &testActivityDeadline{logger: t.logger}
  1587  	registry := t.registry
  1588  	registry.RegisterActivityWithOptions(
  1589  		activityWithWorkerStop,
  1590  		RegisterActivityOptions{Name: a.ActivityType().Name, DisableAlreadyRegisteredCheck: true},
  1591  	)
  1592  
  1593  	mockCtrl := gomock.NewController(t.T())
  1594  	mockService := workflowservicetest.NewMockClient(mockCtrl)
  1595  	workerStopCh := make(chan struct{}, 1)
  1596  	ctx, cancel := context.WithCancel(context.Background())
  1597  	wep := workerExecutionParameters{
  1598  		WorkerOptions: WorkerOptions{
  1599  			Logger:        t.logger,
  1600  			DataConverter: getDefaultDataConverter(),
  1601  		},
  1602  		UserContext:       ctx,
  1603  		UserContextCancel: cancel,
  1604  		WorkerStopChannel: workerStopCh,
  1605  	}
  1606  	activityHandler := newActivityTaskHandler(mockService, wep, registry)
  1607  	pats := &s.PollForActivityTaskResponse{
  1608  		TaskToken: []byte("token"),
  1609  		WorkflowExecution: &s.WorkflowExecution{
  1610  			WorkflowId: common.StringPtr("wID"),
  1611  			RunId:      common.StringPtr("rID")},
  1612  		ActivityType:                    &s.ActivityType{Name: common.StringPtr("test")},
  1613  		ActivityId:                      common.StringPtr(uuid.New()),
  1614  		ScheduledTimestamp:              common.Int64Ptr(time.Now().UnixNano()),
  1615  		ScheduledTimestampOfThisAttempt: common.Int64Ptr(time.Now().UnixNano()),
  1616  		ScheduleToCloseTimeoutSeconds:   common.Int32Ptr(1),
  1617  		StartedTimestamp:                common.Int64Ptr(time.Now().UnixNano()),
  1618  		StartToCloseTimeoutSeconds:      common.Int32Ptr(1),
  1619  		WorkflowType: &s.WorkflowType{
  1620  			Name: common.StringPtr("wType"),
  1621  		},
  1622  		WorkflowDomain: common.StringPtr("domain"),
  1623  	}
  1624  	close(workerStopCh)
  1625  	r, err := activityHandler.Execute(tasklist, pats)
  1626  	t.NoError(err)
  1627  	t.NotNil(r)
  1628  }
  1629  
  1630  // a regrettably-hacky func to use goleak to count leaking goroutines.
  1631  // ideally there will be a structured way to do this in the future, rather than string parsing
  1632  func countLeaks(leaks error) int {
  1633  	if leaks == nil {
  1634  		return 0
  1635  	}
  1636  	// leak messages look something like:
  1637  	// Goroutine 23 in state chan receive, with go.uber.org/cadence/internal.(*coroutineState).initialYield on top of the stack:
  1638  	// ... stacktrace ...
  1639  	//
  1640  	// Goroutine 28 ... on top of the stack:
  1641  	// ... stacktrace ...
  1642  	return strings.Count(leaks.Error(), "on top of the stack")
  1643  }
  1644  
  1645  func (t *TaskHandlersTestSuite) TestRegression_QueriesDoNotLeakGoroutines() {
  1646  	// this test must not be run in parallel with most other tests, as it mutates global vars
  1647  	var ridsToCleanUp []string
  1648  	originalLeaks := goleak.Find()
  1649  	defer func(size int) {
  1650  		// empty the cache to clear out any newly-introduced leaks
  1651  		current := getWorkflowCache()
  1652  		for _, rid := range ridsToCleanUp {
  1653  			current.Delete(rid)
  1654  		}
  1655  		// check the cleanup
  1656  		currentLeaks := goleak.Find()
  1657  		if countLeaks(currentLeaks) != countLeaks(originalLeaks) {
  1658  			t.T().Errorf("failed to clean up goroutines.\nOriginal state:\n%v\n\nCurrent state:\n%v", originalLeaks, currentLeaks)
  1659  		}
  1660  
  1661  		// reset everything to make it "normal".
  1662  		// this does NOT restore the original workflow cache - that cannot be done correctly, initCacheOnce is not safe to copy (thus restore).
  1663  		stickyCacheSize = size
  1664  		workflowCache = nil
  1665  		initCacheOnce = sync.Once{}
  1666  	}(stickyCacheSize)
  1667  	workflowCache = nil
  1668  	initCacheOnce = sync.Once{}
  1669  	// cache is intentionally not *disabled*, as that would go down no-cache code paths.
  1670  	// also, there is an LRU-cache bug where the size allows N to enter, but then removes until N-1 remain,
  1671  	// so a size of 2 actually means a size of 1.
  1672  	SetStickyWorkflowCacheSize(2)
  1673  
  1674  	taskList := "tl1"
  1675  	params := workerExecutionParameters{
  1676  		TaskList: taskList,
  1677  		WorkerOptions: WorkerOptions{
  1678  			Identity:               "test-id-1",
  1679  			Logger:                 t.logger,
  1680  			DisableStickyExecution: false,
  1681  		},
  1682  	}
  1683  	taskHandler := newWorkflowTaskHandler(testDomain, params, nil, t.registry)
  1684  
  1685  	// process a throw-away workflow to fill the cache.  this is copied from TestWorkflowTask_QueryWorkflow since it's
  1686  	// relatively simple, but any should work fine, as long as it can be queried.
  1687  	testEvents := []*s.HistoryEvent{
  1688  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
  1689  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
  1690  		createTestEventDecisionTaskStarted(3),
  1691  		createTestEventDecisionTaskCompleted(4, &s.DecisionTaskCompletedEventAttributes{ScheduledEventId: common.Int64Ptr(2)}),
  1692  		createTestEventActivityTaskScheduled(5, &s.ActivityTaskScheduledEventAttributes{
  1693  			ActivityId:   common.StringPtr("0"),
  1694  			ActivityType: &s.ActivityType{Name: common.StringPtr("Greeter_Activity")},
  1695  			TaskList:     &s.TaskList{Name: &taskList},
  1696  		}),
  1697  	}
  1698  	cachedTask := createWorkflowTask(testEvents[0:1], 1, "HelloWorld_Workflow")
  1699  	cachedTask.WorkflowExecution.WorkflowId = common.StringPtr("cache-filling workflow id")
  1700  	ridsToCleanUp = append(ridsToCleanUp, *cachedTask.WorkflowExecution.RunId)
  1701  	_, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: cachedTask}, nil)
  1702  
  1703  	// sanity check that the cache was indeed filled, and that it has created a goroutine
  1704  	require.NoError(t.T(), err, "cache-filling must succeed")
  1705  	require.Equal(t.T(), 1, getWorkflowCache().Size(), "workflow should be cached, but was not")
  1706  	oneCachedLeak := goleak.Find()
  1707  	require.Error(t.T(), oneCachedLeak, "expected at least one leaking goroutine")
  1708  	require.Equal(t.T(), countLeaks(originalLeaks)+1, countLeaks(oneCachedLeak), // ideally == 1, but currently there are other leaks
  1709  		"expected the cached workflow to leak one goroutine.  original leaks:\n%v\n\nleaks after one workflow:\n%v", originalLeaks, oneCachedLeak)
  1710  
  1711  	// now query a different workflow ID / run ID
  1712  	uncachedTask := createQueryTask(testEvents, 5, "HelloWorld_Workflow", queryType)
  1713  	uncachedTask.WorkflowExecution.WorkflowId = common.StringPtr("should not leak this workflow id")
  1714  	ridsToCleanUp = append(ridsToCleanUp, *uncachedTask.WorkflowExecution.RunId) // only necessary if the test fails
  1715  	result, err := taskHandler.ProcessWorkflowTask(&workflowTask{task: uncachedTask}, nil)
  1716  	require.NoError(t.T(), err)
  1717  	t.verifyQueryResult(result, "waiting-activity-result") // largely a sanity check
  1718  
  1719  	// and finally the purpose of this test:
  1720  	// verify that the cache has not been modified, and that there is no new leak
  1721  	t.Equal(1, getWorkflowCache().Size(), "workflow cache should be the same size")
  1722  	t.True(getWorkflowCache().Exist(cachedTask.WorkflowExecution.GetRunId()), "originally-cached workflow should still be cached")
  1723  	t.False(getWorkflowCache().Exist(uncachedTask.WorkflowExecution.GetRunId()), "queried workflow should not be cached")
  1724  	newLeaks := goleak.Find()
  1725  	t.Error(newLeaks, "expected at least one leaking goroutine")
  1726  	t.Equal(countLeaks(oneCachedLeak), countLeaks(newLeaks),
  1727  		"expected the query to leak no new goroutines.  before query:\n%v\n\nafter query:\n%v", oneCachedLeak, newLeaks)
  1728  }
  1729  
  1730  func Test_NonDeterministicCheck(t *testing.T) {
  1731  	decisionTypes := s.DecisionType_Values()
  1732  	require.Equal(t, 13, len(decisionTypes), "If you see this error, you are adding new decision type. "+
  1733  		"Before updating the number to make this test pass, please make sure you update isDecisionMatchEvent() method "+
  1734  		"to check the new decision type. Otherwise the replay will fail on the new decision event.")
  1735  
  1736  	eventTypes := s.EventType_Values()
  1737  	decisionEventTypeCount := 0
  1738  	for _, et := range eventTypes {
  1739  		if isDecisionEvent(et) {
  1740  			decisionEventTypeCount++
  1741  		}
  1742  	}
  1743  	// CancelTimer has 2 corresponding events.
  1744  	require.Equal(t, len(decisionTypes)+1, decisionEventTypeCount, "Every decision type must have one matching event type. "+
  1745  		"If you add new decision type, you need to update isDecisionEvent() method to include that new event type as well.")
  1746  }
  1747  
  1748  func Test_IsDecisionMatchEvent_UpsertWorkflowSearchAttributes(t *testing.T) {
  1749  	diType := s.DecisionTypeUpsertWorkflowSearchAttributes
  1750  	eType := s.EventTypeUpsertWorkflowSearchAttributes
  1751  	strictMode := false
  1752  
  1753  	testCases := []struct {
  1754  		name     string
  1755  		decision *s.Decision
  1756  		event    *s.HistoryEvent
  1757  		expected bool
  1758  	}{
  1759  		{
  1760  			name: "event type not match",
  1761  			decision: &s.Decision{
  1762  				DecisionType: &diType,
  1763  				UpsertWorkflowSearchAttributesDecisionAttributes: &s.UpsertWorkflowSearchAttributesDecisionAttributes{
  1764  					SearchAttributes: &s.SearchAttributes{},
  1765  				},
  1766  			},
  1767  			event:    &s.HistoryEvent{},
  1768  			expected: false,
  1769  		},
  1770  		{
  1771  			name: "attributes not match",
  1772  			decision: &s.Decision{
  1773  				DecisionType: &diType,
  1774  				UpsertWorkflowSearchAttributesDecisionAttributes: &s.UpsertWorkflowSearchAttributesDecisionAttributes{
  1775  					SearchAttributes: &s.SearchAttributes{},
  1776  				},
  1777  			},
  1778  			event: &s.HistoryEvent{
  1779  				EventType: &eType,
  1780  				UpsertWorkflowSearchAttributesEventAttributes: &s.UpsertWorkflowSearchAttributesEventAttributes{},
  1781  			},
  1782  			expected: true,
  1783  		},
  1784  		{
  1785  			name: "attributes match",
  1786  			decision: &s.Decision{
  1787  				DecisionType: &diType,
  1788  				UpsertWorkflowSearchAttributesDecisionAttributes: &s.UpsertWorkflowSearchAttributesDecisionAttributes{
  1789  					SearchAttributes: &s.SearchAttributes{},
  1790  				},
  1791  			},
  1792  			event: &s.HistoryEvent{
  1793  				EventType: &eType,
  1794  				UpsertWorkflowSearchAttributesEventAttributes: &s.UpsertWorkflowSearchAttributesEventAttributes{
  1795  					SearchAttributes: &s.SearchAttributes{},
  1796  				},
  1797  			},
  1798  			expected: true,
  1799  		},
  1800  	}
  1801  
  1802  	for _, testCase := range testCases {
  1803  		t.Run(testCase.name, func(t *testing.T) {
  1804  			require.Equal(t, testCase.expected, isDecisionMatchEvent(testCase.decision, testCase.event, strictMode))
  1805  		})
  1806  	}
  1807  
  1808  	strictMode = true
  1809  
  1810  	testCases = []struct {
  1811  		name     string
  1812  		decision *s.Decision
  1813  		event    *s.HistoryEvent
  1814  		expected bool
  1815  	}{
  1816  		{
  1817  			name: "attributes not match",
  1818  			decision: &s.Decision{
  1819  				DecisionType: &diType,
  1820  				UpsertWorkflowSearchAttributesDecisionAttributes: &s.UpsertWorkflowSearchAttributesDecisionAttributes{
  1821  					SearchAttributes: &s.SearchAttributes{},
  1822  				},
  1823  			},
  1824  			event: &s.HistoryEvent{
  1825  				EventType: &eType,
  1826  				UpsertWorkflowSearchAttributesEventAttributes: &s.UpsertWorkflowSearchAttributesEventAttributes{},
  1827  			},
  1828  			expected: false,
  1829  		},
  1830  	}
  1831  
  1832  	for _, testCase := range testCases {
  1833  		t.Run(testCase.name, func(t *testing.T) {
  1834  			require.Equal(t, testCase.expected, isDecisionMatchEvent(testCase.decision, testCase.event, strictMode))
  1835  		})
  1836  	}
  1837  }
  1838  
  1839  func Test_IsSearchAttributesMatched(t *testing.T) {
  1840  	testCases := []struct {
  1841  		name     string
  1842  		lhs      *s.SearchAttributes
  1843  		rhs      *s.SearchAttributes
  1844  		expected bool
  1845  	}{
  1846  		{
  1847  			name:     "both nil",
  1848  			lhs:      nil,
  1849  			rhs:      nil,
  1850  			expected: true,
  1851  		},
  1852  		{
  1853  			name:     "left nil",
  1854  			lhs:      nil,
  1855  			rhs:      &s.SearchAttributes{},
  1856  			expected: false,
  1857  		},
  1858  		{
  1859  			name:     "right nil",
  1860  			lhs:      &s.SearchAttributes{},
  1861  			rhs:      nil,
  1862  			expected: false,
  1863  		},
  1864  		{
  1865  			name: "not match",
  1866  			lhs: &s.SearchAttributes{
  1867  				IndexedFields: map[string][]byte{
  1868  					"key1": []byte("1"),
  1869  					"key2": []byte("abc"),
  1870  				},
  1871  			},
  1872  			rhs:      &s.SearchAttributes{},
  1873  			expected: false,
  1874  		},
  1875  		{
  1876  			name: "match",
  1877  			lhs: &s.SearchAttributes{
  1878  				IndexedFields: map[string][]byte{
  1879  					"key1": []byte("1"),
  1880  					"key2": []byte("abc"),
  1881  				},
  1882  			},
  1883  			rhs: &s.SearchAttributes{
  1884  				IndexedFields: map[string][]byte{
  1885  					"key2": []byte("abc"),
  1886  					"key1": []byte("1"),
  1887  				},
  1888  			},
  1889  			expected: true,
  1890  		},
  1891  	}
  1892  
  1893  	for _, testCase := range testCases {
  1894  		t.Run(testCase.name, func(t *testing.T) {
  1895  			require.Equal(t, testCase.expected, isSearchAttributesMatched(testCase.lhs, testCase.rhs))
  1896  		})
  1897  	}
  1898  }