@@ -28,14 +28,17 @@ import (
28
28
"testing"
29
29
"time"
30
30
31
+ "github.com/stretchr/testify/assert"
32
+
33
+ "go.uber.org/cadence/.gen/go/cadence/workflowservicetest"
34
+
31
35
"github.com/golang/mock/gomock"
32
36
"github.com/stretchr/testify/require"
33
37
"github.com/uber-go/tally"
34
38
"github.com/uber/tchannel-go/thrift"
35
39
"go.uber.org/yarpc"
36
40
37
41
"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
38
- "go.uber.org/cadence/.gen/go/cadence/workflowservicetest"
39
42
s "go.uber.org/cadence/.gen/go/shared"
40
43
)
41
44
@@ -60,144 +63,124 @@ var (
60
63
)
61
64
62
65
type testCase struct {
63
- serviceMethod string
64
- callArgs []interface {}
65
- mockReturns []interface {}
66
+ serviceMethod string
67
+ callArgs []interface {}
68
+ }
69
+
70
+ type errCase struct {
71
+ err error
66
72
expectedCounters []string
67
73
}
68
74
69
75
func Test_Wrapper (t * testing.T ) {
70
76
ctx , _ := thrift .NewContext (time .Minute )
71
- tests := []testCase {
72
- // one case for each service call
73
- {"DeprecateDomain" , []interface {}{ctx , & s.DeprecateDomainRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
74
- {"DescribeDomain" , []interface {}{ctx , & s.DescribeDomainRequest {}}, []interface {}{& s.DescribeDomainResponse {}, nil }, []string {CadenceRequest }},
75
- {"GetWorkflowExecutionHistory" , []interface {}{ctx , & s.GetWorkflowExecutionHistoryRequest {}}, []interface {}{& s.GetWorkflowExecutionHistoryResponse {}, nil }, []string {CadenceRequest }},
76
- {"ListClosedWorkflowExecutions" , []interface {}{ctx , & s.ListClosedWorkflowExecutionsRequest {}}, []interface {}{& s.ListClosedWorkflowExecutionsResponse {}, nil }, []string {CadenceRequest }},
77
- {"ListOpenWorkflowExecutions" , []interface {}{ctx , & s.ListOpenWorkflowExecutionsRequest {}}, []interface {}{& s.ListOpenWorkflowExecutionsResponse {}, nil }, []string {CadenceRequest }},
78
- {"PollForActivityTask" , []interface {}{ctx , & s.PollForActivityTaskRequest {}}, []interface {}{& s.PollForActivityTaskResponse {}, nil }, []string {CadenceRequest }},
79
- {"PollForDecisionTask" , []interface {}{ctx , & s.PollForDecisionTaskRequest {}}, []interface {}{& s.PollForDecisionTaskResponse {}, nil }, []string {CadenceRequest }},
80
- {"RecordActivityTaskHeartbeat" , []interface {}{ctx , & s.RecordActivityTaskHeartbeatRequest {}}, []interface {}{& s.RecordActivityTaskHeartbeatResponse {}, nil }, []string {CadenceRequest }},
81
- {"RegisterDomain" , []interface {}{ctx , & s.RegisterDomainRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
82
- {"RequestCancelWorkflowExecution" , []interface {}{ctx , & s.RequestCancelWorkflowExecutionRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
83
- {"RespondActivityTaskCanceled" , []interface {}{ctx , & s.RespondActivityTaskCanceledRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
84
- {"RespondActivityTaskCompleted" , []interface {}{ctx , & s.RespondActivityTaskCompletedRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
85
- {"RespondActivityTaskFailed" , []interface {}{ctx , & s.RespondActivityTaskFailedRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
86
- {"RespondActivityTaskCanceledByID" , []interface {}{ctx , & s.RespondActivityTaskCanceledByIDRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
87
- {"RespondActivityTaskCompletedByID" , []interface {}{ctx , & s.RespondActivityTaskCompletedByIDRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
88
- {"RespondActivityTaskFailedByID" , []interface {}{ctx , & s.RespondActivityTaskFailedByIDRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
89
- {"RespondDecisionTaskCompleted" , []interface {}{ctx , & s.RespondDecisionTaskCompletedRequest {}}, []interface {}{nil , nil }, []string {CadenceRequest }},
90
- {"SignalWorkflowExecution" , []interface {}{ctx , & s.SignalWorkflowExecutionRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
91
- {"SignalWithStartWorkflowExecution" , []interface {}{ctx , & s.SignalWithStartWorkflowExecutionRequest {}}, []interface {}{& s.StartWorkflowExecutionResponse {}, nil }, []string {CadenceRequest }},
92
- {"SignalWithStartWorkflowExecutionAsync" , []interface {}{ctx , & s.SignalWithStartWorkflowExecutionAsyncRequest {}}, []interface {}{& s.SignalWithStartWorkflowExecutionAsyncResponse {}, nil }, []string {CadenceRequest }},
93
- {"StartWorkflowExecution" , []interface {}{ctx , & s.StartWorkflowExecutionRequest {}}, []interface {}{& s.StartWorkflowExecutionResponse {}, nil }, []string {CadenceRequest }},
94
- {"StartWorkflowExecutionAsync" , []interface {}{ctx , & s.StartWorkflowExecutionAsyncRequest {}}, []interface {}{& s.StartWorkflowExecutionAsyncResponse {}, nil }, []string {CadenceRequest }},
95
- {"TerminateWorkflowExecution" , []interface {}{ctx , & s.TerminateWorkflowExecutionRequest {}}, []interface {}{nil }, []string {CadenceRequest }},
96
- {"ResetWorkflowExecution" , []interface {}{ctx , & s.ResetWorkflowExecutionRequest {}}, []interface {}{& s.ResetWorkflowExecutionResponse {}, nil }, []string {CadenceRequest }},
97
- {"UpdateDomain" , []interface {}{ctx , & s.UpdateDomainRequest {}}, []interface {}{& s.UpdateDomainResponse {}, nil }, []string {CadenceRequest }},
98
- // one case of invalid request
99
- {"PollForActivityTask" , []interface {}{ctx , & s.PollForActivityTaskRequest {}}, []interface {}{nil , & s.EntityNotExistsError {}}, []string {CadenceRequest , CadenceInvalidRequest }},
100
- // one case of server error
101
- {"PollForActivityTask" , []interface {}{ctx , & s.PollForActivityTaskRequest {}}, []interface {}{nil , & s.InternalServiceError {}}, []string {CadenceRequest , CadenceError }},
102
- {"QueryWorkflow" , []interface {}{ctx , & s.QueryWorkflowRequest {}}, []interface {}{nil , & s.InternalServiceError {}}, []string {CadenceRequest , CadenceError }},
103
- {"RespondQueryTaskCompleted" , []interface {}{ctx , & s.RespondQueryTaskCompletedRequest {}}, []interface {}{& s.InternalServiceError {}}, []string {CadenceRequest , CadenceError }},
77
+
78
+ typeWrapper := reflect .TypeOf (& workflowServiceMetricsWrapper {})
79
+ tests := make ([]testCase , 0 , typeWrapper .NumMethod ())
80
+ for numMethod := 0 ; numMethod < typeWrapper .NumMethod (); numMethod ++ {
81
+ method := typeWrapper .Method (numMethod )
82
+ inputs := make ([]interface {}, 0 , method .Type .NumIn ()- 1 )
83
+ inputs = append (inputs , ctx )
84
+
85
+ for i := 1 ; i < method .Type .NumIn (); i ++ {
86
+ if method .Type .In (i ).Kind () == reflect .Ptr {
87
+ inputs = append (inputs , reflect .New (method .Type .In (i ).Elem ()).Interface ())
88
+ }
89
+ }
90
+
91
+ tests = append (tests , testCase {
92
+ serviceMethod : method .Name ,
93
+ callArgs : inputs ,
94
+ })
104
95
}
105
96
106
97
// run each test twice - once with the regular scope, once with a sanitized metrics scope
107
98
for _ , test := range tests {
108
- runTest (t , test , newService , assertMetrics , fmt .Sprintf ("%v_normal" , test .serviceMethod ))
109
- runTest (t , test , newPromService , assertPromMetrics , fmt .Sprintf ("%v_prom_sanitized" , test .serviceMethod ))
99
+ for _ , errCase := range []struct {
100
+ err error
101
+ expectedCounters []string
102
+ }{
103
+ {
104
+ err : nil ,
105
+ expectedCounters : []string {CadenceRequest },
106
+ },
107
+ {
108
+ err : & s.EntityNotExistsError {},
109
+ expectedCounters : []string {CadenceRequest , CadenceInvalidRequest },
110
+ },
111
+ {
112
+ err : & s.InternalServiceError {},
113
+ expectedCounters : []string {CadenceRequest , CadenceError },
114
+ },
115
+ } {
116
+ runTest (t , test , errCase , newService , assertMetrics , fmt .Sprintf ("%v_errcase_%v_normal" , test .serviceMethod , errCase .err ))
117
+ runTest (t , test , errCase , newPromService , assertPromMetrics , fmt .Sprintf ("%v_errcase_%v_prom_sanitized" , test .serviceMethod , errCase .err ))
118
+ }
119
+
110
120
}
111
121
}
112
122
113
123
func runTest (
114
124
t * testing.T ,
115
125
test testCase ,
126
+ errCase errCase ,
116
127
serviceFunc func (* testing.T ) (* workflowservicetest.MockClient , workflowserviceclient.Interface , io.Closer , * CapturingStatsReporter ),
117
128
validationFunc func (* testing.T , * CapturingStatsReporter , string , []string ),
118
129
name string ,
119
130
) {
120
131
t .Run (name , func (t * testing.T ) {
121
- t .Parallel ()
122
- // gomock mutates the returns slice, which leads to different test values between the two runs.
123
- // copy the slice until gomock fixes it: https://github.com/golang/mock/issues/353
124
- returns := append (make ([]interface {}, 0 , len (test .mockReturns )), test .mockReturns ... )
125
-
126
132
mockService , wrapperService , closer , reporter := serviceFunc (t )
127
- switch test .serviceMethod {
128
- case "DeprecateDomain" :
129
- mockService .EXPECT ().DeprecateDomain (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
130
- case "DescribeDomain" :
131
- mockService .EXPECT ().DescribeDomain (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
132
- case "GetWorkflowExecutionHistory" :
133
- mockService .EXPECT ().GetWorkflowExecutionHistory (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
134
- case "ListClosedWorkflowExecutions" :
135
- mockService .EXPECT ().ListClosedWorkflowExecutions (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
136
- case "ListOpenWorkflowExecutions" :
137
- mockService .EXPECT ().ListOpenWorkflowExecutions (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
138
- case "PollForActivityTask" :
139
- mockService .EXPECT ().PollForActivityTask (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
140
- case "PollForDecisionTask" :
141
- mockService .EXPECT ().PollForDecisionTask (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
142
- case "RecordActivityTaskHeartbeat" :
143
- mockService .EXPECT ().RecordActivityTaskHeartbeat (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
144
- case "RecordActivityTaskHeartbeatByID" :
145
- mockService .EXPECT ().RecordActivityTaskHeartbeatByID (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
146
- case "RegisterDomain" :
147
- mockService .EXPECT ().RegisterDomain (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
148
- case "RequestCancelWorkflowExecution" :
149
- mockService .EXPECT ().RequestCancelWorkflowExecution (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
150
- case "RespondActivityTaskCanceled" :
151
- mockService .EXPECT ().RespondActivityTaskCanceled (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
152
- case "RespondActivityTaskCompleted" :
153
- mockService .EXPECT ().RespondActivityTaskCompleted (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
154
- case "RespondActivityTaskFailed" :
155
- mockService .EXPECT ().RespondActivityTaskFailed (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
156
- case "RespondActivityTaskCanceledByID" :
157
- mockService .EXPECT ().RespondActivityTaskCanceledByID (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
158
- case "RespondActivityTaskCompletedByID" :
159
- mockService .EXPECT ().RespondActivityTaskCompletedByID (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
160
- case "RespondActivityTaskFailedByID" :
161
- mockService .EXPECT ().RespondActivityTaskFailedByID (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
162
- case "RespondDecisionTaskCompleted" :
163
- mockService .EXPECT ().RespondDecisionTaskCompleted (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
164
- case "SignalWorkflowExecution" :
165
- mockService .EXPECT ().SignalWorkflowExecution (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
166
- case "SignalWithStartWorkflowExecution" :
167
- mockService .EXPECT ().SignalWithStartWorkflowExecution (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
168
- case "SignalWithStartWorkflowExecutionAsync" :
169
- mockService .EXPECT ().SignalWithStartWorkflowExecutionAsync (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
170
- case "StartWorkflowExecution" :
171
- mockService .EXPECT ().StartWorkflowExecution (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
172
- case "StartWorkflowExecutionAsync" :
173
- mockService .EXPECT ().StartWorkflowExecutionAsync (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
174
- case "TerminateWorkflowExecution" :
175
- mockService .EXPECT ().TerminateWorkflowExecution (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
176
- case "ResetWorkflowExecution" :
177
- mockService .EXPECT ().ResetWorkflowExecution (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
178
- case "UpdateDomain" :
179
- mockService .EXPECT ().UpdateDomain (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
180
- case "QueryWorkflow" :
181
- mockService .EXPECT ().QueryWorkflow (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
182
- case "RespondQueryTaskCompleted" :
183
- mockService .EXPECT ().RespondQueryTaskCompleted (gomock .Any (), gomock .Any (), gomock .Any ()).Return (returns ... )
133
+ mockServiceVal := reflect .ValueOf (mockService )
134
+ method , exists := mockServiceVal .Type ().MethodByName (test .serviceMethod )
135
+ require .True (t , exists , "method %s does not exists" , test .serviceMethod )
136
+
137
+ expecterVals := mockServiceVal .MethodByName ("EXPECT" ).Call (nil )
138
+ expectedMethod := expecterVals [0 ].MethodByName (test .serviceMethod )
139
+ mockInputs := make ([]reflect.Value , 0 , expectedMethod .Type ().NumIn ())
140
+ for inCounter := 0 ; inCounter < expectedMethod .Type ().NumIn (); inCounter ++ {
141
+ mockInputs = append (mockInputs , reflect .ValueOf (gomock .Any ()))
142
+ }
143
+ callVals := expectedMethod .Call (mockInputs )
144
+
145
+ returnVals := make ([]reflect.Value , 0 , method .Type .NumOut ())
146
+ for i := 0 ; i < method .Type .NumOut ()- 1 ; i ++ {
147
+ output := method .Type .Out (i )
148
+ if errCase .err == nil {
149
+ returnVals = append (returnVals , reflect .New (output ).Elem ())
150
+ } else {
151
+ returnVals = append (returnVals , reflect .Zero (output ))
152
+ }
184
153
}
185
154
155
+ if errCase .err != nil {
156
+ returnVals = append (returnVals , reflect .ValueOf (errCase .err ))
157
+ } else {
158
+ returnVals = append (returnVals , nilError )
159
+ }
160
+
161
+ callVals [0 ].MethodByName ("Return" ).Call (returnVals )
162
+
186
163
callOption := yarpc.CallOption {}
187
164
inputs := make ([]reflect.Value , len (test .callArgs ))
188
165
for i , arg := range test .callArgs {
189
166
inputs [i ] = reflect .ValueOf (arg )
190
167
}
191
168
inputs = append (inputs , reflect .ValueOf (callOption ))
192
- method := reflect .ValueOf (wrapperService ).MethodByName (test .serviceMethod )
193
- method .Call (inputs )
169
+ actualMethod := reflect .ValueOf (wrapperService ).MethodByName (test .serviceMethod )
170
+ methodReturnVals := actualMethod .Call (inputs )
171
+ err := methodReturnVals [len (methodReturnVals )- 1 ].Interface ()
172
+ if errCase .err != nil {
173
+ assert .ErrorIs (t , err .(error ), errCase .err )
174
+ } else {
175
+ assert .Nil (t , err , "error must be nil" )
176
+ }
194
177
require .NoError (t , closer .Close ())
195
- validationFunc (t , reporter , test .serviceMethod , test .expectedCounters )
178
+ validationFunc (t , reporter , test .serviceMethod , errCase .expectedCounters )
196
179
})
197
180
}
198
181
199
182
func assertMetrics (t * testing.T , reporter * CapturingStatsReporter , methodName string , counterNames []string ) {
200
- require .Equal (t , len (counterNames ), len (reporter .counts ))
183
+ assert .Equal (t , len (counterNames ), len (reporter .counts ), "expected %v counters, got %v" , counterNames , reporter . counts )
201
184
for _ , name := range counterNames {
202
185
counterName := CadenceMetricsPrefix + methodName + "." + name
203
186
find := false
@@ -208,14 +191,14 @@ func assertMetrics(t *testing.T, reporter *CapturingStatsReporter, methodName st
208
191
break
209
192
}
210
193
}
211
- require .True (t , find )
194
+ assert .True (t , find , "counter %v not found in counters %v" , counterName , reporter . counts )
212
195
}
213
- require .Equal (t , 1 , len (reporter .timers ))
214
- require .Equal (t , CadenceMetricsPrefix + methodName + "." + CadenceLatency , reporter .timers [0 ].name )
196
+ assert .Equal (t , 1 , len ( reporter . timers ), "expected 1 timer, got %v" , len (reporter .timers ))
197
+ assert .Equal (t , CadenceMetricsPrefix + methodName + "." + CadenceLatency , reporter . timers [ 0 ]. name , "expected timer %v, got %v" , CadenceMetricsPrefix + methodName + "." + CadenceLatency , reporter .timers [0 ].name )
215
198
}
216
199
217
200
func assertPromMetrics (t * testing.T , reporter * CapturingStatsReporter , methodName string , counterNames []string ) {
218
- require .Equal (t , len (counterNames ), len (reporter .counts ))
201
+ assert .Equal (t , len (counterNames ), len (reporter .counts ), "expected %v counters, got %v" , counterNames , reporter . counts )
219
202
for _ , name := range counterNames {
220
203
counterName := makePromCompatible (CadenceMetricsPrefix + methodName + "." + name )
221
204
find := false
@@ -226,11 +209,11 @@ func assertPromMetrics(t *testing.T, reporter *CapturingStatsReporter, methodNam
226
209
break
227
210
}
228
211
}
229
- require .True (t , find )
212
+ assert .True (t , find , "counter %v not found in counters %v" , counterName , reporter . counts )
230
213
}
231
- require .Equal (t , 1 , len (reporter .timers ))
214
+ assert .Equal (t , 1 , len ( reporter . timers ), "expected 1 timer, got %v" , len (reporter .timers ))
232
215
expected := makePromCompatible (CadenceMetricsPrefix + methodName + "." + CadenceLatency )
233
- require .Equal (t , expected , reporter .timers [0 ].name )
216
+ assert .Equal (t , expected , reporter . timers [ 0 ]. name , "expected timer %v, got %v" , expected , reporter .timers [0 ].name )
234
217
}
235
218
236
219
func makePromCompatible (name string ) string {
@@ -276,3 +259,5 @@ func newPromScope(isReplay *bool) (tally.Scope, io.Closer, *CapturingStatsReport
276
259
scope , closer := tally .NewRootScope (opts , time .Second )
277
260
return WrapScope (isReplay , scope , & realClock {}), closer , reporter
278
261
}
262
+
263
+ var nilError = reflect .Zero (reflect .TypeOf ((* error )(nil )).Elem ())
0 commit comments