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 }