go.uber.org/cadence@v1.2.9/test/integration_test.go (about)

     1  // Copyright (c) 2017-2020 Uber Technologies Inc.
     2  // Portions of the Software are attributed to Copyright (c) 2020 Temporal Technologies Inc.
     3  //
     4  // Permission is hereby granted, free of charge, to any person obtaining a copy
     5  // of this software and associated documentation files (the "Software"), to deal
     6  // in the Software without restriction, including without limitation the rights
     7  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8  // copies of the Software, and to permit persons to whom the Software is
     9  // furnished to do so, subject to the following conditions:
    10  //
    11  // The above copyright notice and this permission notice shall be included in
    12  // all copies or substantial portions of the Software.
    13  //
    14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20  // THE SOFTWARE.
    21  
    22  package test
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"net"
    29  	"strings"
    30  	"sync"
    31  	"testing"
    32  	"time"
    33  
    34  	"github.com/pborman/uuid"
    35  	"github.com/stretchr/testify/require"
    36  	"github.com/stretchr/testify/suite"
    37  	"github.com/uber-go/tally"
    38  	"go.uber.org/goleak"
    39  	"go.uber.org/zap/zaptest"
    40  
    41  	"go.uber.org/cadence"
    42  	"go.uber.org/cadence/.gen/go/shared"
    43  	"go.uber.org/cadence/client"
    44  	"go.uber.org/cadence/interceptors"
    45  	"go.uber.org/cadence/internal"
    46  	"go.uber.org/cadence/worker"
    47  	"go.uber.org/cadence/workflow"
    48  )
    49  
    50  type IntegrationTestSuite struct {
    51  	*require.Assertions
    52  	suite.Suite
    53  	config       Config
    54  	rpcClient    *rpcClient
    55  	libClient    client.Client
    56  	domainClient client.DomainClient
    57  	activities   *Activities
    58  	workflows    *Workflows
    59  	worker       worker.Worker
    60  	seq          int64
    61  	taskListName string
    62  	tracer       *tracingInterceptorFactory
    63  }
    64  
    65  const (
    66  	ctxTimeout                 = 15 * time.Second
    67  	domainName                 = "integration-test-domain"
    68  	domainCacheRefreshInterval = 20 * time.Second
    69  	testContextKey             = "test-context-key"
    70  )
    71  
    72  func TestIntegrationSuite(t *testing.T) {
    73  	suite.Run(t, new(IntegrationTestSuite))
    74  }
    75  
    76  // waitForTCP waits until target tcp address is available.
    77  func waitForTCP(timeout time.Duration, addr string) error {
    78  	var d net.Dialer
    79  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
    80  	defer cancel()
    81  
    82  	for {
    83  		select {
    84  		case <-ctx.Done():
    85  			return fmt.Errorf("failed to wait until %s: %v", addr, ctx.Err())
    86  		default:
    87  			conn, err := d.DialContext(ctx, "tcp", addr)
    88  			if err != nil {
    89  				continue
    90  			}
    91  			_ = conn.Close()
    92  			return nil
    93  		}
    94  	}
    95  }
    96  
    97  func (ts *IntegrationTestSuite) SetupSuite() {
    98  	ts.Assertions = require.New(ts.T())
    99  	ts.config = newConfig()
   100  	ts.activities = newActivities()
   101  	ts.workflows = &Workflows{}
   102  	ts.Nil(waitForTCP(time.Minute, ts.config.ServiceAddr))
   103  	var err error
   104  	if ts.config.EnableGrpcAdapter {
   105  		ts.rpcClient, err = newGRPCAdapterClient(ts.config.ServiceName, ts.config.ServiceAddr)
   106  	} else {
   107  		ts.rpcClient, err = newRPCClient(ts.config.ServiceName, ts.config.ServiceAddr)
   108  	}
   109  	ts.NoError(err)
   110  	ts.libClient = client.NewClient(ts.rpcClient.Interface, domainName,
   111  		&client.Options{
   112  			ContextPropagators: []workflow.ContextPropagator{NewStringMapPropagator([]string{testContextKey})},
   113  		})
   114  	ts.domainClient = client.NewDomainClient(ts.rpcClient.Interface, &client.Options{})
   115  	ts.registerDomain()
   116  	internal.StartVersionMetrics(tally.NoopScope)
   117  }
   118  
   119  func (ts *IntegrationTestSuite) TearDownSuite() {
   120  	ts.Assertions = require.New(ts.T())
   121  	ts.rpcClient.Close()
   122  	close(internal.StopMetrics)
   123  	// allow the pollers to shut down, and ensure there are no goroutine leaks.
   124  	// this will wait for up to 1 minute for leaks to subside, but exit relatively quickly if possible.
   125  	max := time.After(time.Minute)
   126  	var last error
   127  	for {
   128  		select {
   129  		case <-max:
   130  			if last != nil {
   131  				ts.NoError(last)
   132  				return
   133  			}
   134  			ts.FailNow("leaks timed out but no error, should be impossible")
   135  		case <-time.After(time.Second):
   136  			// https://github.com/uber-go/cadence-client/issues/739
   137  			last = goleak.Find(goleak.IgnoreTopFunction("go.uber.org/cadence/internal.(*coroutineState).initialYield"))
   138  			if last == nil {
   139  				// no leak, done waiting
   140  				return
   141  			}
   142  			// else wait for another check or the timeout (which will record the latest error)
   143  		}
   144  	}
   145  }
   146  
   147  func (ts *IntegrationTestSuite) SetupTest() {
   148  	ts.Assertions = require.New(ts.T())
   149  	ts.seq++
   150  	ts.activities.clearInvoked()
   151  	ts.taskListName = fmt.Sprintf("tl-%v", ts.seq)
   152  	ts.tracer = newtracingInterceptorFactory()
   153  }
   154  
   155  func (ts *IntegrationTestSuite) BeforeTest(suiteName, testName string) {
   156  	options := worker.Options{
   157  		DisableStickyExecution:            ts.config.IsStickyOff,
   158  		Logger:                            zaptest.NewLogger(ts.T()),
   159  		WorkflowInterceptorChainFactories: []interceptors.WorkflowInterceptorFactory{ts.tracer},
   160  		ContextPropagators:                []workflow.ContextPropagator{NewStringMapPropagator([]string{testContextKey})},
   161  	}
   162  
   163  	if testName == "TestNonDeterministicWorkflowQuery" || testName == "TestNonDeterministicWorkflowFailPolicy" {
   164  		options.NonDeterministicWorkflowPolicy = worker.NonDeterministicWorkflowPolicyFailWorkflow
   165  
   166  		// disable sticky executon so each workflow yield will require rerunning it from beginning
   167  		options.DisableStickyExecution = true
   168  	}
   169  
   170  	ts.worker = worker.New(ts.rpcClient.Interface, domainName, ts.taskListName, options)
   171  	ts.registerWorkflowsAndActivities(ts.worker)
   172  	ts.Nil(ts.worker.Start())
   173  }
   174  
   175  func (ts *IntegrationTestSuite) TearDownTest() {
   176  	ts.worker.Stop()
   177  }
   178  
   179  func (ts *IntegrationTestSuite) TestBasic() {
   180  	var expected []string
   181  	err := ts.executeWorkflow("test-basic", ts.workflows.Basic, &expected)
   182  	ts.NoError(err)
   183  	ts.EqualValues(expected, ts.activities.invoked())
   184  	ts.Equal([]string{"ExecuteWorkflow begin", "ExecuteActivity", "ExecuteActivity", "ExecuteWorkflow end"},
   185  		ts.tracer.GetTrace("go.uber.org/cadence/test.(*Workflows).Basic"))
   186  }
   187  
   188  func (ts *IntegrationTestSuite) TestActivityRetryOnError() {
   189  	var expected []string
   190  	err := ts.executeWorkflow("test-activity-retry-on-error", ts.workflows.ActivityRetryOnError, &expected)
   191  	ts.NoError(err)
   192  	ts.EqualValues(expected, ts.activities.invoked())
   193  }
   194  
   195  func (ts *IntegrationTestSuite) TestActivityRetryOnTimeoutStableError() {
   196  	var expected []string
   197  	err := ts.executeWorkflow("test-activity-retry-on-timeout-stable-error", ts.workflows.RetryTimeoutStableErrorWorkflow, &expected)
   198  	ts.Nil(err)
   199  }
   200  
   201  func (ts *IntegrationTestSuite) TestActivityRetryOptionsChange() {
   202  	var expected []string
   203  	err := ts.executeWorkflow("test-activity-retry-options-change", ts.workflows.ActivityRetryOptionsChange, &expected)
   204  	ts.NoError(err)
   205  	ts.EqualValues(expected, ts.activities.invoked())
   206  }
   207  
   208  func (ts *IntegrationTestSuite) TestActivityRetryOnStartToCloseTimeout() {
   209  	var expected []string
   210  	err := ts.executeWorkflow(
   211  		"test-activity-retry-on-start2close-timeout",
   212  		ts.workflows.ActivityRetryOnTimeout,
   213  		&expected,
   214  		shared.TimeoutTypeStartToClose)
   215  
   216  	ts.NoError(err)
   217  	ts.EqualValues(expected, ts.activities.invoked())
   218  }
   219  
   220  func (ts *IntegrationTestSuite) TestActivityRetryOnHBTimeout() {
   221  	var expected []string
   222  	err := ts.executeWorkflow("test-activity-retry-on-hbtimeout", ts.workflows.ActivityRetryOnHBTimeout, &expected)
   223  	ts.NoError(err)
   224  	ts.EqualValues(expected, ts.activities.invoked())
   225  }
   226  
   227  func (ts *IntegrationTestSuite) TestActivityAutoHeartbeat() {
   228  	var expected []string
   229  	err := ts.executeWorkflow("test-activity-auto-heartbeat", ts.workflows.ActivityAutoHeartbeat, &expected)
   230  	ts.NoError(err)
   231  	ts.EqualValues(expected, ts.activities.invoked())
   232  }
   233  
   234  func (ts *IntegrationTestSuite) TestContinueAsNew() {
   235  	var result int
   236  	err := ts.executeWorkflow("test-continueasnew", ts.workflows.ContinueAsNew, &result, 4, ts.taskListName)
   237  	ts.NoError(err)
   238  	ts.Equal(999, result)
   239  }
   240  
   241  func (ts *IntegrationTestSuite) TestContinueAsNewCarryOver() {
   242  	var result string
   243  	startOptions := ts.startWorkflowOptions("test-continueasnew-carryover")
   244  	startOptions.Memo = map[string]interface{}{
   245  		"memoKey": "memoVal",
   246  	}
   247  	startOptions.SearchAttributes = map[string]interface{}{
   248  		"CustomKeywordField": "searchAttr",
   249  	}
   250  	err := ts.executeWorkflowWithOption(startOptions, ts.workflows.ContinueAsNewWithOptions, &result, 4, ts.taskListName)
   251  	ts.NoError(err)
   252  	ts.Equal("memoVal,searchAttr", result)
   253  }
   254  
   255  func (ts *IntegrationTestSuite) TestCancellation() {
   256  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   257  	defer cancel()
   258  	run, err := ts.libClient.ExecuteWorkflow(ctx,
   259  		ts.startWorkflowOptions("test-cancellation"), ts.workflows.Basic)
   260  	ts.NoError(err)
   261  	ts.NotNil(run)
   262  	ts.Nil(ts.libClient.CancelWorkflow(ctx, "test-cancellation", run.GetRunID()))
   263  	err = run.Get(ctx, nil)
   264  	ts.Error(err)
   265  	_, ok := err.(*cadence.CanceledError)
   266  	ts.True(ok)
   267  	ts.Truef(client.IsWorkflowError(err), "err from canceled workflows should be a workflow error: %#v", err)
   268  }
   269  
   270  func (ts *IntegrationTestSuite) TestStackTraceQuery() {
   271  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   272  	defer cancel()
   273  	run, err := ts.libClient.ExecuteWorkflow(ctx,
   274  		ts.startWorkflowOptions("test-stack-trace-query"), ts.workflows.Basic)
   275  	ts.NoError(err)
   276  	value, err := ts.libClient.QueryWorkflow(ctx, "test-stack-trace-query", run.GetRunID(), "__stack_trace")
   277  	ts.NoError(err)
   278  	ts.NotNil(value)
   279  	var trace string
   280  	ts.NoError(value.Get(&trace))
   281  	ts.True(strings.Contains(trace, "go.uber.org/cadence/test.(*Workflows).Basic"))
   282  }
   283  
   284  func (ts *IntegrationTestSuite) TestConsistentQuery() {
   285  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   286  	defer cancel()
   287  	// this workflow will start a local activity which blocks for long enough
   288  	// to ensure that consistent query must wait in order to satisfy consistency
   289  	wfOpts := ts.startWorkflowOptions("test-consistent-query")
   290  	wfOpts.DecisionTaskStartToCloseTimeout = 5 * time.Second
   291  	run, err := ts.libClient.ExecuteWorkflow(ctx, wfOpts, ts.workflows.ConsistentQueryWorkflow, 3*time.Second)
   292  	ts.Nil(err)
   293  	// Wait for a second to ensure that first decision task gets started and completed before we send signal.
   294  	// Query cannot be run until first decision task has been completed.
   295  	// If signal occurs right after workflow start then WorkflowStarted and Signal events will both be part of the same
   296  	// decision task. So query will be blocked waiting for signal to complete, this is not what we want because it
   297  	// will not exercise the consistent query code path.
   298  	<-time.After(time.Second)
   299  	err = ts.libClient.SignalWorkflow(ctx, "test-consistent-query", run.GetRunID(), consistentQuerySignalCh, "signal-input")
   300  	ts.NoError(err)
   301  
   302  	value, err := ts.libClient.QueryWorkflowWithOptions(ctx, &client.QueryWorkflowWithOptionsRequest{
   303  		WorkflowID:            "test-consistent-query",
   304  		RunID:                 run.GetRunID(),
   305  		QueryType:             "consistent_query",
   306  		QueryConsistencyLevel: shared.QueryConsistencyLevelStrong.Ptr(),
   307  	})
   308  	ts.Nil(err)
   309  	ts.NotNil(value)
   310  	ts.NotNil(value.QueryResult)
   311  	ts.Nil(value.QueryRejected)
   312  	var queryResult string
   313  	ts.NoError(value.QueryResult.Get(&queryResult))
   314  	ts.Equal("signal-input", queryResult)
   315  }
   316  
   317  func (ts *IntegrationTestSuite) TestWorkflowIDReuseRejectDuplicate() {
   318  	var result string
   319  	err := ts.executeWorkflow(
   320  		"test-workflowidreuse-reject-duplicate",
   321  		ts.workflows.IDReusePolicy,
   322  		&result,
   323  		uuid.New(),
   324  		client.WorkflowIDReusePolicyRejectDuplicate,
   325  		false,
   326  		false,
   327  	)
   328  	ts.Error(err)
   329  	gerr, ok := err.(*workflow.GenericError)
   330  	ts.True(ok)
   331  	ts.True(strings.Contains(gerr.Error(), "WorkflowExecutionAlreadyStartedError"))
   332  	ts.Truef(client.IsWorkflowError(err), "already-started child error should be a workflow error: %#v", err)
   333  }
   334  
   335  func (ts *IntegrationTestSuite) TestWorkflowIDReuseAllowDuplicateFailedOnly1() {
   336  	var result string
   337  	err := ts.executeWorkflow(
   338  		"test-workflowidreuse-reject-duplicate-failed-only1",
   339  		ts.workflows.IDReusePolicy,
   340  		&result,
   341  		uuid.New(),
   342  		client.WorkflowIDReusePolicyAllowDuplicateFailedOnly,
   343  		false,
   344  		false,
   345  	)
   346  	ts.Error(err)
   347  	gerr, ok := err.(*workflow.GenericError)
   348  	ts.True(ok)
   349  	ts.True(strings.Contains(gerr.Error(), "WorkflowExecutionAlreadyStartedError"))
   350  	ts.Truef(client.IsWorkflowError(err), "already-started child error should be a workflow error: %#v", err)
   351  }
   352  
   353  func (ts *IntegrationTestSuite) TestWorkflowIDReuseAllowDuplicateFailedOnly2() {
   354  	var result string
   355  	err := ts.executeWorkflow(
   356  		"test-workflowidreuse-reject-duplicate-failed-only2",
   357  		ts.workflows.IDReusePolicy,
   358  		&result,
   359  		uuid.New(),
   360  		client.WorkflowIDReusePolicyAllowDuplicateFailedOnly,
   361  		false,
   362  		true,
   363  	)
   364  	ts.NoError(err)
   365  	ts.Equal("WORLD", result)
   366  }
   367  
   368  func (ts *IntegrationTestSuite) TestWorkflowIDReuseAllowDuplicate() {
   369  	var result string
   370  	err := ts.executeWorkflow(
   371  		"test-workflowidreuse-allow-duplicate",
   372  		ts.workflows.IDReusePolicy,
   373  		&result,
   374  		uuid.New(),
   375  		client.WorkflowIDReusePolicyAllowDuplicate,
   376  		false,
   377  		false,
   378  	)
   379  	ts.NoError(err)
   380  	ts.Equal("HELLOWORLD", result)
   381  }
   382  
   383  func (ts *IntegrationTestSuite) TestWorkflowIDReuseErrorViaStartWorkflow() {
   384  	duplicatedWID := "test-workflowidreuse-duplicate-start-error"
   385  	// setup: run any workflow once to consume the ID
   386  	err := ts.executeWorkflow(
   387  		duplicatedWID,
   388  		ts.workflows.SimplestWorkflow,
   389  		nil,
   390  	)
   391  	ts.NoError(err, "basic workflow should succeed")
   392  
   393  	// a second attempt should fail
   394  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   395  	defer cancel()
   396  	opts := ts.startWorkflowOptions(duplicatedWID)
   397  	opts.WorkflowIDReusePolicy = client.WorkflowIDReusePolicyRejectDuplicate
   398  	exec, err := ts.libClient.StartWorkflow(ctx, opts, ts.workflows.SimplestWorkflow)
   399  	ts.Nil(exec)
   400  	ts.Error(err)
   401  	ts.IsType(&shared.WorkflowExecutionAlreadyStartedError{}, err, "should be the known already-started error type")
   402  	ts.False(client.IsWorkflowError(err), "start-workflow rejected errors should not be workflow errors")
   403  }
   404  
   405  func (ts *IntegrationTestSuite) TestChildWFRetryOnError() {
   406  	err := ts.executeWorkflow("test-childwf-retry-on-error", ts.workflows.ChildWorkflowRetryOnError, nil)
   407  	ts.Error(err)
   408  	ts.Truef(client.IsWorkflowError(err), "child error should be a workflow error: %#v", err)
   409  	ts.EqualValues([]string{"toUpper", "toUpper", "toUpper"}, ts.activities.invoked())
   410  }
   411  
   412  func (ts *IntegrationTestSuite) TestChildWFRetryOnTimeout() {
   413  	err := ts.executeWorkflow("test-childwf-retry-on-timeout", ts.workflows.ChildWorkflowRetryOnTimeout, nil)
   414  	ts.Error(err)
   415  	ts.Truef(client.IsWorkflowError(err), "child-timeout error should be a workflow error: %#v", err)
   416  	ts.EqualValues([]string{"sleep", "sleep", "sleep"}, ts.activities.invoked())
   417  }
   418  
   419  func (ts *IntegrationTestSuite) TestChildWFWithMemoAndSearchAttributes() {
   420  	var result string
   421  	err := ts.executeWorkflow("test-childwf-success-memo-searchAttr", ts.workflows.ChildWorkflowSuccess, &result)
   422  	ts.NoError(err)
   423  	ts.EqualValues([]string{"getMemoAndSearchAttr"}, ts.activities.invoked())
   424  	ts.Equal("memoVal, searchAttrVal", result)
   425  	ts.Equal([]string{"ExecuteWorkflow begin", "ExecuteChildWorkflow", "ExecuteWorkflow end"},
   426  		ts.tracer.GetTrace("go.uber.org/cadence/test.(*Workflows).ChildWorkflowSuccess"))
   427  }
   428  
   429  func (ts *IntegrationTestSuite) TestChildWFWithParentClosePolicyTerminate() {
   430  	var childWorkflowID string
   431  	err := ts.executeWorkflow("test-childwf-parent-close-policy", ts.workflows.ChildWorkflowSuccessWithParentClosePolicyTerminate, &childWorkflowID)
   432  	ts.NoError(err)
   433  	// Need to wait for child workflow to finish as well otherwise test becomes flaky
   434  	ts.waitForWorkflowFinish(childWorkflowID, "")
   435  	resp, err := ts.libClient.DescribeWorkflowExecution(context.Background(), childWorkflowID, "")
   436  	ts.NoError(err)
   437  	ts.True(resp.WorkflowExecutionInfo.GetCloseTime() > 0)
   438  }
   439  
   440  func (ts *IntegrationTestSuite) TestChildWFWithParentClosePolicyAbandon() {
   441  	var childWorkflowID string
   442  	err := ts.executeWorkflow("test-childwf-parent-close-policy", ts.workflows.ChildWorkflowSuccessWithParentClosePolicyAbandon, &childWorkflowID)
   443  	ts.NoError(err)
   444  	resp, err := ts.libClient.DescribeWorkflowExecution(context.Background(), childWorkflowID, "")
   445  	ts.NoError(err)
   446  	ts.Zerof(resp.WorkflowExecutionInfo.GetCloseTime(), "Expected close time to be zero, got %d. Describe response: %#v", resp.WorkflowExecutionInfo.GetCloseTime(), resp)
   447  }
   448  
   449  func (ts *IntegrationTestSuite) TestChildWFCancel() {
   450  	var childWorkflowID string
   451  	err := ts.executeWorkflow("test-childwf-cancel", ts.workflows.ChildWorkflowCancel, &childWorkflowID)
   452  	ts.NoError(err)
   453  	resp, err := ts.libClient.DescribeWorkflowExecution(context.Background(), childWorkflowID, "")
   454  	ts.NoError(err)
   455  	ts.Equal(shared.WorkflowExecutionCloseStatusCanceled, resp.WorkflowExecutionInfo.GetCloseStatus())
   456  }
   457  
   458  func (ts *IntegrationTestSuite) TestActivityCancelUsingReplay() {
   459  	replayer := worker.NewWorkflowReplayer()
   460  	replayer.RegisterWorkflowWithOptions(ts.workflows.ActivityCancelRepro, workflow.RegisterOptions{DisableAlreadyRegisteredCheck: true})
   461  	err := replayer.ReplayPartialWorkflowHistoryFromJSONFile(zaptest.NewLogger(ts.T()), "fixtures/activity.cancel.sm.repro.json", 12)
   462  	ts.NoError(err)
   463  }
   464  
   465  func (ts *IntegrationTestSuite) TestActivityCancelRepro() {
   466  	var expected []string
   467  	err := ts.executeWorkflow("test-activity-cancel-sm", ts.workflows.ActivityCancelRepro, &expected)
   468  	ts.NoError(err)
   469  	ts.EqualValues(expected, ts.activities.invoked())
   470  }
   471  
   472  func (ts *IntegrationTestSuite) TestWorkflowWithLocalActivityCtxPropagation() {
   473  	var expected string
   474  	err := ts.executeWorkflow("test-wf-local-activity-ctx-prop", ts.workflows.WorkflowWithLocalActivityCtxPropagation, &expected)
   475  	ts.NoError(err)
   476  	ts.EqualValues(expected, "test-data-in-contexttest-data-in-context")
   477  }
   478  
   479  func (ts *IntegrationTestSuite) TestLargeQueryResultError() {
   480  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   481  	defer cancel()
   482  	run, err := ts.libClient.ExecuteWorkflow(ctx,
   483  		ts.startWorkflowOptions("test-large-query-error"), ts.workflows.LargeQueryResultWorkflow)
   484  	ts.Nil(err)
   485  	value, err := ts.libClient.QueryWorkflow(ctx, "test-large-query-error", run.GetRunID(), "large_query")
   486  	ts.Error(err)
   487  	ts.False(client.IsWorkflowError(err), "query errors should not be workflow errors, as they are request-related")
   488  
   489  	queryErr, ok := err.(*shared.QueryFailedError)
   490  	ts.True(ok)
   491  	ts.Equal("query result size (3000000) exceeds limit (2000000)", queryErr.Message)
   492  	ts.Nil(value)
   493  }
   494  
   495  func (ts *IntegrationTestSuite) TestInspectActivityInfo() {
   496  	err := ts.executeWorkflow("test-activity-info", ts.workflows.InspectActivityInfo, nil)
   497  	ts.Nil(err)
   498  }
   499  
   500  func (ts *IntegrationTestSuite) TestInspectLocalActivityInfo() {
   501  	err := ts.executeWorkflow("test-local-activity-info", ts.workflows.InspectLocalActivityInfo, nil)
   502  	ts.Nil(err)
   503  }
   504  
   505  func (ts *IntegrationTestSuite) TestDomainUpdate() {
   506  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   507  	defer cancel()
   508  	name := domainName
   509  	description := "test-description"
   510  	err := ts.domainClient.Update(ctx, &shared.UpdateDomainRequest{
   511  		Name:        &name,
   512  		UpdatedInfo: &shared.UpdateDomainInfo{Description: &description},
   513  	})
   514  	ts.NoError(err)
   515  
   516  	domain, err := ts.domainClient.Describe(ctx, name)
   517  	ts.NoError(err)
   518  	ts.Equal(description, *domain.DomainInfo.Description)
   519  }
   520  
   521  func (ts *IntegrationTestSuite) TestNonDeterministicWorkflowFailPolicy() {
   522  	err := ts.executeWorkflow("test-nondeterminism-failpolicy", ts.workflows.NonDeterminismSimulatorWorkflow, nil)
   523  	var customErr *internal.CustomError
   524  	ok := errors.As(err, &customErr)
   525  	ts.Truef(ok, "expected CustomError but got %T", err)
   526  	ts.Equal("NonDeterministicWorkflowPolicyFailWorkflow", customErr.Reason())
   527  }
   528  
   529  func (ts *IntegrationTestSuite) TestNonDeterministicWorkflowQuery() {
   530  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   531  	defer cancel()
   532  	run, err := ts.libClient.ExecuteWorkflow(ctx, ts.startWorkflowOptions("test-nondeterministic-query"), ts.workflows.NonDeterminismSimulatorWorkflow)
   533  	ts.Nil(err)
   534  	err = run.Get(ctx, nil)
   535  	var customErr *internal.CustomError
   536  	ok := errors.As(err, &customErr)
   537  	ts.Truef(ok, "expected CustomError but got %T", err)
   538  	ts.Equal("NonDeterministicWorkflowPolicyFailWorkflow", customErr.Reason())
   539  
   540  	// query failed workflow should still work
   541  	value, err := ts.libClient.QueryWorkflow(ctx, "test-nondeterministic-query", run.GetRunID(), "__stack_trace")
   542  	ts.NoError(err)
   543  	ts.NotNil(value)
   544  	var trace string
   545  	ts.NoError(value.Get(&trace))
   546  }
   547  
   548  func (ts *IntegrationTestSuite) registerDomain() {
   549  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   550  	defer cancel()
   551  	name := domainName
   552  	retention := int32(1)
   553  	err := ts.domainClient.Register(ctx, &shared.RegisterDomainRequest{
   554  		Name:                                   &name,
   555  		WorkflowExecutionRetentionPeriodInDays: &retention,
   556  	})
   557  	if err != nil {
   558  		if _, ok := err.(*shared.DomainAlreadyExistsError); ok {
   559  			return
   560  		}
   561  	}
   562  	ts.NoError(err)
   563  	time.Sleep(domainCacheRefreshInterval) // wait for domain cache refresh on cadence-server
   564  	// bellow is used to guarantee domain is ready
   565  	var dummyReturn string
   566  	err = ts.executeWorkflow("test-domain-exist", ts.workflows.SimplestWorkflow, &dummyReturn)
   567  	numOfRetry := 20
   568  	for err != nil && numOfRetry >= 0 {
   569  		if _, ok := err.(*shared.EntityNotExistsError); ok {
   570  			time.Sleep(domainCacheRefreshInterval)
   571  			err = ts.executeWorkflow("test-domain-exist", ts.workflows.SimplestWorkflow, &dummyReturn)
   572  		} else {
   573  			break
   574  		}
   575  		numOfRetry--
   576  	}
   577  }
   578  
   579  // executeWorkflow executes a given workflow and waits for the result
   580  func (ts *IntegrationTestSuite) executeWorkflow(
   581  	wfID string, wfFunc interface{}, retValPtr interface{}, args ...interface{}) error {
   582  	options := ts.startWorkflowOptions(wfID)
   583  	return ts.executeWorkflowWithOption(options, wfFunc, retValPtr, args...)
   584  }
   585  
   586  func (ts *IntegrationTestSuite) executeWorkflowWithOption(
   587  	options client.StartWorkflowOptions, wfFunc interface{}, retValPtr interface{}, args ...interface{}) error {
   588  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   589  	defer cancel()
   590  	run, err := ts.libClient.ExecuteWorkflow(ctx, options, wfFunc, args...)
   591  	if err != nil {
   592  		return err
   593  	}
   594  	err = run.Get(ctx, retValPtr)
   595  	logger := zaptest.NewLogger(ts.T())
   596  	if ts.config.Debug {
   597  		iter := ts.libClient.GetWorkflowHistory(ctx, options.ID, run.GetRunID(), false, shared.HistoryEventFilterTypeAllEvent)
   598  		for iter.HasNext() {
   599  			event, err1 := iter.Next()
   600  			if err1 != nil {
   601  				break
   602  			}
   603  			logger.Info(event.String())
   604  		}
   605  	}
   606  	return err
   607  }
   608  
   609  func (ts *IntegrationTestSuite) startWorkflowOptions(wfID string) client.StartWorkflowOptions {
   610  	return client.StartWorkflowOptions{
   611  		ID:                              wfID,
   612  		TaskList:                        ts.taskListName,
   613  		ExecutionStartToCloseTimeout:    15 * time.Second,
   614  		DecisionTaskStartToCloseTimeout: time.Second,
   615  		WorkflowIDReusePolicy:           client.WorkflowIDReusePolicyAllowDuplicate,
   616  	}
   617  }
   618  
   619  func (ts *IntegrationTestSuite) registerWorkflowsAndActivities(w worker.Worker) {
   620  	ts.workflows.register(w)
   621  	ts.activities.register(w)
   622  }
   623  
   624  func (ts *IntegrationTestSuite) waitForWorkflowFinish(wid string, runId string) error {
   625  	ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
   626  	defer cancel()
   627  	wfRun := ts.libClient.GetWorkflow(ctx, wid, runId)
   628  	return wfRun.Get(ctx, nil)
   629  }
   630  
   631  var _ interceptors.WorkflowInterceptorFactory = (*tracingInterceptorFactory)(nil)
   632  
   633  type tracingInterceptorFactory struct {
   634  	sync.Mutex
   635  	// key is workflow id
   636  	instances map[string]*tracingInterceptor
   637  }
   638  
   639  func newtracingInterceptorFactory() *tracingInterceptorFactory {
   640  	return &tracingInterceptorFactory{instances: make(map[string]*tracingInterceptor)}
   641  }
   642  
   643  func (t *tracingInterceptorFactory) GetTrace(workflowType string) []string {
   644  	t.Mutex.Lock()
   645  	defer t.Mutex.Unlock()
   646  	if i, ok := t.instances[workflowType]; ok {
   647  		return i.trace
   648  	}
   649  	panic(fmt.Sprintf("Unknown workflowType %v, known types: %v", workflowType, t.instances))
   650  }
   651  func (t *tracingInterceptorFactory) NewInterceptor(info *workflow.Info, next interceptors.WorkflowInterceptor) interceptors.WorkflowInterceptor {
   652  	t.Mutex.Lock()
   653  	defer t.Mutex.Unlock()
   654  	result := &tracingInterceptor{
   655  		WorkflowInterceptorBase: interceptors.WorkflowInterceptorBase{Next: next},
   656  	}
   657  	t.instances[info.WorkflowType.Name] = result
   658  	return result
   659  }
   660  
   661  var _ interceptors.WorkflowInterceptor = (*tracingInterceptor)(nil)
   662  
   663  type tracingInterceptor struct {
   664  	interceptors.WorkflowInterceptorBase
   665  	trace []string
   666  }
   667  
   668  func (t *tracingInterceptor) ExecuteActivity(ctx workflow.Context, activityType string, args ...interface{}) workflow.Future {
   669  	t.trace = append(t.trace, "ExecuteActivity")
   670  	return t.Next.ExecuteActivity(ctx, activityType, args...)
   671  }
   672  
   673  func (t *tracingInterceptor) ExecuteChildWorkflow(ctx workflow.Context, childWorkflowType string, args ...interface{}) workflow.ChildWorkflowFuture {
   674  	t.trace = append(t.trace, "ExecuteChildWorkflow")
   675  	return t.Next.ExecuteChildWorkflow(ctx, childWorkflowType, args...)
   676  }
   677  
   678  func (t *tracingInterceptor) ExecuteWorkflow(ctx workflow.Context, workflowType string, args ...interface{}) []interface{} {
   679  	t.trace = append(t.trace, "ExecuteWorkflow begin")
   680  	result := t.Next.ExecuteWorkflow(ctx, workflowType, args...)
   681  	t.trace = append(t.trace, "ExecuteWorkflow end")
   682  	return result
   683  }