3030package com .google .api .gax .grpc ;
3131
3232import static com .google .common .truth .Truth .assertThat ;
33+ import static org .junit .Assert .assertThrows ;
3334import static org .mockito .Mockito .verify ;
3435
3536import com .google .api .gax .grpc .testing .FakeChannelFactory ;
3637import com .google .api .gax .grpc .testing .FakeServiceGrpc ;
38+ import com .google .api .gax .rpc .EndpointContext ;
39+ import com .google .api .gax .rpc .UnauthenticatedException ;
40+ import com .google .api .gax .rpc .UnavailableException ;
41+ import com .google .auth .Credentials ;
42+ import com .google .auth .Retryable ;
3743import com .google .common .collect .ImmutableList ;
3844import com .google .common .truth .Truth ;
3945import com .google .type .Color ;
4551import io .grpc .ManagedChannel ;
4652import io .grpc .Metadata ;
4753import io .grpc .MethodDescriptor ;
54+ import io .grpc .Status ;
4855import java .io .IOException ;
4956import java .util .Arrays ;
5057import java .util .HashMap ;
5158import java .util .List ;
5259import java .util .Map ;
5360import java .util .concurrent .TimeUnit ;
61+ import org .junit .Before ;
5462import org .junit .Test ;
5563import org .mockito .ArgumentCaptor ;
5664import org .mockito .Mockito ;
5765import org .threeten .bp .Duration ;
5866
5967public class GrpcClientCallsTest {
68+
69+ // Auth Library's GoogleAuthException is package-private. Copy basic functionality for tests
70+ private static class GoogleAuthException extends IOException implements Retryable {
71+
72+ private final boolean isRetryable ;
73+
74+ private GoogleAuthException (boolean isRetryable ) {
75+ this .isRetryable = isRetryable ;
76+ }
77+
78+ @ Override
79+ public boolean isRetryable () {
80+ return isRetryable ;
81+ }
82+
83+ @ Override
84+ public int getRetryCount () {
85+ return 0 ;
86+ }
87+ }
88+
89+ private GrpcCallContext defaultCallContext ;
90+ private EndpointContext endpointContext ;
91+ private Credentials credentials ;
92+ private Channel mockChannel ;
93+
94+ @ Before
95+ public void setUp () throws IOException {
96+ credentials = Mockito .mock (Credentials .class );
97+ endpointContext = Mockito .mock (EndpointContext .class );
98+ mockChannel = Mockito .mock (Channel .class );
99+
100+ defaultCallContext = GrpcCallContext .createDefault ().withEndpointContext (endpointContext );
101+ Mockito .doNothing ()
102+ .when (endpointContext )
103+ .validateUniverseDomain (Mockito .any (Credentials .class ), Mockito .any (GrpcStatusCode .class ));
104+ }
105+
60106 @ Test
61107 public void testAffinity () throws IOException {
62108 MethodDescriptor <Color , Money > descriptor = FakeServiceGrpc .METHOD_RECOGNIZE ;
@@ -78,7 +124,7 @@ public void testAffinity() throws IOException {
78124 ChannelPool .create (
79125 ChannelPoolSettings .staticallySized (2 ),
80126 new FakeChannelFactory (Arrays .asList (channel0 , channel1 )));
81- GrpcCallContext context = GrpcCallContext . createDefault () .withChannel (pool );
127+ GrpcCallContext context = defaultCallContext .withChannel (pool );
82128
83129 ClientCall <Color , Money > gotCallA =
84130 GrpcClientCalls .newCall (descriptor , context .withChannelAffinity (0 ));
@@ -92,7 +138,7 @@ public void testAffinity() throws IOException {
92138 }
93139
94140 @ Test
95- public void testExtraHeaders () {
141+ public void testExtraHeaders () throws IOException {
96142 Metadata emptyHeaders = new Metadata ();
97143 final Map <String , List <String >> extraHeaders = new HashMap <>();
98144 extraHeaders .put (
@@ -128,12 +174,12 @@ public void testExtraHeaders() {
128174 .thenReturn (mockClientCall );
129175
130176 GrpcCallContext context =
131- GrpcCallContext . createDefault () .withChannel (mockChannel ).withExtraHeaders (extraHeaders );
177+ defaultCallContext .withChannel (mockChannel ).withExtraHeaders (extraHeaders );
132178 GrpcClientCalls .newCall (descriptor , context ).start (mockListener , emptyHeaders );
133179 }
134180
135181 @ Test
136- public void testTimeoutToDeadlineConversion () {
182+ public void testTimeoutToDeadlineConversion () throws IOException {
137183 MethodDescriptor <Color , Money > descriptor = FakeServiceGrpc .METHOD_RECOGNIZE ;
138184
139185 @ SuppressWarnings ("unchecked" )
@@ -152,8 +198,7 @@ public void testTimeoutToDeadlineConversion() {
152198 Duration timeout = Duration .ofSeconds (10 );
153199 Deadline minExpectedDeadline = Deadline .after (timeout .getSeconds (), TimeUnit .SECONDS );
154200
155- GrpcCallContext context =
156- GrpcCallContext .createDefault ().withChannel (mockChannel ).withTimeout (timeout );
201+ GrpcCallContext context = defaultCallContext .withChannel (mockChannel ).withTimeout (timeout );
157202
158203 GrpcClientCalls .newCall (descriptor , context ).start (mockListener , new Metadata ());
159204
@@ -164,7 +209,7 @@ public void testTimeoutToDeadlineConversion() {
164209 }
165210
166211 @ Test
167- public void testTimeoutAfterDeadline () {
212+ public void testTimeoutAfterDeadline () throws IOException {
168213 MethodDescriptor <Color , Money > descriptor = FakeServiceGrpc .METHOD_RECOGNIZE ;
169214
170215 @ SuppressWarnings ("unchecked" )
@@ -185,7 +230,7 @@ public void testTimeoutAfterDeadline() {
185230 Duration timeout = Duration .ofSeconds (10 );
186231
187232 GrpcCallContext context =
188- GrpcCallContext . createDefault ()
233+ defaultCallContext
189234 .withChannel (mockChannel )
190235 .withCallOptions (CallOptions .DEFAULT .withDeadline (priorDeadline ))
191236 .withTimeout (timeout );
@@ -197,7 +242,7 @@ public void testTimeoutAfterDeadline() {
197242 }
198243
199244 @ Test
200- public void testTimeoutBeforeDeadline () {
245+ public void testTimeoutBeforeDeadline () throws IOException {
201246 MethodDescriptor <Color , Money > descriptor = FakeServiceGrpc .METHOD_RECOGNIZE ;
202247
203248 @ SuppressWarnings ("unchecked" )
@@ -219,7 +264,7 @@ public void testTimeoutBeforeDeadline() {
219264 Deadline minExpectedDeadline = Deadline .after (timeout .getSeconds (), TimeUnit .SECONDS );
220265
221266 GrpcCallContext context =
222- GrpcCallContext . createDefault ()
267+ defaultCallContext
223268 .withChannel (mockChannel )
224269 .withCallOptions (CallOptions .DEFAULT .withDeadline (subsequentDeadline ))
225270 .withTimeout (timeout );
@@ -232,4 +277,66 @@ public void testTimeoutBeforeDeadline() {
232277 Truth .assertThat (capturedCallOptions .getValue ().getDeadline ()).isAtLeast (minExpectedDeadline );
233278 Truth .assertThat (capturedCallOptions .getValue ().getDeadline ()).isAtMost (maxExpectedDeadline );
234279 }
280+
281+ @ Test
282+ public void testValidUniverseDomain () throws IOException {
283+ GrpcCallContext context =
284+ GrpcCallContext .createDefault ()
285+ .withChannel (mockChannel )
286+ .withCredentials (credentials )
287+ .withEndpointContext (endpointContext );
288+
289+ CallOptions callOptions = context .getCallOptions ();
290+
291+ MethodDescriptor <Color , Money > descriptor = FakeServiceGrpc .METHOD_RECOGNIZE ;
292+ GrpcClientCalls .newCall (descriptor , context );
293+ Mockito .verify (mockChannel , Mockito .times (1 )).newCall (descriptor , callOptions );
294+ }
295+
296+ // This test is when the universe domain does not match
297+ @ Test
298+ public void testInvalidUniverseDomain () throws IOException {
299+ Mockito .doThrow (
300+ new UnauthenticatedException (
301+ null , GrpcStatusCode .of (Status .Code .UNAUTHENTICATED ), false ))
302+ .when (endpointContext )
303+ .validateUniverseDomain (Mockito .any (Credentials .class ), Mockito .any (GrpcStatusCode .class ));
304+ GrpcCallContext context =
305+ GrpcCallContext .createDefault ()
306+ .withChannel (mockChannel )
307+ .withCredentials (credentials )
308+ .withEndpointContext (endpointContext );
309+
310+ CallOptions callOptions = context .getCallOptions ();
311+
312+ MethodDescriptor <Color , Money > descriptor = FakeServiceGrpc .METHOD_RECOGNIZE ;
313+ UnauthenticatedException exception =
314+ assertThrows (
315+ UnauthenticatedException .class , () -> GrpcClientCalls .newCall (descriptor , context ));
316+ assertThat (exception .getStatusCode ().getCode ()).isEqualTo (GrpcStatusCode .Code .UNAUTHENTICATED );
317+ Mockito .verify (mockChannel , Mockito .never ()).newCall (descriptor , callOptions );
318+ }
319+
320+ // This test is when the MDS is unable to return a valid universe domain
321+ @ Test
322+ public void testUniverseDomainNotReady_shouldRetry () throws IOException {
323+ Mockito .doThrow (new GoogleAuthException (true ))
324+ .when (endpointContext )
325+ .validateUniverseDomain (Mockito .any (Credentials .class ), Mockito .any (GrpcStatusCode .class ));
326+ GrpcCallContext context =
327+ GrpcCallContext .createDefault ()
328+ .withChannel (mockChannel )
329+ .withCredentials (credentials )
330+ .withEndpointContext (endpointContext );
331+
332+ CallOptions callOptions = context .getCallOptions ();
333+
334+ MethodDescriptor <Color , Money > descriptor = FakeServiceGrpc .METHOD_RECOGNIZE ;
335+ UnavailableException exception =
336+ assertThrows (
337+ UnavailableException .class , () -> GrpcClientCalls .newCall (descriptor , context ));
338+ assertThat (exception .getStatusCode ().getCode ()).isEqualTo (GrpcStatusCode .Code .UNAVAILABLE );
339+ Truth .assertThat (exception .isRetryable ()).isTrue ();
340+ Mockito .verify (mockChannel , Mockito .never ()).newCall (descriptor , callOptions );
341+ }
235342}
0 commit comments