3131package com .google .auth .oauth2 ;
3232
3333import static org .junit .Assert .assertArrayEquals ;
34+ import static org .junit .Assert .assertEquals ;
3435import static org .junit .Assert .assertThrows ;
3536import static org .junit .Assert .assertTrue ;
3637
3738import com .google .api .client .http .HttpStatusCodes ;
3839import com .google .auth .ServiceAccountSigner ;
3940import com .google .common .collect .ImmutableMap ;
4041import java .io .IOException ;
42+ import org .junit .Before ;
4143import org .junit .Test ;
4244import org .junit .runner .RunWith ;
4345import org .junit .runners .JUnit4 ;
@@ -49,17 +51,53 @@ public class IamUtilsTest {
4951 private static final String CLIENT_EMAIL =
5052 "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr@developer.gserviceaccount.com" ;
5153
54+ private ServiceAccountCredentials credentials ;
55+
56+ @ Before
57+ public void setup () throws IOException {
58+ // Mock this call for the Credentials because the IAM SignBlob RPC requires an access token. The
59+ // call is initialized with HttpCredentialsAdapter which will make a call to get the access
60+ // token
61+ credentials = Mockito .mock (ServiceAccountCredentials .class );
62+ Mockito .when (credentials .getRequestMetadata (Mockito .any ())).thenReturn (ImmutableMap .of ());
63+ }
64+
5265 @ Test
53- public void sign_noRetry () throws IOException {
66+ public void sign_success_noRetry () {
5467 byte [] expectedSignature = {0xD , 0xE , 0xA , 0xD };
5568
56- // Mock this call because signing requires an access token. The call is initialized with
57- // HttpCredentialsAdapter which will make a call to get the access token
58- ServiceAccountCredentials credentials = Mockito .mock (ServiceAccountCredentials .class );
59- Mockito .when (credentials .getRequestMetadata (Mockito .any ())).thenReturn (ImmutableMap .of ());
69+ ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory transportFactory =
70+ new ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory ();
71+ transportFactory .transport .setSignedBlob (expectedSignature );
72+ transportFactory .transport .setTargetPrincipal (CLIENT_EMAIL );
73+ transportFactory .transport .addStatusCodeAndMessage (HttpStatusCodes .STATUS_CODE_OK , "" );
74+
75+ byte [] signature =
76+ IamUtils .sign (
77+ CLIENT_EMAIL ,
78+ credentials ,
79+ transportFactory .transport ,
80+ expectedSignature ,
81+ ImmutableMap .of ());
82+ assertArrayEquals (expectedSignature , signature );
83+
84+ assertEquals (1 , transportFactory .transport .getNumRequests ());
85+ }
86+
87+ // The SignBlob RPC will retry up to three times before it gives up. This test will return two
88+ // 5xx status codes before returning a success. This test covers the cases where the number of
89+ // retry attempts is below the configured retry attempt count bounds (3 attempts).
90+ @ Test
91+ public void sign_retryTwoTimes_success () {
92+ byte [] expectedSignature = {0xD , 0xE , 0xA , 0xD };
6093
6194 ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory transportFactory =
6295 new ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory ();
96+ transportFactory .transport .addStatusCodeAndMessage (
97+ HttpStatusCodes .STATUS_CODE_BAD_GATEWAY , "Bad Gateway" );
98+ transportFactory .transport .addStatusCodeAndMessage (
99+ HttpStatusCodes .STATUS_CODE_SERVICE_UNAVAILABLE , "Unavailable" );
100+ transportFactory .transport .addStatusCodeAndMessage (HttpStatusCodes .STATUS_CODE_OK , "" );
63101 transportFactory .transport .setSignedBlob (expectedSignature );
64102 transportFactory .transport .setTargetPrincipal (CLIENT_EMAIL );
65103
@@ -71,22 +109,97 @@ public void sign_noRetry() throws IOException {
71109 expectedSignature ,
72110 ImmutableMap .of ());
73111 assertArrayEquals (expectedSignature , signature );
112+
113+ // Expect that three requests are made (2 failures which are retries + 1 final requests which
114+ // resulted in a successful response)
115+ assertEquals (3 , transportFactory .transport .getNumRequests ());
74116 }
75117
118+ // The rpc will retry up to three times before it gives up. This test will enqueue three failed
119+ // status codes + messages before returning a success. After the third retry attempt, the request
120+ // will try one last time and the result will be reported back to the user.
76121 @ Test
77- public void sign_4xxServerError_exception () throws IOException {
122+ public void sign_retryThreeTimes_success () {
78123 byte [] expectedSignature = {0xD , 0xE , 0xA , 0xD };
79124
80- // Mock this call because signing requires an access token. The call is initialized with
81- // HttpCredentialsAdapter which will make a call to get the access token
82- ServiceAccountCredentials credentials = Mockito .mock (ServiceAccountCredentials .class );
83- Mockito .when (credentials .getRequestMetadata (Mockito .any ())).thenReturn (ImmutableMap .of ());
125+ ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory transportFactory =
126+ new ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory ();
127+ transportFactory .transport .setSignedBlob (expectedSignature );
128+ transportFactory .transport .setTargetPrincipal (CLIENT_EMAIL );
129+ transportFactory .transport .addStatusCodeAndMessage (
130+ HttpStatusCodes .STATUS_CODE_BAD_GATEWAY , "Bad Gateway" );
131+ transportFactory .transport .addStatusCodeAndMessage (
132+ HttpStatusCodes .STATUS_CODE_SERVICE_UNAVAILABLE , "Unavailable" );
133+ transportFactory .transport .addStatusCodeAndMessage (
134+ HttpStatusCodes .STATUS_CODE_SERVER_ERROR , "Server Error" );
135+ transportFactory .transport .addStatusCodeAndMessage (HttpStatusCodes .STATUS_CODE_OK , "" );
136+
137+ byte [] signature =
138+ IamUtils .sign (
139+ CLIENT_EMAIL ,
140+ credentials ,
141+ transportFactory .transport ,
142+ expectedSignature ,
143+ ImmutableMap .of ());
144+ assertArrayEquals (expectedSignature , signature );
145+
146+ // Expect that three requests are made (3 failures which are retried + 1 final request which
147+ // resulted the final success response)
148+ assertEquals (4 , transportFactory .transport .getNumRequests ());
149+ }
150+
151+ // The rpc will retry up to three times before it gives up. This test will enqueue four failed
152+ // status codes + messages before returning a success. After the third retry attempt, the request
153+ // will try one last time and the result will be reported back to the user.
154+ @ Test
155+ public void sign_retryThreeTimes_exception () {
156+ byte [] expectedSignature = {0xD , 0xE , 0xA , 0xD };
84157
85158 ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory transportFactory =
86159 new ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory ();
87160 transportFactory .transport .setSignedBlob (expectedSignature );
88161 transportFactory .transport .setTargetPrincipal (CLIENT_EMAIL );
89- transportFactory .transport .setErrorResponseCodeAndMessage (
162+ transportFactory .transport .addStatusCodeAndMessage (
163+ HttpStatusCodes .STATUS_CODE_BAD_GATEWAY , "Bad Gateway" );
164+ transportFactory .transport .addStatusCodeAndMessage (
165+ HttpStatusCodes .STATUS_CODE_SERVICE_UNAVAILABLE , "Unavailable" );
166+ transportFactory .transport .addStatusCodeAndMessage (
167+ HttpStatusCodes .STATUS_CODE_SERVER_ERROR , "Server Error" );
168+ transportFactory .transport .addStatusCodeAndMessage (
169+ HttpStatusCodes .STATUS_CODE_BAD_GATEWAY , "Bad Gateway" );
170+ transportFactory .transport .addStatusCodeAndMessage (HttpStatusCodes .STATUS_CODE_OK , "" );
171+
172+ ServiceAccountSigner .SigningException exception =
173+ assertThrows (
174+ ServiceAccountSigner .SigningException .class ,
175+ () ->
176+ IamUtils .sign (
177+ CLIENT_EMAIL ,
178+ credentials ,
179+ transportFactory .transport ,
180+ expectedSignature ,
181+ ImmutableMap .of ()));
182+ assertTrue (exception .getMessage ().contains ("Failed to sign the provided bytes" ));
183+ assertTrue (
184+ exception
185+ .getCause ()
186+ .getMessage ()
187+ .contains ("Unexpected Error code 502 trying to sign provided bytes" ));
188+
189+ // Expect that three requests are made (3 failures which are retried + 1 final request which
190+ // resulted in another failed response)
191+ assertEquals (4 , transportFactory .transport .getNumRequests ());
192+ }
193+
194+ @ Test
195+ public void sign_4xxError_noRetry_exception () {
196+ byte [] expectedSignature = {0xD , 0xE , 0xA , 0xD };
197+
198+ ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory transportFactory =
199+ new ImpersonatedCredentialsTest .MockIAMCredentialsServiceTransportFactory ();
200+ transportFactory .transport .setSignedBlob (expectedSignature );
201+ transportFactory .transport .setTargetPrincipal (CLIENT_EMAIL );
202+ transportFactory .transport .addStatusCodeAndMessage (
90203 HttpStatusCodes .STATUS_CODE_UNAUTHORIZED , "Failed to sign the provided bytes" );
91204
92205 ServiceAccountSigner .SigningException exception =
@@ -105,5 +218,8 @@ public void sign_4xxServerError_exception() throws IOException {
105218 .getCause ()
106219 .getMessage ()
107220 .contains ("Error code 401 trying to sign provided bytes:" ));
221+
222+ // Only one request will have been made for a 4xx error (no retries)
223+ assertEquals (1 , transportFactory .transport .getNumRequests ());
108224 }
109225}
0 commit comments