@@ -35,6 +35,7 @@ import (
35
35
"go.uber.org/cadence/workflow"
36
36
)
37
37
38
+ // Basic happy paths
38
39
func TestReplayWorkflowHistoryFromFile (t * testing.T ) {
39
40
for _ , testFile := range []string {"basic.json" , "basic_new.json" , "version.json" , "version_new.json" } {
40
41
t .Run ("replay_" + strings .Split (testFile , "." )[0 ], func (t * testing.T ) {
@@ -48,6 +49,7 @@ func TestReplayWorkflowHistoryFromFile(t *testing.T) {
48
49
}
49
50
}
50
51
52
+ // Child workflow happy path
51
53
func TestReplayChildWorkflowBugBackport (t * testing.T ) {
52
54
replayer := worker .NewWorkflowReplayer ()
53
55
replayer .RegisterWorkflowWithOptions (childWorkflow , workflow.RegisterOptions {Name : "child" })
@@ -62,18 +64,18 @@ func TestGreetingsWorkflowforActivity(t *testing.T) {
62
64
replayer := worker .NewWorkflowReplayer ()
63
65
replayer .RegisterWorkflowWithOptions (greetingsWorkflowActivity , workflow.RegisterOptions {Name : "greetings" })
64
66
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" )
66
68
}
67
69
70
+ // Simple greeting workflow with 3 activities executed sequentially: getGreetingsActivity, getNameActivity, sayGreetingsActivity
68
71
func TestGreetingsWorkflow (t * testing.T ) {
69
72
replayer := worker .NewWorkflowReplayer ()
70
73
replayer .RegisterWorkflowWithOptions (greetingsWorkflow , workflow.RegisterOptions {Name : "greetings" })
71
74
err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "greetings.json" )
72
75
require .NoError (t , err )
73
76
}
74
77
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
77
79
func TestGreetingsWorkflow3 (t * testing.T ) {
78
80
replayer := worker .NewWorkflowReplayer ()
79
81
replayer .RegisterActivityWithOptions (getNameActivity3 , activity.RegisterOptions {Name : "main.getNameActivity" , DisableAlreadyRegisteredCheck : true })
@@ -82,107 +84,100 @@ func TestGreetingsWorkflow3(t *testing.T) {
82
84
require .NoError (t , err )
83
85
}
84
86
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 ) {
87
90
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 )
92
96
}
93
97
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 ) {
100
103
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 )
105
107
}
106
108
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 ) {
112
119
replayer := worker .NewWorkflowReplayer ()
113
-
114
120
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" })
115
123
err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "choice.json" )
116
- require . NoError (t , err )
124
+ assert . ErrorContains (t , err , "nondeterministic workflow: missing replay decision" )
117
125
}
118
126
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 ) {
127
131
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" })
132
135
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 " )
134
137
}
135
138
139
+ // Branch workflow happy case.
140
+ // It branches out to 3 open activities and then they complete.
136
141
func TestBranchWorkflow (t * testing.T ) {
137
142
replayer := worker .NewWorkflowReplayer ()
138
-
139
143
replayer .RegisterWorkflowWithOptions (sampleBranchWorkflow , workflow.RegisterOptions {Name : "branch" })
140
-
141
144
err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "branch.json" )
142
145
require .NoError (t , err )
143
146
}
144
147
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 .
147
150
func TestBranchWorkflowWithExtraBranch (t * testing.T ) {
148
151
replayer := worker .NewWorkflowReplayer ()
149
-
150
152
replayer .RegisterWorkflowWithOptions (sampleBranchWorkflow2 , workflow.RegisterOptions {Name : "branch" })
151
-
152
153
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 " )
154
155
}
155
156
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.
158
160
func TestSequentialStepsWorkflow (t * testing.T ) {
159
161
replayer := worker .NewWorkflowReplayer ()
160
-
161
162
replayer .RegisterWorkflowWithOptions (replayerHelloWorldWorkflow , workflow.RegisterOptions {Name : "fx.ReplayerHelloWorldWorkflow" })
162
163
replayer .RegisterActivityWithOptions (replayerHelloWorldActivity , activity.RegisterOptions {Name : "replayerhello" })
163
-
164
- // sequential.json file contains history of a run with 2 activity calls sequentially
165
164
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 " )
167
166
}
168
167
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 ) {
170
170
replayer := worker .NewWorkflowReplayer ()
171
-
172
171
replayer .RegisterWorkflowWithOptions (sampleParallelWorkflow , workflow.RegisterOptions {Name : "branch2" })
173
-
174
172
err := replayer .ReplayWorkflowHistoryFromJSONFile (zaptest .NewLogger (t ), "branch2.json" )
175
173
require .NoError (t , err )
176
174
}
177
175
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 ) {
182
179
replayer := worker .NewWorkflowReplayer ()
183
-
184
180
replayer .RegisterWorkflowWithOptions (sampleParallelWorkflow2 , workflow.RegisterOptions {Name : "branch2" })
185
-
186
181
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 " )
188
183
}
0 commit comments