@@ -35,6 +35,7 @@ import (
3535 "go.uber.org/cadence/workflow"
3636)
3737
38+ // Basic happy paths
3839func TestReplayWorkflowHistoryFromFile (t * testing.T ) {
3940 for _ , testFile := range []string {"basic.json" , "basic_new.json" , "version.json" , "version_new.json" } {
4041 t .Run ("replay_" + strings .Split (testFile , "." )[0 ], func (t * testing.T ) {
@@ -48,6 +49,7 @@ func TestReplayWorkflowHistoryFromFile(t *testing.T) {
4849 }
4950}
5051
52+ // Child workflow happy path
5153func TestReplayChildWorkflowBugBackport (t * testing.T ) {
5254 replayer := worker .NewWorkflowReplayer ()
5355 replayer .RegisterWorkflowWithOptions (childWorkflow , workflow.RegisterOptions {Name : "child" })
@@ -62,18 +64,18 @@ func TestGreetingsWorkflowforActivity(t *testing.T) {
6264 replayer := worker .NewWorkflowReplayer ()
6365 replayer .RegisterWorkflowWithOptions (greetingsWorkflowActivity , workflow.RegisterOptions {Name : "greetings" })
6466 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "greetings.json" )
65- require . Error (t , err )
67+ assert . ErrorContains (t , err , "nondeterministic workflow: mismatching history event and replay decision found" )
6668}
6769
70+ // Simple greeting workflow with 3 activities executed sequentially: getGreetingsActivity, getNameActivity, sayGreetingsActivity
6871func TestGreetingsWorkflow (t * testing.T ) {
6972 replayer := worker .NewWorkflowReplayer ()
7073 replayer .RegisterWorkflowWithOptions (greetingsWorkflow , workflow.RegisterOptions {Name : "greetings" })
7174 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "greetings.json" )
7275 require .NoError (t , err )
7376}
7477
75- // Should have failed but passed. Maybe, because the result recorded in history still matches the return type of the workflow.
76- // TODO(remove comment): Debug why is this still missed
78+ // Return types of activity change is not considered non-determinism (at least for now) so this test doesn't find non-determinism error
7779func TestGreetingsWorkflow3 (t * testing.T ) {
7880 replayer := worker .NewWorkflowReplayer ()
7981 replayer .RegisterActivityWithOptions (getNameActivity3 , activity.RegisterOptions {Name : "main.getNameActivity" , DisableAlreadyRegisteredCheck : true })
@@ -82,107 +84,100 @@ func TestGreetingsWorkflow3(t *testing.T) {
8284 require .NoError (t , err )
8385}
8486
85- // Fails because the expected signature was different from history.
86- func TestGreetingsWorkflow4 (t * testing.T ) {
87+ // The recorded history has following activities in this order: main.getOrderActivity, main.orderBananaActivity
88+ // This test runs a version of choice workflow which does the exact same thing so no errors expected.
89+ func TestExclusiveChoiceWorkflowSuccess (t * testing.T ) {
8790 replayer := worker .NewWorkflowReplayer ()
88- replayer .RegisterActivityWithOptions (getNameActivity4 , activity.RegisterOptions {Name : "main.getNameActivity" , DisableAlreadyRegisteredCheck : true })
89- replayer .RegisterWorkflowWithOptions (greetingsWorkflow4 , workflow.RegisterOptions {Name : "greetings" })
90- err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "greetings.json" )
91- require .Error (t , err )
91+ replayer .RegisterWorkflowWithOptions (exclusiveChoiceWorkflow , workflow.RegisterOptions {Name : "choice" })
92+ replayer .RegisterActivityWithOptions (getBananaOrderActivity , activity.RegisterOptions {Name : "main.getOrderActivity" })
93+ replayer .RegisterActivityWithOptions (orderBananaActivity , activity.RegisterOptions {Name : "main.orderBananaActivity" })
94+ err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "choice.json" )
95+ require .NoError (t , err )
9296}
9397
94- // Panic with failed to register activity. This passes in cadence_samples because it's registered in Helper.
95- // To test it on cadence_samples change the https://github.com/uber-common/cadence-samples/blob/master/cmd/samples/recipes/greetings/greetings_workflow.go
96- // to include the extra return types in getNameActivity.
97- func TestGreetingsWorkflow2 (t * testing.T ) {
98-
99- t .Skip ("Panic with failed to register activity. Here the activity returns incompatible arguments so the test should fail" )
98+ // The recorded history has following activities in this order: main.getOrderActivity, main.orderBananaActivity
99+ // This test runs a version of choice workflow which does the exact same thing but the activities are not registered.
100+ // It doesn't matter for replayer so no exceptions expected.
101+ // The reason is that activity result decoding logic just passes the result back to the given pointer
102+ func TestExclusiveChoiceWorkflowActivitiyRegistrationMissing (t * testing.T ) {
100103 replayer := worker .NewWorkflowReplayer ()
101- replayer .RegisterActivityWithOptions (getNameActivity2 , activity.RegisterOptions {Name : "main.getNameActivity" , DisableAlreadyRegisteredCheck : true })
102- replayer .RegisterWorkflowWithOptions (greetingsWorkflow2 , workflow.RegisterOptions {Name : "greetings" })
103- err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "greetings.json" )
104- require .Error (t , err )
104+ replayer .RegisterWorkflowWithOptions (exclusiveChoiceWorkflow , workflow.RegisterOptions {Name : "choice" })
105+ err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "choice.json" )
106+ require .NoError (t , err )
105107}
106108
107- // Ideally replayer doesn't concern itself with the change in the activity content until it matches the expected output type.
108- // History has recorded the output of banana activity instead. The replayer should have failed because we have not registered any
109- // activity here in the test.
110- // The replayer still runs whatever it found in the history and passes.
111- func TestExclusiveChoiceWorkflowWithUnregisteredActivity (t * testing.T ) {
109+ // The recorded history has following activities in this order: main.getOrderActivity, main.orderBananaActivity
110+ // This test runs a version of choice workflow which registers a single return parameter function for main.getOrderActivity
111+ // - Original main.getOrderActivity signature: func() (string, error)
112+ // - New main.getOrderActivity signature: func() error
113+ //
114+ // In this case result of main.getOrderActivity from history is not passed back to the given pointer by the workflow.
115+ // Compared to the activity registration missing scenario (above case) this is a little bit weird behavior.
116+ // The workflow code continues with orderChoice="" instead of "banana". Therefore it doesn't invoke 2nd activity main.getOrderActivity.
117+ // This means history has more events then replay decisions which causes non-determinism error
118+ func TestExclusiveChoiceWorkflowWithActivitySignatureChange (t * testing.T ) {
112119 replayer := worker .NewWorkflowReplayer ()
113-
114120 replayer .RegisterWorkflowWithOptions (exclusiveChoiceWorkflow , workflow.RegisterOptions {Name : "choice" })
121+ replayer .RegisterActivityWithOptions (func () error { return nil }, activity.RegisterOptions {Name : "main.getOrderActivity" })
122+ replayer .RegisterActivityWithOptions (orderBananaActivity , activity.RegisterOptions {Name : "main.orderBananaActivity" })
115123 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "choice.json" )
116- require . NoError (t , err )
124+ assert . ErrorContains (t , err , "nondeterministic workflow: missing replay decision" )
117125}
118126
119- // This test registers Cherry Activity as the activity but calls Apple activity in the workflow code. Infact, Cherry and Banana
120- // activities are not even a part of the workflow code in question.
121- // History has recorded the output of banana activity. Here, The workflow is not waiting for the activity so it doesn't notice
122- // that registered activity is different from executed activity.
123- // The replayer relies on whatever is recorded in the History so as long as the main activity name in the options matched partially
124- // it doesn't raise errors.
125- // TODO(remove comment): Replayer now catches this
126- func TestExclusiveChoiceWorkflowWithDifferentActvityCombo (t * testing.T ) {
127+ // The recorded history has following activities in this order: main.getOrderActivity, main.orderBananaActivity
128+ // This test runs a version of choice workflow which calls main.getOrderActivity and then calls the main.orderCherryActivity.
129+ // The replayer will find non-determinism because of mismatch between replay decision and history (banana vs cherry)
130+ func TestExclusiveChoiceWorkflowWithMismatchingActivity (t * testing.T ) {
127131 replayer := worker .NewWorkflowReplayer ()
128-
129- replayer .RegisterWorkflowWithOptions (exclusiveChoiceWorkflow2 , workflow.RegisterOptions {Name : "choice" })
130- replayer .RegisterActivityWithOptions (getAppleOrderActivity , activity.RegisterOptions {Name : "main.getOrderActivity" })
131- replayer .RegisterActivityWithOptions (orderAppleActivity , activity.RegisterOptions {Name : "testactivity" })
132+ replayer .RegisterWorkflowWithOptions (exclusiveChoiceWorkflowAlwaysCherry , workflow.RegisterOptions {Name : "choice" })
133+ replayer .RegisterActivityWithOptions (getBananaOrderActivity , activity.RegisterOptions {Name : "main.getOrderActivity" })
134+ replayer .RegisterActivityWithOptions (orderCherryActivity , activity.RegisterOptions {Name : "main.orderCherryActivity" })
132135 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "choice.json" )
133- assert .ErrorContains (t , err , "nondeterministic workflow" )
136+ assert .ErrorContains (t , err , "nondeterministic workflow: mismatching history event and replay decision found " )
134137}
135138
139+ // Branch workflow happy case.
140+ // It branches out to 3 open activities and then they complete.
136141func TestBranchWorkflow (t * testing.T ) {
137142 replayer := worker .NewWorkflowReplayer ()
138-
139143 replayer .RegisterWorkflowWithOptions (sampleBranchWorkflow , workflow.RegisterOptions {Name : "branch" })
140-
141144 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "branch.json" )
142145 require .NoError (t , err )
143146}
144147
145- // Fails with a non deterministic error because there was an additional unexpected branch. Decreasing the number of branches will
146- // also fail the test because the history expects the same number of branches executing the activity .
148+ // Branch workflow normal history file is replayed against modified workflow code which
149+ // has 2 branches only. This causes nondetereministic error .
147150func TestBranchWorkflowWithExtraBranch (t * testing.T ) {
148151 replayer := worker .NewWorkflowReplayer ()
149-
150152 replayer .RegisterWorkflowWithOptions (sampleBranchWorkflow2 , workflow.RegisterOptions {Name : "branch" })
151-
152153 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "branch.json" )
153- assert .ErrorContains (t , err , "nondeterministic workflow" )
154+ assert .ErrorContains (t , err , "nondeterministic workflow: missing replay decision " )
154155}
155156
156- // TestSequentialStepsWorkflow replays a history with 2 sequential activity calls and runs it against new version of the workflow code which only calls 1 activity.
157- // This should be considered as non-determinism error.
157+ // TestSequentialStepsWorkflow replays a history with 2 sequential non overlapping activity calls (one completes before the other is scheduled)
158+ // and runs it against new version of the workflow code which only calls 1 activity.
159+ // This is considered as non-determinism error.
158160func TestSequentialStepsWorkflow (t * testing.T ) {
159161 replayer := worker .NewWorkflowReplayer ()
160-
161162 replayer .RegisterWorkflowWithOptions (replayerHelloWorldWorkflow , workflow.RegisterOptions {Name : "fx.ReplayerHelloWorldWorkflow" })
162163 replayer .RegisterActivityWithOptions (replayerHelloWorldActivity , activity.RegisterOptions {Name : "replayerhello" })
163-
164- // sequential.json file contains history of a run with 2 activity calls sequentially
165164 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "sequential.json" )
166- assert .ErrorContains (t , err , "nondeterministic workflow" )
165+ assert .ErrorContains (t , err , "nondeterministic workflow: missing replay decision " )
167166}
168167
169- func TestParallel (t * testing.T ) {
168+ // Runs simpleParallelWorkflow which starts two workflow.Go routines that executes 1 and 2 activities respectively.
169+ func TestSimpleParallelWorkflow (t * testing.T ) {
170170 replayer := worker .NewWorkflowReplayer ()
171-
172171 replayer .RegisterWorkflowWithOptions (sampleParallelWorkflow , workflow.RegisterOptions {Name : "branch2" })
173-
174172 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "branch2.json" )
175173 require .NoError (t , err )
176174}
177175
178- // Should have failed since the first go routine has only one branch whereas the history has two branches.
179- // The replayer totally misses this change.
180- // TODO(remove comment): Replayer now catches this
181- func TestParallel2 (t * testing.T ) {
176+ // Runs modified version of simpleParallelWorkflow which starts 1 less activity in the second workflow-gouroutine.
177+ // This is considered as non-determinism error.
178+ func TestSimpleParallelWorkflowWithMissingActivityCall (t * testing.T ) {
182179 replayer := worker .NewWorkflowReplayer ()
183-
184180 replayer .RegisterWorkflowWithOptions (sampleParallelWorkflow2 , workflow.RegisterOptions {Name : "branch2" })
185-
186181 err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "branch2.json" )
187- assert .ErrorContains (t , err , "nondeterministic workflow" )
182+ assert .ErrorContains (t , err , "nondeterministic workflow: missing replay decision " )
188183}
0 commit comments