@@ -28,14 +28,17 @@ import (
2828 "testing"
2929 "time"
3030
31+ "github.com/stretchr/testify/assert"
32+
33+ "go.uber.org/cadence/.gen/go/cadence/workflowservicetest"
34+
3135 "github.com/golang/mock/gomock"
3236 "github.com/stretchr/testify/require"
3337 "github.com/uber-go/tally"
3438 "github.com/uber/tchannel-go/thrift"
3539 "go.uber.org/yarpc"
3640
3741 "go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
38- "go.uber.org/cadence/.gen/go/cadence/workflowservicetest"
3942 s "go.uber.org/cadence/.gen/go/shared"
4043)
4144
@@ -60,144 +63,124 @@ var (
6063)
6164
6265type 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
6672 expectedCounters []string
6773}
6874
6975func Test_Wrapper (t * testing.T ) {
7076 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+ })
10495 }
10596
10697 // run each test twice - once with the regular scope, once with a sanitized metrics scope
10798 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+
110120 }
111121}
112122
113123func runTest (
114124 t * testing.T ,
115125 test testCase ,
126+ errCase errCase ,
116127 serviceFunc func (* testing.T ) (* workflowservicetest.MockClient , workflowserviceclient.Interface , io.Closer , * CapturingStatsReporter ),
117128 validationFunc func (* testing.T , * CapturingStatsReporter , string , []string ),
118129 name string ,
119130) {
120131 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-
126132 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+ }
184153 }
185154
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+
186163 callOption := yarpc.CallOption {}
187164 inputs := make ([]reflect.Value , len (test .callArgs ))
188165 for i , arg := range test .callArgs {
189166 inputs [i ] = reflect .ValueOf (arg )
190167 }
191168 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+ }
194177 require .NoError (t , closer .Close ())
195- validationFunc (t , reporter , test .serviceMethod , test .expectedCounters )
178+ validationFunc (t , reporter , test .serviceMethod , errCase .expectedCounters )
196179 })
197180}
198181
199182func 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 )
201184 for _ , name := range counterNames {
202185 counterName := CadenceMetricsPrefix + methodName + "." + name
203186 find := false
@@ -208,14 +191,14 @@ func assertMetrics(t *testing.T, reporter *CapturingStatsReporter, methodName st
208191 break
209192 }
210193 }
211- require .True (t , find )
194+ assert .True (t , find , "counter %v not found in counters %v" , counterName , reporter . counts )
212195 }
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 )
215198}
216199
217200func 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 )
219202 for _ , name := range counterNames {
220203 counterName := makePromCompatible (CadenceMetricsPrefix + methodName + "." + name )
221204 find := false
@@ -226,11 +209,11 @@ func assertPromMetrics(t *testing.T, reporter *CapturingStatsReporter, methodNam
226209 break
227210 }
228211 }
229- require .True (t , find )
212+ assert .True (t , find , "counter %v not found in counters %v" , counterName , reporter . counts )
230213 }
231- require .Equal (t , 1 , len (reporter .timers ))
214+ assert .Equal (t , 1 , len ( reporter . timers ), "expected 1 timer, got %v" , len (reporter .timers ))
232215 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 )
234217}
235218
236219func makePromCompatible (name string ) string {
@@ -276,3 +259,5 @@ func newPromScope(isReplay *bool) (tally.Scope, io.Closer, *CapturingStatsReport
276259 scope , closer := tally .NewRootScope (opts , time .Second )
277260 return WrapScope (isReplay , scope , & realClock {}), closer , reporter
278261}
262+
263+ var nilError = reflect .Zero (reflect .TypeOf ((* error )(nil )).Elem ())
0 commit comments