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

     1  // Copyright (c) 2017 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  
    21  package internal
    22  
    23  import (
    24  	"encoding/json"
    25  	"go.uber.org/cadence/internal/common"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  	"go.uber.org/zap"
    31  	"go.uber.org/zap/zapcore"
    32  	"go.uber.org/zap/zaptest/observer"
    33  
    34  	s "go.uber.org/cadence/.gen/go/shared"
    35  )
    36  
    37  func TestReplayAwareLogger(t *testing.T) {
    38  	t.Parallel()
    39  	core, observed := observer.New(zapcore.InfoLevel)
    40  	logger := zap.New(core, zap.Development())
    41  
    42  	isReplay, enableLoggingInReplay := false, false
    43  	logger = logger.WithOptions(zap.WrapCore(wrapLogger(&isReplay, &enableLoggingInReplay)))
    44  
    45  	logger.Info("normal info")
    46  
    47  	isReplay = true
    48  	logger.Info("replay info") // this log should be suppressed
    49  
    50  	isReplay, enableLoggingInReplay = false, true
    51  	logger.Info("normal2 info")
    52  
    53  	isReplay = true
    54  	logger.Info("replay2 info")
    55  
    56  	var messages []string
    57  	for _, log := range observed.AllUntimed() {
    58  		messages = append(messages, log.Message)
    59  	}
    60  	assert.Len(t, messages, 3) // ensures "replay info" wasn't just misspelled
    61  	assert.Contains(t, messages, "normal info")
    62  	assert.NotContains(t, messages, "replay info")
    63  	assert.Contains(t, messages, "normal2 info")
    64  	assert.Contains(t, messages, "replay2 info")
    65  }
    66  
    67  func testDecodeValueHelper(t *testing.T, env *workflowEnvironmentImpl) {
    68  	equals := func(a, b interface{}) bool {
    69  		ao := a.(ActivityOptions)
    70  		bo := b.(ActivityOptions)
    71  		return ao.TaskList == bo.TaskList
    72  	}
    73  	value := ActivityOptions{TaskList: "test-tasklist"}
    74  	blob := env.encodeValue(value)
    75  	isEqual := env.isEqualValue(value, blob, equals)
    76  	require.True(t, isEqual)
    77  
    78  	value.TaskList = "value-changed"
    79  	isEqual = env.isEqualValue(value, blob, equals)
    80  	require.False(t, isEqual)
    81  }
    82  
    83  func TestDecodedValue(t *testing.T) {
    84  	t.Parallel()
    85  	env := &workflowEnvironmentImpl{
    86  		dataConverter: getDefaultDataConverter(),
    87  	}
    88  	testDecodeValueHelper(t, env)
    89  }
    90  
    91  func TestDecodedValue_WithDataConverter(t *testing.T) {
    92  	t.Parallel()
    93  	env := &workflowEnvironmentImpl{
    94  		dataConverter: newTestDataConverter(),
    95  	}
    96  	testDecodeValueHelper(t, env)
    97  }
    98  
    99  func Test_DecodedValuePtr(t *testing.T) {
   100  	t.Parallel()
   101  	env := &workflowEnvironmentImpl{
   102  		dataConverter: getDefaultDataConverter(),
   103  	}
   104  	equals := func(a, b interface{}) bool {
   105  		ao := a.(*ActivityOptions)
   106  		bo := b.(*ActivityOptions)
   107  		return ao.TaskList == bo.TaskList
   108  	}
   109  	value := &ActivityOptions{TaskList: "test-tasklist"}
   110  	blob := env.encodeValue(value)
   111  	isEqual := env.isEqualValue(value, blob, equals)
   112  	require.True(t, isEqual)
   113  
   114  	value.TaskList = "value-changed"
   115  	isEqual = env.isEqualValue(value, blob, equals)
   116  	require.False(t, isEqual)
   117  }
   118  
   119  func Test_DecodedValueNil(t *testing.T) {
   120  	t.Parallel()
   121  	env := &workflowEnvironmentImpl{
   122  		dataConverter: getDefaultDataConverter(),
   123  	}
   124  	equals := func(a, b interface{}) bool {
   125  		return a == nil && b == nil
   126  	}
   127  	// newValue is nil, old value is nil
   128  	var value interface{}
   129  	blob := env.encodeValue(value)
   130  	isEqual := env.isEqualValue(value, blob, equals)
   131  	require.True(t, isEqual)
   132  
   133  	// newValue is nil, oldValue is not nil
   134  	blob = env.encodeValue("any-non-nil-value")
   135  	isEqual = env.isEqualValue(value, blob, equals)
   136  	require.False(t, isEqual)
   137  
   138  	// newValue is not nil, oldValue is nil
   139  	blob = env.encodeValue(nil)
   140  	isEqual = env.isEqualValue("non-nil-value", blob, equals)
   141  	require.False(t, isEqual)
   142  }
   143  
   144  func Test_ValidateAndSerializeSearchAttributes(t *testing.T) {
   145  	t.Parallel()
   146  	_, err := validateAndSerializeSearchAttributes(nil)
   147  	require.EqualError(t, err, "search attributes is empty")
   148  
   149  	attr := map[string]interface{}{
   150  		"JustKey": make(chan int),
   151  	}
   152  	_, err = validateAndSerializeSearchAttributes(attr)
   153  	require.EqualError(t, err, "encode search attribute [JustKey] error: json: unsupported type: chan int")
   154  
   155  	attr = map[string]interface{}{
   156  		"key": 1,
   157  	}
   158  	searchAttr, err := validateAndSerializeSearchAttributes(attr)
   159  	require.NoError(t, err)
   160  	require.Equal(t, 1, len(searchAttr.IndexedFields))
   161  	var resp int
   162  	json.Unmarshal(searchAttr.IndexedFields["key"], &resp)
   163  	require.Equal(t, 1, resp)
   164  }
   165  
   166  func Test_UpsertSearchAttributes(t *testing.T) {
   167  	t.Parallel()
   168  	env := &workflowEnvironmentImpl{
   169  		decisionsHelper: newDecisionsHelper(),
   170  		workflowInfo:    GetWorkflowInfo(createRootTestContext(t)),
   171  	}
   172  	err := env.UpsertSearchAttributes(nil)
   173  	require.Error(t, err)
   174  
   175  	err = env.UpsertSearchAttributes(map[string]interface{}{
   176  		CadenceChangeVersion: []string{"change2-1", "change1-1"}},
   177  	)
   178  	require.NoError(t, err)
   179  	_, ok := env.decisionsHelper.decisions[makeDecisionID(decisionTypeUpsertSearchAttributes, "change2-1")]
   180  	require.True(t, ok)
   181  	require.Equal(t, int32(0), env.counterID)
   182  
   183  	err = env.UpsertSearchAttributes(map[string]interface{}{"key": 1})
   184  	require.NoError(t, err)
   185  	require.Equal(t, int32(1), env.counterID)
   186  }
   187  
   188  func Test_MergeSearchAttributes(t *testing.T) {
   189  	t.Parallel()
   190  	tests := []struct {
   191  		name     string
   192  		current  *s.SearchAttributes
   193  		upsert   *s.SearchAttributes
   194  		expected *s.SearchAttributes
   195  	}{
   196  		{
   197  			name:     "currentIsNil",
   198  			current:  nil,
   199  			upsert:   &s.SearchAttributes{},
   200  			expected: nil,
   201  		},
   202  		{
   203  			name:     "currentIsEmpty",
   204  			current:  &s.SearchAttributes{IndexedFields: make(map[string][]byte)},
   205  			upsert:   &s.SearchAttributes{},
   206  			expected: nil,
   207  		},
   208  		{
   209  			name: "normalMerge",
   210  			current: &s.SearchAttributes{
   211  				IndexedFields: map[string][]byte{
   212  					"CustomIntField":     []byte(`1`),
   213  					"CustomKeywordField": []byte(`keyword`),
   214  				},
   215  			},
   216  			upsert: &s.SearchAttributes{
   217  				IndexedFields: map[string][]byte{
   218  					"CustomIntField":  []byte(`2`),
   219  					"CustomBoolField": []byte(`true`),
   220  				},
   221  			},
   222  			expected: &s.SearchAttributes{
   223  				IndexedFields: map[string][]byte{
   224  					"CustomIntField":     []byte(`2`),
   225  					"CustomKeywordField": []byte(`keyword`),
   226  					"CustomBoolField":    []byte(`true`),
   227  				},
   228  			},
   229  		},
   230  	}
   231  
   232  	for _, test := range tests {
   233  		test := test
   234  		t.Run(test.name, func(t *testing.T) {
   235  			t.Parallel()
   236  			result := mergeSearchAttributes(test.current, test.upsert)
   237  			require.Equal(t, test.expected, result)
   238  		})
   239  	}
   240  }
   241  
   242  func Test_GetChangeVersion(t *testing.T) {
   243  	t.Parallel()
   244  	tests := []struct {
   245  		name     string
   246  		changeID string
   247  		version  Version
   248  		expected string
   249  	}{
   250  		{
   251  			name:     "default",
   252  			changeID: "cid",
   253  			version:  DefaultVersion,
   254  			expected: "cid--1",
   255  		},
   256  		{
   257  			name:     "normal_case",
   258  			changeID: "cid",
   259  			version:  1,
   260  			expected: "cid-1",
   261  		},
   262  	}
   263  	for _, test := range tests {
   264  		test := test
   265  		t.Run(test.name, func(t *testing.T) {
   266  			t.Parallel()
   267  			result := getChangeVersion(test.changeID, test.version)
   268  			require.Equal(t, test.expected, result)
   269  		})
   270  	}
   271  }
   272  
   273  func Test_GetChangeVersions(t *testing.T) {
   274  	t.Parallel()
   275  	tests := []struct {
   276  		name                   string
   277  		changeID               string
   278  		version                Version
   279  		existingChangeVersions map[string]Version
   280  		expected               []string
   281  	}{
   282  		{
   283  			name:                   "single_change_id",
   284  			changeID:               "cid",
   285  			version:                1,
   286  			existingChangeVersions: map[string]Version{},
   287  			expected:               []string{"cid-1"},
   288  		},
   289  		{
   290  			name:     "multi_change_ids",
   291  			changeID: "cid2",
   292  			version:  1,
   293  			existingChangeVersions: map[string]Version{
   294  				"cid": 1,
   295  			},
   296  			expected: []string{"cid2-1", "cid-1"},
   297  		},
   298  	}
   299  	for _, test := range tests {
   300  		test := test
   301  		t.Run(test.name, func(t *testing.T) {
   302  			t.Parallel()
   303  			result := getChangeVersions(test.changeID, test.version, test.existingChangeVersions)
   304  			require.Equal(t, test.expected, result)
   305  		})
   306  	}
   307  }
   308  
   309  func Test_CreateSearchAttributesForChangeVersion(t *testing.T) {
   310  	t.Parallel()
   311  	result := createSearchAttributesForChangeVersion("cid", 1, map[string]Version{})
   312  	val, ok := result["CadenceChangeVersion"]
   313  	require.True(t, ok, "Remember to update related key on server side")
   314  	require.Equal(t, []string{"cid-1"}, val)
   315  }
   316  
   317  func TestHistoryEstimationforSmallEvents(t *testing.T) {
   318  	taskList := "tasklist"
   319  	testEvents := []*s.HistoryEvent{
   320  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   321  		createTestEventDecisionTaskScheduled(2, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   322  		createTestEventDecisionTaskStarted(3),
   323  		{
   324  			EventId:   common.Int64Ptr(4),
   325  			EventType: common.EventTypePtr(s.EventTypeDecisionTaskFailed),
   326  		},
   327  		{
   328  			EventId:   common.Int64Ptr(5),
   329  			EventType: common.EventTypePtr(s.EventTypeWorkflowExecutionSignaled),
   330  		},
   331  		createTestEventDecisionTaskScheduled(6, &s.DecisionTaskScheduledEventAttributes{TaskList: &s.TaskList{Name: &taskList}}),
   332  		createTestEventDecisionTaskStarted(7),
   333  	}
   334  	core, _ := observer.New(zapcore.InfoLevel)
   335  	logger := zap.New(core, zap.Development())
   336  	w := workflowExecutionEventHandlerImpl{
   337  		workflowEnvironmentImpl: &workflowEnvironmentImpl{logger: logger},
   338  	}
   339  
   340  	w.logger = logger
   341  	historySizeSum := 0
   342  	for _, event := range testEvents {
   343  		sum := estimateHistorySize(logger, event)
   344  		historySizeSum += sum
   345  	}
   346  	trueSize := len(testEvents) * historySizeEstimationBuffer
   347  
   348  	assert.Equal(t, trueSize, historySizeSum)
   349  }
   350  
   351  func TestHistoryEstimationforPackedEvents(t *testing.T) {
   352  	// create an array of bytes for testing
   353  	var byteArray []byte
   354  	byteArray = append(byteArray, 100)
   355  	taskList := "tasklist"
   356  	testEvents := []*s.HistoryEvent{
   357  		createTestEventWorkflowExecutionStarted(1, &s.WorkflowExecutionStartedEventAttributes{
   358  			TaskList:                &s.TaskList{Name: &taskList},
   359  			Input:                   byteArray,
   360  			ContinuedFailureDetails: byteArray}),
   361  		createTestEventWorkflowExecutionStarted(2, &s.WorkflowExecutionStartedEventAttributes{
   362  			TaskList:                &s.TaskList{Name: &taskList},
   363  			Input:                   byteArray,
   364  			ContinuedFailureDetails: byteArray}),
   365  		createTestEventWorkflowExecutionStarted(3, &s.WorkflowExecutionStartedEventAttributes{
   366  			TaskList:                &s.TaskList{Name: &taskList},
   367  			Input:                   byteArray,
   368  			ContinuedFailureDetails: byteArray}),
   369  	}
   370  	core, _ := observer.New(zapcore.InfoLevel)
   371  	logger := zap.New(core, zap.Development())
   372  	w := workflowExecutionEventHandlerImpl{
   373  		workflowEnvironmentImpl: &workflowEnvironmentImpl{logger: logger},
   374  	}
   375  
   376  	w.logger = logger
   377  	historySizeSum := 0
   378  	for _, event := range testEvents {
   379  		sum := estimateHistorySize(logger, event)
   380  		historySizeSum += sum
   381  	}
   382  	trueSize := len(testEvents)*historySizeEstimationBuffer + len(byteArray)*2*len(testEvents)
   383  	assert.Equal(t, trueSize, historySizeSum)
   384  }
   385  
   386  func TestProcessQuery_KnownQueryTypes(t *testing.T) {
   387  	rootCtx := setWorkflowEnvOptionsIfNotExist(Background())
   388  	eo := getWorkflowEnvOptions(rootCtx)
   389  	eo.queryHandlers["a"] = nil
   390  
   391  	weh := &workflowExecutionEventHandlerImpl{
   392  		workflowEnvironmentImpl: &workflowEnvironmentImpl{
   393  			dataConverter: DefaultDataConverter,
   394  		},
   395  		workflowDefinition: &syncWorkflowDefinition{
   396  			rootCtx: rootCtx,
   397  		},
   398  	}
   399  
   400  	result, err := weh.ProcessQuery(QueryTypeQueryTypes, nil)
   401  	assert.NoError(t, err)
   402  	assert.Equal(t, "[\"__open_sessions\",\"__query_types\",\"__stack_trace\",\"a\"]\n", string(result))
   403  }