-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dedup duplicate update IDs for test environment #1695
base: master
Are you sure you want to change the base?
Changes from 6 commits
7881da0
2526b8b
d70caf8
47057fe
5639859
d03b73a
fd52f49
16b9ce3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,6 +143,11 @@ type ( | |
taskQueues map[string]struct{} | ||
} | ||
|
||
updateResult struct { | ||
success interface{} | ||
err error | ||
} | ||
|
||
// testWorkflowEnvironmentShared is the shared data between parent workflow and child workflow test environments | ||
testWorkflowEnvironmentShared struct { | ||
locker sync.Mutex | ||
|
@@ -208,6 +213,7 @@ type ( | |
signalHandler func(name string, input *commonpb.Payloads, header *commonpb.Header) error | ||
queryHandler func(string, *commonpb.Payloads, *commonpb.Header) (*commonpb.Payloads, error) | ||
updateHandler func(name string, id string, input *commonpb.Payloads, header *commonpb.Header, resp UpdateCallbacks) | ||
updateMap map[string]updateResult | ||
startedHandler func(r WorkflowExecution, e error) | ||
|
||
isWorkflowCompleted bool | ||
|
@@ -229,6 +235,8 @@ type ( | |
|
||
workflowFunctionExecuting bool | ||
bufferedUpdateRequests map[string][]func() | ||
|
||
currentUpdateId string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if there are multiple concurrent updates sent to the workflow? It looks like those would be lost. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, good point |
||
} | ||
|
||
testSessionEnvironmentImpl struct { | ||
|
@@ -2179,6 +2187,7 @@ func (env *testWorkflowEnvironmentImpl) RegisterUpdateHandler( | |
handler func(name string, id string, input *commonpb.Payloads, header *commonpb.Header, resp UpdateCallbacks), | ||
) { | ||
env.updateHandler = handler | ||
env.updateMap = make(map[string]updateResult) | ||
} | ||
|
||
func (env *testWorkflowEnvironmentImpl) RegisterQueryHandler( | ||
|
@@ -2721,10 +2730,22 @@ func (env *testWorkflowEnvironmentImpl) updateWorkflow(name string, id string, u | |
if err != nil { | ||
panic(err) | ||
} | ||
env.postCallback(func() { | ||
// Do not send any headers on test invocations | ||
env.updateHandler(name, id, data, nil, uc) | ||
}, true) | ||
|
||
// check for duplicate update ID | ||
if _, ok := env.updateMap[id]; ok { | ||
// return cached result | ||
env.postCallback(func() { | ||
uc.Accept() | ||
uc.Complete(env.updateMap[id].success, env.updateMap[id].err) | ||
}, false) | ||
} else { | ||
env.currentUpdateId = id | ||
env.postCallback(func() { | ||
// Do not send any headers on test invocations | ||
env.updateHandler(name, id, data, nil, uc) | ||
}, true) | ||
} | ||
|
||
} | ||
|
||
func (env *testWorkflowEnvironmentImpl) updateWorkflowByID(workflowID, name, id string, uc UpdateCallbacks, args ...interface{}) error { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -280,6 +280,7 @@ func TestWorkflowIDUpdateWorkflowByID(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}, "input") | ||
require.NoError(t, err) | ||
}, time.Second) | ||
|
@@ -322,6 +323,7 @@ func TestChildWorkflowUpdate(t *testing.T) { | |
require.Fail(t, "update failed", err) | ||
} | ||
}, | ||
env: env, | ||
}, nil) | ||
assert.NoError(t, err) | ||
}, time.Second*5) | ||
|
@@ -375,6 +377,7 @@ func TestWorkflowUpdateOrder(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 0) | ||
|
||
|
@@ -415,6 +418,7 @@ func TestWorkflowNotRegisteredRejected(t *testing.T) { | |
require.Fail(t, "update should not be accepted") | ||
}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 0) | ||
|
||
|
@@ -439,6 +443,7 @@ func TestWorkflowUpdateOrderAcceptReject(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 0) | ||
|
||
|
@@ -452,6 +457,7 @@ func TestWorkflowUpdateOrderAcceptReject(t *testing.T) { | |
require.Fail(t, "update should not be rejected") | ||
}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 0) | ||
|
||
|
@@ -462,6 +468,7 @@ func TestWorkflowUpdateOrderAcceptReject(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 0) | ||
|
||
|
@@ -491,6 +498,63 @@ func TestWorkflowUpdateOrderAcceptReject(t *testing.T) { | |
require.Equal(t, "unknown update bad update. KnownUpdates=[update]", updateRejectionErr.Error()) | ||
} | ||
|
||
func TestWorkflowDuplicateIDDedup(t *testing.T) { | ||
var suite WorkflowTestSuite | ||
// Test dev server rejects UpdateWorkflow with same ID | ||
env := suite.NewTestWorkflowEnvironment() | ||
env.RegisterDelayedCallback(func() { | ||
env.UpdateWorkflow("update", "id", &updateCallback{ | ||
reject: func(err error) { | ||
require.Fail(t, fmt.Sprintf("update should not be rejected, err: %v", err)) | ||
}, | ||
accept: func() { | ||
}, | ||
complete: func(result interface{}, err error) { | ||
intResult, ok := result.(int) | ||
if !ok { | ||
require.Fail(t, "result should be int") | ||
} else { | ||
require.Equal(t, 0, intResult) | ||
} | ||
}, | ||
env: env, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current solution requires |
||
}, 0) | ||
}, 0) | ||
|
||
env.RegisterDelayedCallback(func() { | ||
env.UpdateWorkflow("update", "id", &updateCallback{ | ||
reject: func(err error) { | ||
require.Fail(t, fmt.Sprintf("update should not be rejected, err: %v", err)) | ||
}, | ||
accept: func() { | ||
}, | ||
complete: func(result interface{}, err error) { | ||
intResult, ok := result.(int) | ||
if !ok { | ||
require.Fail(t, "result should be int") | ||
} else { | ||
// if dedup, this be okay, even if we pass in 1 as arg, since it's deduping, | ||
// the result should match the first update's result, 0 | ||
require.Equal(t, 0, intResult) | ||
} | ||
}, | ||
env: env, | ||
}, 1) | ||
|
||
}, 1*time.Millisecond) | ||
|
||
env.ExecuteWorkflow(func(ctx Context) error { | ||
err := SetUpdateHandler(ctx, "update", func(ctx Context, i int) (int, error) { | ||
return i, nil | ||
}, UpdateHandlerOptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
return Sleep(ctx, time.Hour) | ||
}) | ||
require.NoError(t, env.GetWorkflowError()) | ||
} | ||
|
||
func TestAllHandlersFinished(t *testing.T) { | ||
var suite WorkflowTestSuite | ||
env := suite.NewTestWorkflowEnvironment() | ||
|
@@ -502,6 +566,7 @@ func TestAllHandlersFinished(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 0) | ||
|
||
|
@@ -512,6 +577,7 @@ func TestAllHandlersFinished(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, time.Minute) | ||
|
||
|
@@ -560,6 +626,7 @@ func TestWorkflowAllHandlersFinished(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 0) | ||
|
||
|
@@ -570,6 +637,7 @@ func TestWorkflowAllHandlersFinished(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, time.Minute) | ||
|
||
|
@@ -580,6 +648,7 @@ func TestWorkflowAllHandlersFinished(t *testing.T) { | |
}, | ||
accept: func() {}, | ||
complete: func(interface{}, error) {}, | ||
env: env, | ||
}) | ||
}, 2*time.Minute) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm so this class is not for users, this whole file is for testing since it is post-fixed with
*_test.go
. The logic can't be in the updateCallback as we take an impl. from user. The logic needs to be in(env *testWorkflowEnvironmentImpl) updateWorkflow
. We could potentially add a wrapper around the user interface. Does that make sense?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
user
in this context referring to the user of the test suite?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And would it be accurate to rephrase this as "we need to implement this within the test suite, not the individual test logic.
updateCallback
is a test specific implementation"?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What interface are you referring to here?
updateWorkflow
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah
yeah, exactly
UpdateCallbacks