go.uber.org/cadence@v1.2.9/internal/internal_workflow_client_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  package internal
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"errors"
    26  	"fmt"
    27  	"log"
    28  	"os"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/golang/mock/gomock"
    33  	"github.com/pborman/uuid"
    34  	"github.com/stretchr/testify/suite"
    35  	"go.uber.org/yarpc"
    36  
    37  	"go.uber.org/cadence/.gen/go/cadence/workflowservicetest"
    38  	"go.uber.org/cadence/.gen/go/shared"
    39  	"go.uber.org/cadence/internal/common"
    40  	"go.uber.org/cadence/internal/common/metrics"
    41  	"go.uber.org/cadence/internal/common/serializer"
    42  )
    43  
    44  const (
    45  	domain                = "some random domain"
    46  	workflowID            = "some random workflow ID"
    47  	workflowType          = "some random workflow type"
    48  	runID                 = "some random run ID"
    49  	tasklist              = "some random tasklist"
    50  	identity              = "some random identity"
    51  	timeoutInSeconds      = 20
    52  	workflowIDReusePolicy = WorkflowIDReusePolicyAllowDuplicateFailedOnly
    53  	testHeader            = "test-header"
    54  )
    55  
    56  // historyEventIteratorSuite
    57  
    58  type (
    59  	historyEventIteratorSuite struct {
    60  		suite.Suite
    61  		mockCtrl              *gomock.Controller
    62  		workflowServiceClient *workflowservicetest.MockClient
    63  		wfClient              *workflowClient
    64  	}
    65  )
    66  
    67  // stringMapPropagator propagates the list of keys across a workflow,
    68  // interpreting the payloads as strings.
    69  type stringMapPropagator struct {
    70  	keys map[string]struct{}
    71  }
    72  
    73  // NewStringMapPropagator returns a context propagator that propagates a set of
    74  // string key-value pairs across a workflow
    75  func NewStringMapPropagator(keys []string) ContextPropagator {
    76  	keyMap := make(map[string]struct{}, len(keys))
    77  	for _, key := range keys {
    78  		keyMap[key] = struct{}{}
    79  	}
    80  	return &stringMapPropagator{keyMap}
    81  }
    82  
    83  // Inject injects values from context into headers for propagation
    84  func (s *stringMapPropagator) Inject(ctx context.Context, writer HeaderWriter) error {
    85  	for key := range s.keys {
    86  		value, ok := ctx.Value(contextKey(key)).(string)
    87  		if !ok {
    88  			return fmt.Errorf("unable to extract key from context %v", key)
    89  		}
    90  		writer.Set(key, []byte(value))
    91  	}
    92  	return nil
    93  }
    94  
    95  // InjectFromWorkflow injects values from context into headers for propagation
    96  func (s *stringMapPropagator) InjectFromWorkflow(ctx Context, writer HeaderWriter) error {
    97  	for key := range s.keys {
    98  		value, ok := ctx.Value(contextKey(key)).(string)
    99  		if !ok {
   100  			return fmt.Errorf("unable to extract key from context %v", key)
   101  		}
   102  		writer.Set(key, []byte(value))
   103  	}
   104  	return nil
   105  }
   106  
   107  // Extract extracts values from headers and puts them into context
   108  func (s *stringMapPropagator) Extract(ctx context.Context, reader HeaderReader) (context.Context, error) {
   109  	if err := reader.ForEachKey(func(key string, value []byte) error {
   110  		if _, ok := s.keys[key]; ok {
   111  			ctx = context.WithValue(ctx, contextKey(key), string(value))
   112  		}
   113  		return nil
   114  	}); err != nil {
   115  		return nil, err
   116  	}
   117  	return ctx, nil
   118  }
   119  
   120  // ExtractToWorkflow extracts values from headers and puts them into context
   121  func (s *stringMapPropagator) ExtractToWorkflow(ctx Context, reader HeaderReader) (Context, error) {
   122  	if err := reader.ForEachKey(func(key string, value []byte) error {
   123  		if _, ok := s.keys[key]; ok {
   124  			ctx = WithValue(ctx, contextKey(key), string(value))
   125  		}
   126  		return nil
   127  	}); err != nil {
   128  		return nil, err
   129  	}
   130  	return ctx, nil
   131  }
   132  
   133  func TestHistoryEventIteratorSuite(t *testing.T) {
   134  	s := new(historyEventIteratorSuite)
   135  	suite.Run(t, s)
   136  }
   137  
   138  func (s *historyEventIteratorSuite) SetupSuite() {
   139  	if testing.Verbose() {
   140  		log.SetOutput(os.Stdout)
   141  	}
   142  }
   143  
   144  func (s *historyEventIteratorSuite) SetupTest() {
   145  	// Create service endpoint
   146  	s.mockCtrl = gomock.NewController(s.T())
   147  	s.workflowServiceClient = workflowservicetest.NewMockClient(s.mockCtrl)
   148  
   149  	s.wfClient = &workflowClient{
   150  		workflowService: s.workflowServiceClient,
   151  		domain:          domain,
   152  	}
   153  }
   154  
   155  func (s *historyEventIteratorSuite) TearDownTest() {
   156  	s.mockCtrl.Finish() // assert mock’s expectations
   157  }
   158  
   159  func (s *historyEventIteratorSuite) TestIterator_NoError() {
   160  	filterType := shared.HistoryEventFilterTypeAllEvent
   161  	request1 := getGetWorkflowExecutionHistoryRequest(filterType)
   162  	response1 := &shared.GetWorkflowExecutionHistoryResponse{
   163  		History: &shared.History{
   164  			Events: []*shared.HistoryEvent{
   165  				// dummy history event
   166  				&shared.HistoryEvent{},
   167  			},
   168  		},
   169  		NextPageToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
   170  	}
   171  	request2 := getGetWorkflowExecutionHistoryRequest(filterType)
   172  	request2.NextPageToken = response1.NextPageToken
   173  	response2 := &shared.GetWorkflowExecutionHistoryResponse{
   174  		History: &shared.History{
   175  			Events: []*shared.HistoryEvent{
   176  				// dummy history event
   177  				&shared.HistoryEvent{},
   178  			},
   179  		},
   180  		NextPageToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
   181  	}
   182  
   183  	dummyEvent := []*shared.HistoryEvent{
   184  		// dummy history event
   185  		&shared.HistoryEvent{},
   186  	}
   187  
   188  	blobData := serializeEvents(dummyEvent)
   189  	request3 := getGetWorkflowExecutionHistoryRequest(filterType)
   190  	request3.NextPageToken = response2.NextPageToken
   191  	response3 := &shared.GetWorkflowExecutionHistoryResponse{
   192  		RawHistory: []*shared.DataBlob{
   193  			// dummy history event
   194  			blobData,
   195  		},
   196  		NextPageToken: nil,
   197  	}
   198  
   199  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request1, gomock.Any()).Return(response1, nil).Times(1)
   200  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request2, gomock.Any()).Return(response2, nil).Times(1)
   201  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request3, gomock.Any()).Return(response3, nil).Times(1)
   202  
   203  	events := []*shared.HistoryEvent{}
   204  	iter := s.wfClient.GetWorkflowHistory(context.Background(), workflowID, runID, true, shared.HistoryEventFilterTypeAllEvent)
   205  	for iter.HasNext() {
   206  		event, err := iter.Next()
   207  		s.Nil(err)
   208  		events = append(events, event)
   209  	}
   210  	s.Equal(3, len(events))
   211  }
   212  
   213  func (s *historyEventIteratorSuite) TestIterator_NoError_EmptyPage() {
   214  	filterType := shared.HistoryEventFilterTypeAllEvent
   215  	request1 := getGetWorkflowExecutionHistoryRequest(filterType)
   216  	response1 := &shared.GetWorkflowExecutionHistoryResponse{
   217  		History: &shared.History{
   218  			Events: []*shared.HistoryEvent{},
   219  		},
   220  		NextPageToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
   221  	}
   222  	request2 := getGetWorkflowExecutionHistoryRequest(filterType)
   223  	request2.NextPageToken = response1.NextPageToken
   224  	response2 := &shared.GetWorkflowExecutionHistoryResponse{
   225  		History: &shared.History{
   226  			Events: []*shared.HistoryEvent{
   227  				// dummy history event
   228  				&shared.HistoryEvent{},
   229  			},
   230  		},
   231  		NextPageToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
   232  	}
   233  
   234  	dummyEvent := []*shared.HistoryEvent{
   235  		// dummy history event
   236  		&shared.HistoryEvent{},
   237  	}
   238  
   239  	blobData := serializeEvents(dummyEvent)
   240  	request3 := getGetWorkflowExecutionHistoryRequest(filterType)
   241  	request3.NextPageToken = response2.NextPageToken
   242  	response3 := &shared.GetWorkflowExecutionHistoryResponse{
   243  		RawHistory: []*shared.DataBlob{
   244  			// dummy history event
   245  			blobData,
   246  		},
   247  		NextPageToken: nil,
   248  	}
   249  
   250  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request1, gomock.Any()).Return(response1, nil).Times(1)
   251  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request2, gomock.Any()).Return(response2, nil).Times(1)
   252  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request3, gomock.Any()).Return(response3, nil).Times(1)
   253  
   254  	events := []*shared.HistoryEvent{}
   255  	iter := s.wfClient.GetWorkflowHistory(context.Background(), workflowID, runID, true, shared.HistoryEventFilterTypeAllEvent)
   256  	for iter.HasNext() {
   257  		event, err := iter.Next()
   258  		s.Nil(err)
   259  		events = append(events, event)
   260  	}
   261  	s.Equal(2, len(events))
   262  }
   263  
   264  func (s *historyEventIteratorSuite) TestIterator_Error() {
   265  	filterType := shared.HistoryEventFilterTypeAllEvent
   266  	request1 := getGetWorkflowExecutionHistoryRequest(filterType)
   267  	response1 := &shared.GetWorkflowExecutionHistoryResponse{
   268  		History: &shared.History{
   269  			Events: []*shared.HistoryEvent{
   270  				// dummy history event
   271  				&shared.HistoryEvent{},
   272  			},
   273  		},
   274  		NextPageToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
   275  	}
   276  	request2 := getGetWorkflowExecutionHistoryRequest(filterType)
   277  	request2.NextPageToken = response1.NextPageToken
   278  
   279  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request1, gomock.Any()).Return(response1, nil).Times(1)
   280  
   281  	iter := s.wfClient.GetWorkflowHistory(context.Background(), workflowID, runID, true, shared.HistoryEventFilterTypeAllEvent)
   282  
   283  	s.True(iter.HasNext())
   284  	event, err := iter.Next()
   285  	s.NotNil(event)
   286  	s.Nil(err)
   287  
   288  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), request2, gomock.Any()).Return(nil, &shared.EntityNotExistsError{}).Times(1)
   289  
   290  	s.True(iter.HasNext())
   291  	event, err = iter.Next()
   292  	s.Nil(event)
   293  	s.NotNil(err)
   294  }
   295  
   296  func (s *historyEventIteratorSuite) TestIterator_StopsTryingNearTimeout() {
   297  	// ensuring "when GetWorkflow().Get(...) times out while waiting", we return a timed-out error of some kind,
   298  	// and stop sending requests rather than trying again and getting some other kind of error.
   299  	// historically this led to a "bad request", insufficient time left for long poll, which was confusing and noisy.
   300  
   301  	filterType := shared.HistoryEventFilterTypeCloseEvent
   302  	reqNormal := getGetWorkflowExecutionHistoryRequest(filterType)
   303  	reqFinal := getGetWorkflowExecutionHistoryRequest(filterType)
   304  
   305  	// all items filtered out for both requests
   306  	resEmpty := &shared.GetWorkflowExecutionHistoryResponse{
   307  		History:       &shared.History{Events: nil}, // this or RawHistory must be non-nil, but they can be empty
   308  		NextPageToken: []byte{1, 2, 3, 4, 5},
   309  	}
   310  	reqFinal.NextPageToken = resEmpty.NextPageToken
   311  
   312  	s.True(time.Second < (defaultGetHistoryTimeoutInSecs*time.Second), "sanity check: default timeout must be longer than how long we extend the timeout for tests")
   313  
   314  	// begin with a deadline that is long enough to allow 2 requests
   315  	d := time.Now().Add((defaultGetHistoryTimeoutInSecs * time.Second) + time.Second)
   316  	baseCtx, cancel := context.WithDeadline(context.Background(), d)
   317  	defer cancel()
   318  	ctx := &fakeDeadlineContext{baseCtx, d}
   319  
   320  	// prep the iterator
   321  	iter := s.wfClient.GetWorkflowHistory(ctx, workflowID, runID, true, filterType)
   322  
   323  	// first attempt should occur, and trigger a second request with less than the requested timeout
   324  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), reqNormal, gomock.Any()).DoAndReturn(func(_ context.Context, _ *shared.GetWorkflowExecutionHistoryRequest, _ ...yarpc.CallOption) (*shared.GetWorkflowExecutionHistoryResponse, error) {
   325  		// first request is being sent, modify the context to simulate time passing,
   326  		// and give the second request insufficient time to trigger another poll.
   327  		//
   328  		// without this, you should see an attempt at a third call, as the context is not canceled,
   329  		// and this mock took no time to respond.
   330  		ctx.d = time.Now().Add(time.Second)
   331  		return resEmpty, nil
   332  	}).Times(1)
   333  	// second request should occur, but not another
   334  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), reqFinal, gomock.Any()).Return(resEmpty, nil).Times(1)
   335  
   336  	// trigger both paginated requests as part of the single HasNext call
   337  	s.True(iter.HasNext())
   338  	event, err := iter.Next()
   339  	s.Nil(event, "iterator should not have returned any events")
   340  	s.Error(err, "iterator should have errored")
   341  	// canceled may also be appropriate, but currently this is true
   342  	s.Truef(errors.Is(err, context.DeadlineExceeded), "iterator should have returned a deadline-exceeded error, but returned a: %#v", err)
   343  	s.Contains(err.Error(), "waiting for the workflow to finish", "should be descriptive of what happened")
   344  }
   345  
   346  // minor helper type to allow faking deadlines between calls, as we cannot normally modify a context that way.
   347  type fakeDeadlineContext struct {
   348  	context.Context
   349  
   350  	d time.Time
   351  }
   352  
   353  func (f *fakeDeadlineContext) Deadline() (time.Time, bool) {
   354  	return f.d, true
   355  }
   356  
   357  // workflowRunSuite
   358  
   359  type (
   360  	workflowRunSuite struct {
   361  		suite.Suite
   362  		mockCtrl              *gomock.Controller
   363  		workflowServiceClient *workflowservicetest.MockClient
   364  		workflowClient        Client
   365  	}
   366  )
   367  
   368  func TestWorkflowRunSuite(t *testing.T) {
   369  	s := new(workflowRunSuite)
   370  	suite.Run(t, s)
   371  }
   372  
   373  func (s *workflowRunSuite) SetupSuite() {
   374  	if testing.Verbose() {
   375  		log.SetOutput(os.Stdout)
   376  	}
   377  }
   378  
   379  func (s *workflowRunSuite) TearDownSuite() {
   380  
   381  }
   382  
   383  func (s *workflowRunSuite) SetupTest() {
   384  	// Create service endpoint
   385  	s.mockCtrl = gomock.NewController(s.T())
   386  	s.workflowServiceClient = workflowservicetest.NewMockClient(s.mockCtrl)
   387  
   388  	metricsScope := metrics.NewTaggedScope(nil)
   389  	options := &ClientOptions{
   390  		MetricsScope: metricsScope,
   391  		Identity:     identity,
   392  	}
   393  	s.workflowClient = NewClient(s.workflowServiceClient, domain, options)
   394  }
   395  
   396  func (s *workflowRunSuite) TearDownTest() {
   397  	s.mockCtrl.Finish()
   398  }
   399  
   400  func (s *workflowRunSuite) TestExecuteWorkflow_NoDup_Success() {
   401  	createResponse := &shared.StartWorkflowExecutionResponse{
   402  		RunId: common.StringPtr(runID),
   403  	}
   404  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   405  
   406  	filterType := shared.HistoryEventFilterTypeCloseEvent
   407  	eventType := shared.EventTypeWorkflowExecutionCompleted
   408  	workflowResult := time.Hour * 59
   409  	encodedResult, _ := encodeArg(getDefaultDataConverter(), workflowResult)
   410  	getRequest := getGetWorkflowExecutionHistoryRequest(filterType)
   411  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   412  		History: &shared.History{
   413  			Events: []*shared.HistoryEvent{
   414  				&shared.HistoryEvent{
   415  					EventType: &eventType,
   416  					WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   417  						Result: encodedResult,
   418  					},
   419  				},
   420  			},
   421  		},
   422  		NextPageToken: nil,
   423  	}
   424  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest, callOptions()...).Return(getResponse, nil).Times(1)
   425  
   426  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   427  		context.Background(),
   428  		StartWorkflowOptions{
   429  			ID:                              workflowID,
   430  			TaskList:                        tasklist,
   431  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   432  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   433  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   434  		}, workflowType,
   435  	)
   436  	s.Nil(err)
   437  	s.Equal(workflowRun.GetID(), workflowID)
   438  	s.Equal(workflowRun.GetRunID(), runID)
   439  	decodedResult := time.Minute
   440  	err = workflowRun.Get(context.Background(), &decodedResult)
   441  	s.Nil(err)
   442  	s.Equal(workflowResult, decodedResult)
   443  }
   444  
   445  func (s *workflowRunSuite) TestExecuteWorkflow_NoDup_RawHistory_Success() {
   446  	createResponse := &shared.StartWorkflowExecutionResponse{
   447  		RunId: common.StringPtr(runID),
   448  	}
   449  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   450  
   451  	filterType := shared.HistoryEventFilterTypeCloseEvent
   452  	eventType := shared.EventTypeWorkflowExecutionCompleted
   453  	workflowResult := time.Hour * 59
   454  	encodedResult, _ := encodeArg(getDefaultDataConverter(), workflowResult)
   455  	events := []*shared.HistoryEvent{
   456  		&shared.HistoryEvent{
   457  			EventType: &eventType,
   458  			WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   459  				Result: encodedResult,
   460  			},
   461  		},
   462  	}
   463  
   464  	blobData := serializeEvents(events)
   465  	getRequest := getGetWorkflowExecutionHistoryRequest(filterType)
   466  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   467  		RawHistory: []*shared.DataBlob{
   468  			blobData,
   469  		},
   470  		NextPageToken: nil,
   471  	}
   472  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest, callOptions()...).Return(getResponse, nil).Times(1)
   473  
   474  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   475  		context.Background(),
   476  		StartWorkflowOptions{
   477  			ID:                              workflowID,
   478  			TaskList:                        tasklist,
   479  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   480  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   481  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   482  		}, workflowType,
   483  	)
   484  	s.Nil(err)
   485  	s.Equal(workflowRun.GetID(), workflowID)
   486  	s.Equal(workflowRun.GetRunID(), runID)
   487  	decodedResult := time.Minute
   488  	err = workflowRun.Get(context.Background(), &decodedResult)
   489  	s.Nil(err)
   490  	s.Equal(workflowResult, decodedResult)
   491  }
   492  
   493  func (s *workflowRunSuite) TestExecuteWorkflowWorkflowExecutionAlreadyStartedError() {
   494  	alreadyStartedErr := &shared.WorkflowExecutionAlreadyStartedError{
   495  		RunId:          common.StringPtr(runID),
   496  		Message:        common.StringPtr("Already Started"),
   497  		StartRequestId: common.StringPtr(uuid.NewRandom().String()),
   498  	}
   499  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).
   500  		Return(nil, alreadyStartedErr).Times(1)
   501  
   502  	eventType := shared.EventTypeWorkflowExecutionCompleted
   503  	workflowResult := time.Hour * 59
   504  	encodedResult, _ := encodeArg(nil, workflowResult)
   505  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   506  		History: &shared.History{
   507  			Events: []*shared.HistoryEvent{
   508  				{
   509  					EventType: &eventType,
   510  					WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   511  						Result: encodedResult,
   512  					},
   513  				},
   514  			},
   515  		},
   516  		NextPageToken: nil,
   517  	}
   518  	getHistory := s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).
   519  		Return(getResponse, nil).Times(1)
   520  	getHistory.Do(func(ctx interface{}, getRequest *shared.GetWorkflowExecutionHistoryRequest, opt1 interface{}, opt2 interface{}, opt3 interface{}, opt4 interface{}) {
   521  		workflowID := getRequest.Execution.WorkflowId
   522  		s.NotNil(workflowID)
   523  		s.NotEmpty(*workflowID)
   524  	})
   525  
   526  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   527  		context.Background(),
   528  		StartWorkflowOptions{
   529  			ID:                              workflowID,
   530  			TaskList:                        tasklist,
   531  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   532  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   533  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   534  		}, workflowType,
   535  	)
   536  	s.Nil(err)
   537  	s.Equal(workflowRun.GetID(), workflowID)
   538  	s.Equal(workflowRun.GetRunID(), runID)
   539  	decodedResult := time.Minute
   540  	err = workflowRun.Get(context.Background(), &decodedResult)
   541  	s.Nil(err)
   542  	s.Equal(workflowResult, decodedResult)
   543  }
   544  
   545  func (s *workflowRunSuite) TestExecuteWorkflowWorkflowExecutionAlreadyStartedError_RawHistory() {
   546  	alreadyStartedErr := &shared.WorkflowExecutionAlreadyStartedError{
   547  		RunId:          common.StringPtr(runID),
   548  		Message:        common.StringPtr("Already Started"),
   549  		StartRequestId: common.StringPtr(uuid.NewRandom().String()),
   550  	}
   551  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).
   552  		Return(nil, alreadyStartedErr).Times(1)
   553  
   554  	eventType := shared.EventTypeWorkflowExecutionCompleted
   555  	workflowResult := time.Hour * 59
   556  	encodedResult, _ := encodeArg(nil, workflowResult)
   557  	events := []*shared.HistoryEvent{
   558  		{
   559  			EventType: &eventType,
   560  			WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   561  				Result: encodedResult,
   562  			},
   563  		},
   564  	}
   565  
   566  	blobData := serializeEvents(events)
   567  
   568  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   569  		RawHistory: []*shared.DataBlob{
   570  			blobData,
   571  		},
   572  		NextPageToken: nil,
   573  	}
   574  	getHistory := s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).
   575  		Return(getResponse, nil).Times(1)
   576  	getHistory.Do(func(ctx interface{}, getRequest *shared.GetWorkflowExecutionHistoryRequest, opt1 interface{}, opt2 interface{}, opt3 interface{}, opt4 interface{}) {
   577  		workflowID := getRequest.Execution.WorkflowId
   578  		s.NotNil(workflowID)
   579  		s.NotEmpty(*workflowID)
   580  	})
   581  
   582  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   583  		context.Background(),
   584  		StartWorkflowOptions{
   585  			ID:                              workflowID,
   586  			TaskList:                        tasklist,
   587  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   588  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   589  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   590  		}, workflowType,
   591  	)
   592  	s.Nil(err)
   593  	s.Equal(workflowRun.GetID(), workflowID)
   594  	s.Equal(workflowRun.GetRunID(), runID)
   595  	decodedResult := time.Minute
   596  	err = workflowRun.Get(context.Background(), &decodedResult)
   597  	s.Nil(err)
   598  	s.Equal(workflowResult, decodedResult)
   599  }
   600  
   601  // Test for the bug in ExecuteWorkflow.
   602  // When Options.ID was empty then GetWorkflowExecutionHistory was called with an empty WorkflowID.
   603  func (s *workflowRunSuite) TestExecuteWorkflow_NoIdInOptions() {
   604  	createResponse := &shared.StartWorkflowExecutionResponse{
   605  		RunId: common.StringPtr(runID),
   606  	}
   607  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   608  
   609  	eventType := shared.EventTypeWorkflowExecutionCompleted
   610  	workflowResult := time.Hour * 59
   611  	encodedResult, _ := encodeArg(nil, workflowResult)
   612  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   613  		History: &shared.History{
   614  			Events: []*shared.HistoryEvent{
   615  				{
   616  					EventType: &eventType,
   617  					WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   618  						Result: encodedResult,
   619  					},
   620  				},
   621  			},
   622  		},
   623  		NextPageToken: nil,
   624  	}
   625  	var wid *string
   626  	getHistory := s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(getResponse, nil).Times(1)
   627  	getHistory.Do(func(ctx interface{}, getRequest *shared.GetWorkflowExecutionHistoryRequest, opt1 interface{}, opt2 interface{}, opt3 interface{}, opt4 interface{}) {
   628  		wid = getRequest.Execution.WorkflowId
   629  		s.NotNil(wid)
   630  		s.NotEmpty(*wid)
   631  	})
   632  
   633  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   634  		context.Background(),
   635  		StartWorkflowOptions{
   636  			TaskList:                        tasklist,
   637  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   638  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   639  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   640  		}, workflowType,
   641  	)
   642  	s.Nil(err)
   643  	s.Equal(workflowRun.GetRunID(), runID)
   644  	decodedResult := time.Minute
   645  	err = workflowRun.Get(context.Background(), &decodedResult)
   646  	s.Nil(err)
   647  	s.Equal(workflowResult, decodedResult)
   648  	s.Equal(workflowRun.GetID(), *wid)
   649  }
   650  
   651  // Test for the bug in ExecuteWorkflow in the case of raw history returned from API.
   652  // When Options.ID was empty then GetWorkflowExecutionHistory was called with an empty WorkflowID.
   653  func (s *workflowRunSuite) TestExecuteWorkflow_NoIdInOptions_RawHistory() {
   654  	createResponse := &shared.StartWorkflowExecutionResponse{
   655  		RunId: common.StringPtr(runID),
   656  	}
   657  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   658  
   659  	eventType := shared.EventTypeWorkflowExecutionCompleted
   660  	workflowResult := time.Hour * 59
   661  	encodedResult, _ := encodeArg(nil, workflowResult)
   662  	events := []*shared.HistoryEvent{
   663  		{
   664  			EventType: &eventType,
   665  			WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   666  				Result: encodedResult,
   667  			},
   668  		},
   669  	}
   670  
   671  	blobData := serializeEvents(events)
   672  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   673  		RawHistory: []*shared.DataBlob{
   674  			blobData,
   675  		},
   676  		NextPageToken: nil,
   677  	}
   678  	var wid *string
   679  	getHistory := s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), gomock.Any(), callOptions()...).Return(getResponse, nil).Times(1)
   680  	getHistory.Do(func(ctx interface{}, getRequest *shared.GetWorkflowExecutionHistoryRequest, opt1 interface{}, opt2 interface{}, opt3 interface{}, opt4 interface{}) {
   681  		wid = getRequest.Execution.WorkflowId
   682  		s.NotNil(wid)
   683  		s.NotEmpty(*wid)
   684  	})
   685  
   686  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   687  		context.Background(),
   688  		StartWorkflowOptions{
   689  			TaskList:                        tasklist,
   690  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   691  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   692  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   693  		}, workflowType,
   694  	)
   695  	s.Nil(err)
   696  	s.Equal(workflowRun.GetRunID(), runID)
   697  	decodedResult := time.Minute
   698  	err = workflowRun.Get(context.Background(), &decodedResult)
   699  	s.Nil(err)
   700  	s.Equal(workflowResult, decodedResult)
   701  	s.Equal(workflowRun.GetID(), *wid)
   702  }
   703  
   704  func (s *workflowRunSuite) TestExecuteWorkflow_NoDup_Cancelled() {
   705  	createResponse := &shared.StartWorkflowExecutionResponse{
   706  		RunId: common.StringPtr(runID),
   707  	}
   708  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   709  
   710  	filterType := shared.HistoryEventFilterTypeCloseEvent
   711  	eventType := shared.EventTypeWorkflowExecutionCanceled
   712  	details := "some details"
   713  	encodedDetails, _ := encodeArg(getDefaultDataConverter(), details)
   714  	getRequest := getGetWorkflowExecutionHistoryRequest(filterType)
   715  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   716  		History: &shared.History{
   717  			Events: []*shared.HistoryEvent{
   718  				&shared.HistoryEvent{
   719  					EventType: &eventType,
   720  					WorkflowExecutionCanceledEventAttributes: &shared.WorkflowExecutionCanceledEventAttributes{
   721  						Details: encodedDetails,
   722  					},
   723  				},
   724  			},
   725  		},
   726  		NextPageToken: nil,
   727  	}
   728  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest, callOptions()...).Return(getResponse, nil).Times(1)
   729  
   730  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   731  		context.Background(),
   732  		StartWorkflowOptions{
   733  			ID:                              workflowID,
   734  			TaskList:                        tasklist,
   735  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   736  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   737  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   738  		}, workflowType,
   739  	)
   740  	s.Nil(err)
   741  	s.Equal(workflowRun.GetID(), workflowID)
   742  	s.Equal(workflowRun.GetRunID(), runID)
   743  	decodedResult := time.Minute
   744  	err = workflowRun.Get(context.Background(), &decodedResult)
   745  	s.NotNil(err)
   746  	_, ok := err.(*CanceledError)
   747  	s.True(ok)
   748  	s.Equal(time.Minute, decodedResult)
   749  }
   750  
   751  func (s *workflowRunSuite) TestExecuteWorkflow_NoDup_Failed() {
   752  	createResponse := &shared.StartWorkflowExecutionResponse{
   753  		RunId: common.StringPtr(runID),
   754  	}
   755  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   756  
   757  	filterType := shared.HistoryEventFilterTypeCloseEvent
   758  	eventType := shared.EventTypeWorkflowExecutionFailed
   759  	reason := "some reason"
   760  	details := "some details"
   761  	dataConverter := getDefaultDataConverter()
   762  	encodedDetails, _ := encodeArg(dataConverter, details)
   763  	getRequest := getGetWorkflowExecutionHistoryRequest(filterType)
   764  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   765  		History: &shared.History{
   766  			Events: []*shared.HistoryEvent{
   767  				&shared.HistoryEvent{
   768  					EventType: &eventType,
   769  					WorkflowExecutionFailedEventAttributes: &shared.WorkflowExecutionFailedEventAttributes{
   770  						Reason:  common.StringPtr(reason),
   771  						Details: encodedDetails,
   772  					},
   773  				},
   774  			},
   775  		},
   776  		NextPageToken: nil,
   777  	}
   778  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest, callOptions()...).Return(getResponse, nil).Times(1)
   779  
   780  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   781  		context.Background(),
   782  		StartWorkflowOptions{
   783  			ID:                              workflowID,
   784  			TaskList:                        tasklist,
   785  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   786  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   787  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   788  		}, workflowType,
   789  	)
   790  	s.Nil(err)
   791  	s.Equal(workflowRun.GetID(), workflowID)
   792  	s.Equal(workflowRun.GetRunID(), runID)
   793  	decodedResult := time.Minute
   794  	err = workflowRun.Get(context.Background(), &decodedResult)
   795  	s.Equal(constructError(reason, encodedDetails, dataConverter), err)
   796  	s.Equal(time.Minute, decodedResult)
   797  }
   798  
   799  func (s *workflowRunSuite) TestExecuteWorkflow_NoDup_Terminated() {
   800  	createResponse := &shared.StartWorkflowExecutionResponse{
   801  		RunId: common.StringPtr(runID),
   802  	}
   803  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   804  
   805  	filterType := shared.HistoryEventFilterTypeCloseEvent
   806  	eventType := shared.EventTypeWorkflowExecutionTerminated
   807  	getRequest := getGetWorkflowExecutionHistoryRequest(filterType)
   808  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   809  		History: &shared.History{
   810  			Events: []*shared.HistoryEvent{
   811  				&shared.HistoryEvent{
   812  					EventType: &eventType,
   813  					WorkflowExecutionTerminatedEventAttributes: &shared.WorkflowExecutionTerminatedEventAttributes{},
   814  				},
   815  			},
   816  		},
   817  		NextPageToken: nil,
   818  	}
   819  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest, callOptions()...).Return(getResponse, nil).Times(1)
   820  
   821  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   822  		context.Background(),
   823  		StartWorkflowOptions{
   824  			ID:                              workflowID,
   825  			TaskList:                        tasklist,
   826  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   827  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   828  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   829  		}, workflowType,
   830  	)
   831  	s.Nil(err)
   832  	s.Equal(workflowRun.GetID(), workflowID)
   833  	s.Equal(workflowRun.GetRunID(), runID)
   834  	decodedResult := time.Minute
   835  	err = workflowRun.Get(context.Background(), &decodedResult)
   836  	s.Equal(newTerminatedError(), err)
   837  	s.Equal(time.Minute, decodedResult)
   838  }
   839  
   840  func (s *workflowRunSuite) TestExecuteWorkflow_NoDup_TimedOut() {
   841  	createResponse := &shared.StartWorkflowExecutionResponse{
   842  		RunId: common.StringPtr(runID),
   843  	}
   844  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   845  
   846  	filterType := shared.HistoryEventFilterTypeCloseEvent
   847  	eventType := shared.EventTypeWorkflowExecutionTimedOut
   848  	timeType := shared.TimeoutTypeScheduleToStart
   849  	getRequest := getGetWorkflowExecutionHistoryRequest(filterType)
   850  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   851  		History: &shared.History{
   852  			Events: []*shared.HistoryEvent{
   853  				&shared.HistoryEvent{
   854  					EventType: &eventType,
   855  					WorkflowExecutionTimedOutEventAttributes: &shared.WorkflowExecutionTimedOutEventAttributes{
   856  						TimeoutType: &timeType,
   857  					},
   858  				},
   859  			},
   860  		},
   861  		NextPageToken: nil,
   862  	}
   863  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest, callOptions()...).Return(getResponse, nil).Times(1)
   864  
   865  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   866  		context.Background(),
   867  		StartWorkflowOptions{
   868  			ID:                              workflowID,
   869  			TaskList:                        tasklist,
   870  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   871  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   872  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   873  		}, workflowType,
   874  	)
   875  	s.Nil(err)
   876  	s.Equal(workflowRun.GetID(), workflowID)
   877  	s.Equal(workflowRun.GetRunID(), runID)
   878  	decodedResult := time.Minute
   879  	err = workflowRun.Get(context.Background(), &decodedResult)
   880  	s.NotNil(err)
   881  	_, ok := err.(*TimeoutError)
   882  	s.True(ok)
   883  	s.Equal(timeType, err.(*TimeoutError).TimeoutType())
   884  	s.Equal(time.Minute, decodedResult)
   885  }
   886  
   887  func (s *workflowRunSuite) TestExecuteWorkflow_NoDup_ContinueAsNew() {
   888  	createResponse := &shared.StartWorkflowExecutionResponse{
   889  		RunId: common.StringPtr(runID),
   890  	}
   891  	s.workflowServiceClient.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), callOptions()...).Return(createResponse, nil).Times(1)
   892  
   893  	newRunID := "some other random run ID"
   894  	filterType := shared.HistoryEventFilterTypeCloseEvent
   895  	eventType1 := shared.EventTypeWorkflowExecutionContinuedAsNew
   896  	getRequest1 := getGetWorkflowExecutionHistoryRequest(filterType)
   897  	getResponse1 := &shared.GetWorkflowExecutionHistoryResponse{
   898  		History: &shared.History{
   899  			Events: []*shared.HistoryEvent{
   900  				&shared.HistoryEvent{
   901  					EventType: &eventType1,
   902  					WorkflowExecutionContinuedAsNewEventAttributes: &shared.WorkflowExecutionContinuedAsNewEventAttributes{
   903  						NewExecutionRunId: common.StringPtr(newRunID),
   904  					},
   905  				},
   906  			},
   907  		},
   908  		NextPageToken: nil,
   909  	}
   910  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest1, callOptions()...).Return(getResponse1, nil).Times(1)
   911  
   912  	workflowResult := time.Hour * 59
   913  	encodedResult, _ := encodeArg(getDefaultDataConverter(), workflowResult)
   914  	eventType2 := shared.EventTypeWorkflowExecutionCompleted
   915  	getRequest2 := getGetWorkflowExecutionHistoryRequest(filterType)
   916  	getRequest2.Execution.RunId = common.StringPtr(newRunID)
   917  	getResponse2 := &shared.GetWorkflowExecutionHistoryResponse{
   918  		History: &shared.History{
   919  			Events: []*shared.HistoryEvent{
   920  				&shared.HistoryEvent{
   921  					EventType: &eventType2,
   922  					WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   923  						Result: encodedResult,
   924  					},
   925  				},
   926  			},
   927  		},
   928  		NextPageToken: nil,
   929  	}
   930  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest2, callOptions()...).Return(getResponse2, nil).Times(1)
   931  
   932  	workflowRun, err := s.workflowClient.ExecuteWorkflow(
   933  		context.Background(),
   934  		StartWorkflowOptions{
   935  			ID:                              workflowID,
   936  			TaskList:                        tasklist,
   937  			ExecutionStartToCloseTimeout:    timeoutInSeconds * time.Second,
   938  			DecisionTaskStartToCloseTimeout: timeoutInSeconds * time.Second,
   939  			WorkflowIDReusePolicy:           workflowIDReusePolicy,
   940  		}, workflowType,
   941  	)
   942  	s.Nil(err)
   943  	s.Equal(workflowRun.GetID(), workflowID)
   944  	s.Equal(workflowRun.GetRunID(), runID)
   945  	decodedResult := time.Minute
   946  	err = workflowRun.Get(context.Background(), &decodedResult)
   947  	s.Nil(err)
   948  	s.Equal(workflowResult, decodedResult)
   949  }
   950  
   951  func (s *workflowRunSuite) TestGetWorkflow() {
   952  	filterType := shared.HistoryEventFilterTypeCloseEvent
   953  	eventType := shared.EventTypeWorkflowExecutionCompleted
   954  	workflowResult := time.Hour * 59
   955  	encodedResult, _ := encodeArg(getDefaultDataConverter(), workflowResult)
   956  	getRequest := getGetWorkflowExecutionHistoryRequest(filterType)
   957  	getResponse := &shared.GetWorkflowExecutionHistoryResponse{
   958  		History: &shared.History{
   959  			Events: []*shared.HistoryEvent{
   960  				&shared.HistoryEvent{
   961  					EventType: &eventType,
   962  					WorkflowExecutionCompletedEventAttributes: &shared.WorkflowExecutionCompletedEventAttributes{
   963  						Result: encodedResult,
   964  					},
   965  				},
   966  			},
   967  		},
   968  		NextPageToken: nil,
   969  	}
   970  	s.workflowServiceClient.EXPECT().GetWorkflowExecutionHistory(gomock.Any(), getRequest, callOptions()...).Return(getResponse, nil).Times(1)
   971  
   972  	workflowID := workflowID
   973  	runID := runID
   974  
   975  	workflowRun := s.workflowClient.GetWorkflow(
   976  		context.Background(),
   977  		workflowID,
   978  		runID,
   979  	)
   980  	s.Equal(workflowRun.GetID(), workflowID)
   981  	s.Equal(workflowRun.GetRunID(), runID)
   982  	decodedResult := time.Minute
   983  	err := workflowRun.Get(context.Background(), &decodedResult)
   984  	s.Nil(err)
   985  	s.Equal(workflowResult, decodedResult)
   986  }
   987  
   988  func getGetWorkflowExecutionHistoryRequest(filterType shared.HistoryEventFilterType) *shared.GetWorkflowExecutionHistoryRequest {
   989  	isLongPoll := true
   990  
   991  	request := &shared.GetWorkflowExecutionHistoryRequest{
   992  		Domain: common.StringPtr(domain),
   993  		Execution: &shared.WorkflowExecution{
   994  			WorkflowId: common.StringPtr(workflowID),
   995  			RunId:      getRunID(runID),
   996  		},
   997  		WaitForNewEvent:        common.BoolPtr(isLongPoll),
   998  		HistoryEventFilterType: &filterType,
   999  		SkipArchival:           common.BoolPtr(true),
  1000  	}
  1001  
  1002  	return request
  1003  }
  1004  
  1005  // workflow client test suite
  1006  type (
  1007  	workflowClientTestSuite struct {
  1008  		suite.Suite
  1009  		mockCtrl *gomock.Controller
  1010  		service  *workflowservicetest.MockClient
  1011  		client   Client
  1012  	}
  1013  )
  1014  
  1015  func TestWorkflowClientSuite(t *testing.T) {
  1016  	suite.Run(t, new(workflowClientTestSuite))
  1017  }
  1018  
  1019  func (s *workflowClientTestSuite) SetupSuite() {
  1020  	if testing.Verbose() {
  1021  		log.SetOutput(os.Stdout)
  1022  	}
  1023  }
  1024  
  1025  func (s *workflowClientTestSuite) SetupTest() {
  1026  	s.mockCtrl = gomock.NewController(s.T())
  1027  	s.service = workflowservicetest.NewMockClient(s.mockCtrl)
  1028  	s.client = NewClient(s.service, domain, nil)
  1029  }
  1030  
  1031  func (s *workflowClientTestSuite) TearDownTest() {
  1032  	s.mockCtrl.Finish() // assert mock’s expectations
  1033  }
  1034  
  1035  func (s *workflowClientTestSuite) TestSignalWithStartWorkflow() {
  1036  	signalName := "my signal"
  1037  	signalInput := []byte("my signal input")
  1038  	options := StartWorkflowOptions{
  1039  		ID:                              workflowID,
  1040  		TaskList:                        tasklist,
  1041  		ExecutionStartToCloseTimeout:    timeoutInSeconds,
  1042  		DecisionTaskStartToCloseTimeout: timeoutInSeconds,
  1043  	}
  1044  
  1045  	createResponse := &shared.StartWorkflowExecutionResponse{
  1046  		RunId: common.StringPtr(runID),
  1047  	}
  1048  	s.service.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(createResponse, nil).Times(2)
  1049  
  1050  	resp, err := s.client.SignalWithStartWorkflow(context.Background(), workflowID, signalName, signalInput,
  1051  		options, workflowType)
  1052  	s.Nil(err)
  1053  	s.Equal(createResponse.GetRunId(), resp.RunID)
  1054  
  1055  	resp, err = s.client.SignalWithStartWorkflow(context.Background(), "", signalName, signalInput,
  1056  		options, workflowType)
  1057  	s.Nil(err)
  1058  	s.Equal(createResponse.GetRunId(), resp.RunID)
  1059  }
  1060  
  1061  func (s *workflowClientTestSuite) TestSignalWithStartWorkflow_Error() {
  1062  	signalName := "my signal"
  1063  	signalInput := []byte("my signal input")
  1064  	options := StartWorkflowOptions{}
  1065  
  1066  	resp, err := s.client.SignalWithStartWorkflow(context.Background(), workflowID, signalName, signalInput,
  1067  		options, workflowType)
  1068  	s.Equal(errors.New("missing TaskList"), err)
  1069  	s.Nil(resp)
  1070  
  1071  	options.TaskList = tasklist
  1072  	resp, err = s.client.SignalWithStartWorkflow(context.Background(), workflowID, signalName, signalInput,
  1073  		options, workflowType)
  1074  	s.NotNil(err)
  1075  	s.Nil(resp)
  1076  
  1077  	options.ExecutionStartToCloseTimeout = timeoutInSeconds
  1078  	createResponse := &shared.StartWorkflowExecutionResponse{
  1079  		RunId: common.StringPtr(runID),
  1080  	}
  1081  	s.service.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(createResponse, nil)
  1082  	resp, err = s.client.SignalWithStartWorkflow(context.Background(), workflowID, signalName, signalInput,
  1083  		options, workflowType)
  1084  	s.Nil(err)
  1085  	s.Equal(createResponse.GetRunId(), resp.RunID)
  1086  }
  1087  
  1088  func (s *workflowClientTestSuite) TestStartWorkflow() {
  1089  	client, ok := s.client.(*workflowClient)
  1090  	s.True(ok)
  1091  	options := StartWorkflowOptions{
  1092  		ID:                              workflowID,
  1093  		TaskList:                        tasklist,
  1094  		ExecutionStartToCloseTimeout:    timeoutInSeconds,
  1095  		DecisionTaskStartToCloseTimeout: timeoutInSeconds,
  1096  	}
  1097  	f1 := func(ctx Context, r []byte) string {
  1098  		return "result"
  1099  	}
  1100  
  1101  	createResponse := &shared.StartWorkflowExecutionResponse{
  1102  		RunId: common.StringPtr(runID),
  1103  	}
  1104  	s.service.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(createResponse, nil)
  1105  
  1106  	resp, err := client.StartWorkflow(context.Background(), options, f1, []byte("test"))
  1107  	s.Equal(getDefaultDataConverter(), client.dataConverter)
  1108  	s.Nil(err)
  1109  	s.Equal(createResponse.GetRunId(), resp.RunID)
  1110  }
  1111  
  1112  func (s *workflowClientTestSuite) TestStartWorkflow_WithContext() {
  1113  	s.client = NewClient(s.service, domain, &ClientOptions{ContextPropagators: []ContextPropagator{NewStringMapPropagator([]string{testHeader})}})
  1114  	client, ok := s.client.(*workflowClient)
  1115  	s.True(ok)
  1116  	options := StartWorkflowOptions{
  1117  		ID:                              workflowID,
  1118  		TaskList:                        tasklist,
  1119  		ExecutionStartToCloseTimeout:    timeoutInSeconds,
  1120  		DecisionTaskStartToCloseTimeout: timeoutInSeconds,
  1121  	}
  1122  	f1 := func(ctx Context, r []byte) error {
  1123  		value := ctx.Value(contextKey(testHeader))
  1124  		if val, ok := value.([]byte); ok {
  1125  			s.Equal("test-data", string(val))
  1126  			return nil
  1127  		}
  1128  		return fmt.Errorf("context did not propagate to workflow")
  1129  	}
  1130  
  1131  	createResponse := &shared.StartWorkflowExecutionResponse{
  1132  		RunId: common.StringPtr(runID),
  1133  	}
  1134  	s.service.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(createResponse, nil)
  1135  
  1136  	resp, err := client.StartWorkflow(context.Background(), options, f1, []byte("test"))
  1137  	s.Nil(err)
  1138  	s.Equal(createResponse.GetRunId(), resp.RunID)
  1139  }
  1140  
  1141  func (s *workflowClientTestSuite) TestStartWorkflow_WithDataConverter() {
  1142  	dc := newTestDataConverter()
  1143  	s.client = NewClient(s.service, domain, &ClientOptions{DataConverter: dc})
  1144  	client, ok := s.client.(*workflowClient)
  1145  	s.True(ok)
  1146  	options := StartWorkflowOptions{
  1147  		ID:                              workflowID,
  1148  		TaskList:                        tasklist,
  1149  		ExecutionStartToCloseTimeout:    timeoutInSeconds,
  1150  		DecisionTaskStartToCloseTimeout: timeoutInSeconds,
  1151  	}
  1152  	f1 := func(ctx Context, r []byte) string {
  1153  		return "result"
  1154  	}
  1155  	input := []byte("test")
  1156  
  1157  	createResponse := &shared.StartWorkflowExecutionResponse{
  1158  		RunId: common.StringPtr(runID),
  1159  	}
  1160  	s.service.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(createResponse, nil).
  1161  		Do(func(_ interface{}, req *shared.StartWorkflowExecutionRequest, _ ...interface{}) {
  1162  			dc := client.dataConverter
  1163  			encodedArg, _ := dc.ToData(input)
  1164  			s.Equal(req.Input, encodedArg)
  1165  			var decodedArg []byte
  1166  			dc.FromData(req.Input, &decodedArg)
  1167  			s.Equal(input, decodedArg)
  1168  		})
  1169  
  1170  	resp, err := client.StartWorkflow(context.Background(), options, f1, input)
  1171  	s.Equal(newTestDataConverter(), client.dataConverter)
  1172  	s.Nil(err)
  1173  	s.Equal(createResponse.GetRunId(), resp.RunID)
  1174  }
  1175  
  1176  func (s *workflowClientTestSuite) TestStartWorkflow_WithMemoAndSearchAttr() {
  1177  	memo := map[string]interface{}{
  1178  		"testMemo": "memo value",
  1179  	}
  1180  	searchAttributes := map[string]interface{}{
  1181  		"testAttr": "attr value",
  1182  	}
  1183  	options := StartWorkflowOptions{
  1184  		ID:                              workflowID,
  1185  		TaskList:                        tasklist,
  1186  		ExecutionStartToCloseTimeout:    timeoutInSeconds,
  1187  		DecisionTaskStartToCloseTimeout: timeoutInSeconds,
  1188  		Memo:                            memo,
  1189  		SearchAttributes:                searchAttributes,
  1190  	}
  1191  	wf := func(ctx Context) string {
  1192  		return "result"
  1193  	}
  1194  	startResp := &shared.StartWorkflowExecutionResponse{}
  1195  
  1196  	s.service.EXPECT().StartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(startResp, nil).
  1197  		Do(func(_ interface{}, req *shared.StartWorkflowExecutionRequest, _ ...interface{}) {
  1198  			var resultMemo, resultAttr string
  1199  			err := json.Unmarshal(req.Memo.Fields["testMemo"], &resultMemo)
  1200  			s.NoError(err)
  1201  			s.Equal("memo value", resultMemo)
  1202  
  1203  			err = json.Unmarshal(req.SearchAttributes.IndexedFields["testAttr"], &resultAttr)
  1204  			s.NoError(err)
  1205  			s.Equal("attr value", resultAttr)
  1206  		})
  1207  	s.client.StartWorkflow(context.Background(), options, wf)
  1208  }
  1209  
  1210  func (s *workflowClientTestSuite) SignalWithStartWorkflowWithMemoAndSearchAttr() {
  1211  	memo := map[string]interface{}{
  1212  		"testMemo": "memo value",
  1213  	}
  1214  	searchAttributes := map[string]interface{}{
  1215  		"testAttr": "attr value",
  1216  	}
  1217  	options := StartWorkflowOptions{
  1218  		ID:                              workflowID,
  1219  		TaskList:                        tasklist,
  1220  		ExecutionStartToCloseTimeout:    timeoutInSeconds,
  1221  		DecisionTaskStartToCloseTimeout: timeoutInSeconds,
  1222  		Memo:                            memo,
  1223  		SearchAttributes:                searchAttributes,
  1224  	}
  1225  	wf := func(ctx Context) string {
  1226  		return "result"
  1227  	}
  1228  	startResp := &shared.StartWorkflowExecutionResponse{}
  1229  
  1230  	s.service.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any(),
  1231  		gomock.Any(), gomock.Any(), gomock.Any()).Return(startResp, nil).
  1232  		Do(func(_ interface{}, req *shared.SignalWithStartWorkflowExecutionRequest, _ ...interface{}) {
  1233  			var resultMemo, resultAttr string
  1234  			err := json.Unmarshal(req.Memo.Fields["testMemo"], &resultMemo)
  1235  			s.NoError(err)
  1236  			s.Equal("memo value", resultMemo)
  1237  
  1238  			err = json.Unmarshal(req.SearchAttributes.IndexedFields["testAttr"], &resultAttr)
  1239  			s.NoError(err)
  1240  			s.Equal("attr value", resultAttr)
  1241  		})
  1242  	s.client.SignalWithStartWorkflow(context.Background(), "wid", "signal", "value", options, wf)
  1243  }
  1244  
  1245  func (s *workflowClientTestSuite) TestGetWorkflowMemo() {
  1246  	var input1 map[string]interface{}
  1247  	result1, err := getWorkflowMemo(input1, nil)
  1248  	s.NoError(err)
  1249  	s.Nil(result1)
  1250  
  1251  	input1 = make(map[string]interface{})
  1252  	result2, err := getWorkflowMemo(input1, nil)
  1253  	s.NoError(err)
  1254  	s.NotNil(result2)
  1255  	s.Equal(0, len(result2.Fields))
  1256  
  1257  	input1["t1"] = "v1"
  1258  	result3, err := getWorkflowMemo(input1, nil)
  1259  	s.NoError(err)
  1260  	s.NotNil(result3)
  1261  	s.Equal(1, len(result3.Fields))
  1262  	var resultString string
  1263  	decodeArg(nil, result3.Fields["t1"], &resultString)
  1264  	s.Equal("v1", resultString)
  1265  
  1266  	input1["non-serializable"] = make(chan int)
  1267  	_, err = getWorkflowMemo(input1, nil)
  1268  	s.Error(err)
  1269  }
  1270  
  1271  func (s *workflowClientTestSuite) TestSerializeSearchAttributes() {
  1272  	var input1 map[string]interface{}
  1273  	result1, err := serializeSearchAttributes(input1)
  1274  	s.NoError(err)
  1275  	s.Nil(result1)
  1276  
  1277  	input1 = make(map[string]interface{})
  1278  	result2, err := serializeSearchAttributes(input1)
  1279  	s.NoError(err)
  1280  	s.NotNil(result2)
  1281  	s.Equal(0, len(result2.IndexedFields))
  1282  
  1283  	input1["t1"] = "v1"
  1284  	result3, err := serializeSearchAttributes(input1)
  1285  	s.NoError(err)
  1286  	s.NotNil(result3)
  1287  	s.Equal(1, len(result3.IndexedFields))
  1288  	var resultString string
  1289  	decodeArg(nil, result3.IndexedFields["t1"], &resultString)
  1290  	s.Equal("v1", resultString)
  1291  
  1292  	input1["non-serializable"] = make(chan int)
  1293  	_, err = serializeSearchAttributes(input1)
  1294  	s.Error(err)
  1295  }
  1296  
  1297  func (s *workflowClientTestSuite) TestListWorkflow() {
  1298  	request := &shared.ListWorkflowExecutionsRequest{}
  1299  	response := &shared.ListWorkflowExecutionsResponse{}
  1300  	s.service.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(response, nil).
  1301  		Do(func(_ interface{}, req *shared.ListWorkflowExecutionsRequest, _ ...interface{}) {
  1302  			s.Equal(domain, request.GetDomain())
  1303  		})
  1304  	resp, err := s.client.ListWorkflow(context.Background(), request)
  1305  	s.Nil(err)
  1306  	s.Equal(response, resp)
  1307  
  1308  	responseErr := &shared.BadRequestError{}
  1309  	request.Domain = common.StringPtr("another")
  1310  	s.service.EXPECT().ListWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, responseErr).
  1311  		Do(func(_ interface{}, req *shared.ListWorkflowExecutionsRequest, _ ...interface{}) {
  1312  			s.Equal("another", request.GetDomain())
  1313  		})
  1314  	resp, err = s.client.ListWorkflow(context.Background(), request)
  1315  	s.Equal(responseErr, err)
  1316  }
  1317  
  1318  func (s *workflowClientTestSuite) TestListArchivedWorkflow() {
  1319  	request := &shared.ListArchivedWorkflowExecutionsRequest{}
  1320  	response := &shared.ListArchivedWorkflowExecutionsResponse{}
  1321  	s.service.EXPECT().ListArchivedWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(response, nil).
  1322  		Do(func(_ interface{}, req *shared.ListArchivedWorkflowExecutionsRequest, _ ...interface{}) {
  1323  			s.Equal(domain, request.GetDomain())
  1324  		})
  1325  	ctxWithTimeout, cancel := context.WithTimeout(context.Background(), time.Minute)
  1326  	defer cancel()
  1327  	resp, err := s.client.ListArchivedWorkflow(ctxWithTimeout, request)
  1328  	s.Nil(err)
  1329  	s.Equal(response, resp)
  1330  
  1331  	responseErr := &shared.BadRequestError{}
  1332  	request.Domain = common.StringPtr("another")
  1333  	s.service.EXPECT().ListArchivedWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, responseErr).
  1334  		Do(func(_ interface{}, req *shared.ListArchivedWorkflowExecutionsRequest, _ ...interface{}) {
  1335  			s.Equal("another", request.GetDomain())
  1336  		})
  1337  	resp, err = s.client.ListArchivedWorkflow(ctxWithTimeout, request)
  1338  	s.Equal(responseErr, err)
  1339  }
  1340  
  1341  func (s *workflowClientTestSuite) TestScanWorkflow() {
  1342  	request := &shared.ListWorkflowExecutionsRequest{}
  1343  	response := &shared.ListWorkflowExecutionsResponse{}
  1344  	s.service.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(response, nil).
  1345  		Do(func(_ interface{}, req *shared.ListWorkflowExecutionsRequest, _ ...interface{}) {
  1346  			s.Equal(domain, request.GetDomain())
  1347  		})
  1348  	resp, err := s.client.ScanWorkflow(context.Background(), request)
  1349  	s.Nil(err)
  1350  	s.Equal(response, resp)
  1351  
  1352  	responseErr := &shared.BadRequestError{}
  1353  	request.Domain = common.StringPtr("another")
  1354  	s.service.EXPECT().ScanWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, responseErr).
  1355  		Do(func(_ interface{}, req *shared.ListWorkflowExecutionsRequest, _ ...interface{}) {
  1356  			s.Equal("another", request.GetDomain())
  1357  		})
  1358  	resp, err = s.client.ScanWorkflow(context.Background(), request)
  1359  	s.Equal(responseErr, err)
  1360  }
  1361  
  1362  func (s *workflowClientTestSuite) TestCountWorkflow() {
  1363  	request := &shared.CountWorkflowExecutionsRequest{}
  1364  	response := &shared.CountWorkflowExecutionsResponse{}
  1365  	s.service.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(response, nil).
  1366  		Do(func(_ interface{}, req *shared.CountWorkflowExecutionsRequest, _ ...interface{}) {
  1367  			s.Equal(domain, request.GetDomain())
  1368  		})
  1369  	resp, err := s.client.CountWorkflow(context.Background(), request)
  1370  	s.Nil(err)
  1371  	s.Equal(response, resp)
  1372  
  1373  	responseErr := &shared.BadRequestError{}
  1374  	request.Domain = common.StringPtr("another")
  1375  	s.service.EXPECT().CountWorkflowExecutions(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, responseErr).
  1376  		Do(func(_ interface{}, req *shared.CountWorkflowExecutionsRequest, _ ...interface{}) {
  1377  			s.Equal("another", request.GetDomain())
  1378  		})
  1379  	resp, err = s.client.CountWorkflow(context.Background(), request)
  1380  	s.Equal(responseErr, err)
  1381  }
  1382  
  1383  func (s *workflowClientTestSuite) TestGetSearchAttributes() {
  1384  	response := &shared.GetSearchAttributesResponse{}
  1385  	s.service.EXPECT().GetSearchAttributes(gomock.Any(), gomock.Any()).Return(response, nil)
  1386  	resp, err := s.client.GetSearchAttributes(context.Background())
  1387  	s.Nil(err)
  1388  	s.Equal(response, resp)
  1389  
  1390  	responseErr := &shared.BadRequestError{}
  1391  	s.service.EXPECT().GetSearchAttributes(gomock.Any(), gomock.Any()).Return(nil, responseErr)
  1392  	resp, err = s.client.GetSearchAttributes(context.Background())
  1393  	s.Equal(responseErr, err)
  1394  }
  1395  
  1396  func serializeEvents(events []*shared.HistoryEvent) *shared.DataBlob {
  1397  
  1398  	blob, _ := serializer.SerializeBatchEvents(events, shared.EncodingTypeThriftRW)
  1399  
  1400  	return &shared.DataBlob{
  1401  		EncodingType: shared.EncodingTypeThriftRW.Ptr(),
  1402  		Data:         blob.Data,
  1403  	}
  1404  }
  1405  
  1406  func (s *workflowClientTestSuite) TestCancelWorkflow() {
  1407  	s.service.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), newPartialCancelRequestMatcher(common.StringPtr("testWf"), common.StringPtr("test reason")), gomock.All(gomock.Any())).Return(nil)
  1408  
  1409  	err := s.client.CancelWorkflow(context.Background(), "testWf", "testRun", WithCancelReason("test reason"))
  1410  
  1411  	s.Nil(err)
  1412  }
  1413  
  1414  func (s *workflowClientTestSuite) TestCancelWorkflowBackwardsCompatible() {
  1415  	s.service.EXPECT().RequestCancelWorkflowExecution(gomock.Any(), newPartialCancelRequestMatcher(common.StringPtr("testWf"), nil), gomock.All(gomock.Any())).Return(nil)
  1416  
  1417  	err := s.client.CancelWorkflow(context.Background(), "testWf", "testRun")
  1418  
  1419  	s.Nil(err)
  1420  }
  1421  
  1422  type PartialCancelRequestMatcher struct {
  1423  	wfId  *string
  1424  	cause *string
  1425  }
  1426  
  1427  func newPartialCancelRequestMatcher(wfId *string, cause *string) gomock.Matcher {
  1428  	return &PartialCancelRequestMatcher{
  1429  		wfId:  wfId,
  1430  		cause: cause,
  1431  	}
  1432  }
  1433  
  1434  func (m *PartialCancelRequestMatcher) Matches(a interface{}) bool {
  1435  	aEx, ok := a.(*shared.RequestCancelWorkflowExecutionRequest)
  1436  	if !ok {
  1437  		return false
  1438  	}
  1439  
  1440  	return (aEx.Cause == m.cause || *aEx.Cause == *m.cause) && *aEx.WorkflowExecution.WorkflowId == *m.wfId
  1441  }
  1442  
  1443  func (m *PartialCancelRequestMatcher) String() string {
  1444  	return "partial cancellation request matcher matches cause and wfId fields"
  1445  }