17
17
18
18
package net .devh .boot .grpc .test .metric ;
19
19
20
+ import static io .grpc .Status .Code .CANCELLED ;
20
21
import static io .grpc .Status .Code .UNIMPLEMENTED ;
21
22
import static net .devh .boot .grpc .common .metric .MetricConstants .METRIC_NAME_CLIENT_PROCESSING_DURATION ;
22
23
import static net .devh .boot .grpc .common .metric .MetricConstants .METRIC_NAME_CLIENT_REQUESTS_SENT ;
26
27
import static net .devh .boot .grpc .common .metric .MetricConstants .METRIC_NAME_SERVER_RESPONSES_SENT ;
27
28
import static net .devh .boot .grpc .common .metric .MetricConstants .TAG_METHOD_NAME ;
28
29
import static net .devh .boot .grpc .common .metric .MetricConstants .TAG_STATUS_CODE ;
30
+ import static org .assertj .core .api .Assertions .assertThat ;
31
+ import static org .junit .jupiter .api .Assertions .assertDoesNotThrow ;
29
32
import static org .junit .jupiter .api .Assertions .assertEquals ;
30
33
import static org .junit .jupiter .api .Assertions .assertNotNull ;
31
34
import static org .junit .jupiter .api .Assertions .assertThrows ;
35
+ import static org .junit .jupiter .api .Assertions .assertTimeoutPreemptively ;
32
36
import static org .junit .jupiter .api .Assertions .assertTrue ;
37
+ import static org .junit .jupiter .api .Assertions .fail ;
33
38
39
+ import java .time .Duration ;
40
+ import java .util .concurrent .CountDownLatch ;
34
41
import java .util .concurrent .TimeUnit ;
42
+ import java .util .concurrent .atomic .AtomicReference ;
35
43
36
44
import org .junit .jupiter .api .Test ;
45
+ import org .junit .jupiter .api .function .Executable ;
37
46
import org .springframework .beans .factory .annotation .Autowired ;
38
47
import org .springframework .boot .autoconfigure .ImportAutoConfiguration ;
39
48
import org .springframework .boot .test .context .SpringBootTest ;
43
52
import com .google .protobuf .Empty ;
44
53
45
54
import io .grpc .StatusRuntimeException ;
55
+ import io .grpc .stub .ClientCallStreamObserver ;
56
+ import io .grpc .stub .StreamObserver ;
46
57
import io .micrometer .core .instrument .Counter ;
47
58
import io .micrometer .core .instrument .Meter ;
48
59
import io .micrometer .core .instrument .MeterRegistry ;
55
66
import net .devh .boot .grpc .test .config .BaseAutoConfiguration ;
56
67
import net .devh .boot .grpc .test .config .MetricConfiguration ;
57
68
import net .devh .boot .grpc .test .config .ServiceConfiguration ;
69
+ import net .devh .boot .grpc .test .proto .SomeType ;
58
70
import net .devh .boot .grpc .test .proto .TestServiceGrpc .TestServiceBlockingStub ;
71
+ import net .devh .boot .grpc .test .proto .TestServiceGrpc .TestServiceStub ;
59
72
60
73
/**
61
74
* A full test with Spring for both the server side and the client side interceptors.
70
83
@ SpringJUnitConfig (classes = {MetricConfiguration .class , ServiceConfiguration .class , BaseAutoConfiguration .class })
71
84
@ ImportAutoConfiguration ({GrpcClientMetricAutoConfiguration .class , GrpcServerMetricAutoConfiguration .class })
72
85
@ DirtiesContext
73
- public class MetricCollectingInterceptorTest {
86
+ class MetricCollectingInterceptorTest {
87
+
88
+ private static final Empty EMPTY = Empty .getDefaultInstance ();
74
89
75
90
@ Autowired
76
91
private MeterRegistry meterRegistry ;
77
92
78
93
@ GrpcClient ("test" )
79
94
private TestServiceBlockingStub testService ;
80
95
96
+ @ GrpcClient ("test" )
97
+ private TestServiceStub testStreamService ;
98
+
81
99
/**
82
100
* Test successful call.
83
101
*/
84
102
@ Test
85
103
@ DirtiesContext
86
- public void testMetricsSuccessfulCall () {
104
+ void testMetricsSuccessfulCall () {
87
105
log .info ("--- Starting tests with successful call ---" );
88
106
// Invoke 1
89
- assertEquals ("1.2.3" , this .testService .normal (Empty . getDefaultInstance () ).getVersion ());
107
+ assertEquals ("1.2.3" , this .testService .normal (EMPTY ).getVersion ());
90
108
91
109
// Test-Client 1
92
110
final Counter requestSentCounter =
@@ -137,7 +155,7 @@ public void testMetricsSuccessfulCall() {
137
155
// --------------------------------------------------------------------
138
156
139
157
// Invoke 2
140
- assertEquals ("1.2.3" , this .testService .normal (Empty . getDefaultInstance () ).getVersion ());
158
+ assertEquals ("1.2.3" , this .testService .normal (EMPTY ).getVersion ());
141
159
142
160
// Test-Client 2
143
161
assertEquals (2 , requestSentCounter .count ());
@@ -156,16 +174,130 @@ public void testMetricsSuccessfulCall() {
156
174
log .info ("--- Test completed ---" );
157
175
}
158
176
177
+ /**
178
+ * Test cancelled call.
179
+ */
180
+ @ Test
181
+ @ DirtiesContext
182
+ void testMetricsCancelledCall () {
183
+ log .info ("--- Starting tests with cancelled call ---" );
184
+ final AtomicReference <Throwable > exception = new AtomicReference <>();
185
+ final CountDownLatch counter = new CountDownLatch (1 );
186
+
187
+ // Invoke
188
+ final ClientCallStreamObserver <SomeType > observer =
189
+ (ClientCallStreamObserver <SomeType >) this .testStreamService .echo (new StreamObserver <SomeType >() {
190
+
191
+ @ Override
192
+ public void onNext (final SomeType value ) {
193
+ try {
194
+ fail ("Should never be here" );
195
+ } catch (final RuntimeException t ) {
196
+ setError (t );
197
+ throw t ;
198
+ }
199
+ }
200
+
201
+ @ Override
202
+ public void onError (final Throwable t ) {
203
+ setError (t );
204
+ counter .countDown ();
205
+ }
206
+
207
+ @ Override
208
+ public void onCompleted () {
209
+ try {
210
+ fail ("Should never be here" );
211
+ } catch (final RuntimeException t ) {
212
+ setError (t );
213
+ counter .countDown ();
214
+ throw t ;
215
+ }
216
+ }
217
+
218
+ private synchronized void setError (final Throwable t ) {
219
+ final Throwable previous = exception .get ();
220
+ if (previous == null ) {
221
+ exception .set (t );
222
+ } else {
223
+ previous .addSuppressed (t );
224
+ }
225
+ }
226
+
227
+ });
228
+
229
+ assertDoesNotThrow (() -> counter .await (1 , TimeUnit .SECONDS ));
230
+
231
+ observer .cancel ("Cancelled" , null );
232
+ assertTimeoutPreemptively (Duration .ofSeconds (3 ), (Executable ) counter ::await );
233
+ assertThat (exception .get ())
234
+ .isNotNull ()
235
+ .isInstanceOfSatisfying (StatusRuntimeException .class ,
236
+ t -> assertEquals (CANCELLED , t .getStatus ().getCode ()));
237
+
238
+ // Test-Client
239
+ final Counter requestSentCounter = this .meterRegistry
240
+ .find (METRIC_NAME_CLIENT_REQUESTS_SENT )
241
+ .tag (MetricConstants .TAG_METHOD_NAME , "echo" )
242
+ .counter ();
243
+ assertNotNull (requestSentCounter );
244
+ assertEquals (1 , requestSentCounter .count ());
245
+
246
+ final Counter responseReceivedCounter = this .meterRegistry
247
+ .find (METRIC_NAME_CLIENT_RESPONSES_RECEIVED )
248
+ .tag (MetricConstants .TAG_METHOD_NAME , "echo" )
249
+ .counter ();
250
+ assertNotNull (responseReceivedCounter );
251
+ assertEquals (0 , responseReceivedCounter .count ());
252
+
253
+ final Timer clientTimer = this .meterRegistry
254
+ .find (METRIC_NAME_CLIENT_PROCESSING_DURATION )
255
+ .tag (MetricConstants .TAG_METHOD_NAME , "echo" )
256
+ .tag (TAG_STATUS_CODE , CANCELLED .name ())
257
+ .timer ();
258
+ assertNotNull (clientTimer );
259
+ assertEquals (1 , clientTimer .count ());
260
+ assertTrue (clientTimer .max (TimeUnit .SECONDS ) < 1 );
261
+
262
+ // Test-Server
263
+ final Counter requestsReceivedCounter = this .meterRegistry
264
+ .find (METRIC_NAME_SERVER_REQUESTS_RECEIVED )
265
+ .tag (MetricConstants .TAG_METHOD_NAME , "echo" )
266
+ .counter ();
267
+ assertNotNull (requestsReceivedCounter );
268
+ assertEquals (1 , requestsReceivedCounter .count ());
269
+
270
+ final Counter responsesSentCounter = this .meterRegistry
271
+ .find (METRIC_NAME_SERVER_RESPONSES_SENT )
272
+ .tag (MetricConstants .TAG_METHOD_NAME , "echo" )
273
+ .counter ();
274
+ assertNotNull (responsesSentCounter );
275
+ assertEquals (0 , responsesSentCounter .count ());
276
+
277
+ final Timer serverTimer = this .meterRegistry
278
+ .find (METRIC_NAME_SERVER_PROCESSING_DURATION )
279
+ .tag (MetricConstants .TAG_METHOD_NAME , "echo" )
280
+ .tag (TAG_STATUS_CODE , UNIMPLEMENTED .name ())
281
+ .timer ();
282
+ assertNotNull (serverTimer );
283
+ assertEquals (1 , serverTimer .count ());
284
+ assertTrue (serverTimer .max (TimeUnit .SECONDS ) < 1 );
285
+
286
+ // Client has network overhead so it has to be slower
287
+ assertTrue (serverTimer .max (TimeUnit .SECONDS ) <= clientTimer .max (TimeUnit .SECONDS ));
288
+ log .info ("--- Test completed ---" );
289
+ }
290
+
159
291
/**
160
292
* Test failing call.
161
293
*/
162
294
@ Test
163
295
@ DirtiesContext
164
- public void testMetricsFailingCall () {
296
+ void testMetricsFailingCall () {
165
297
log .info ("--- Starting tests with failing call ---" );
166
298
// Invoke
167
299
assertThrows (StatusRuntimeException .class ,
168
- () -> this .testService .unimplemented (Empty . getDefaultInstance ()). getVersion ( ));
300
+ () -> this .testService .unimplemented (EMPTY ));
169
301
170
302
// Test-Client
171
303
final Counter requestSentCounter = this .meterRegistry
0 commit comments