2121import java .io .InputStream ;
2222import java .io .IOException ;
2323import java .io .OutputStream ;
24+ import java .util .ArrayList ;
2425import java .util .Collections ;
2526import java .util .List ;
2627import java .util .Map ;
@@ -65,20 +66,28 @@ public void testRetrySuccess() throws DbxException, IOException {
6566 DbxClientV1 client = new DbxClientV1 (config , "fakeAccessToken" );
6667 String json = "{\" reset\" :true,\" entries\" :[],\" cursor\" :\" fakeCursor\" ,\" has_more\" :true}" ;
6768
69+
6870 // 503 twice, then return result
6971 HttpRequestor .Uploader mockUploader = mockUploader ();
7072 when (mockUploader .finish ())
71- .thenReturn (createEmptyResponse (503 ))
72- .thenReturn (createEmptyResponse (503 ))
73+ .thenReturn (createEmptyResponse (503 )) // no backoff
74+ .thenReturn (createRateLimitResponse (1 )) // backoff 1 sec
75+ .thenReturn (createRateLimitResponse (2 )) // backoff 2 sec
7376 .thenReturn (createSuccessResponse (json ));
7477
7578 when (mockRequestor .startPost (anyString (), anyHeaders ()))
7679 .thenReturn (mockUploader );
7780
81+ long start = System .currentTimeMillis ();
7882 DbxDelta <DbxEntry > actual = client .getDelta (null );
83+ long end = System .currentTimeMillis ();
7984
80- // should have only been called 3 times: initial call + 2 retries
81- verify (mockRequestor , times (3 )).startPost (anyString (), anyHeaders ());
85+ // no way easy way to properly test this, but request should
86+ // have taken AT LEAST 3 seconds due to backoff.
87+ assertTrue ((end - start ) >= 3000L , "duration: " + (end - start ) + " millis" );
88+
89+ // should have been called 4 times: initial call + 3 retries
90+ verify (mockRequestor , times (4 )).startPost (anyString (), anyHeaders ());
8291
8392 assertEquals (actual .reset , true );
8493 assertEquals (actual .cursor , "fakeCursor" );
@@ -178,14 +187,20 @@ private static HttpRequestor.Response createEmptyResponse(int statusCode) {
178187 );
179188 }
180189
181- private static HttpRequestor .Response createDownloaderResponse (byte [] body , String header , String json ) {
182- Map <String , List <String >> headers = new TreeMap <String , List <String >>(String .CASE_INSENSITIVE_ORDER );
183- headers .put (header , Collections .<String >singletonList (json ));
190+ private static HttpRequestor .Response createRateLimitResponse (long backoffSeconds ) {
191+ byte [] body = new byte [0 ];
192+ return new HttpRequestor .Response (
193+ 503 , // API v1 uses 503 for rate limits
194+ new ByteArrayInputStream (body ),
195+ headers ("Retry-After" , Long .toString (backoffSeconds ))
196+ );
197+ }
184198
199+ private static HttpRequestor .Response createDownloaderResponse (byte [] body , String header , String json ) {
185200 return new HttpRequestor .Response (
186201 200 ,
187202 new ByteArrayInputStream (body ),
188- headers
203+ headers ( header , json )
189204 );
190205 }
191206
@@ -210,6 +225,27 @@ public OutputStream answer(InvocationOnMock invocation) {
210225 return uploader ;
211226 }
212227
228+ private static Map <String , List <String >> headers (String name , String value , String ... rest ) {
229+ assertTrue (rest .length % 2 == 0 );
230+
231+ Map <String , List <String >> headers = new TreeMap <String , List <String >>(String .CASE_INSENSITIVE_ORDER );
232+ List <String > values = new ArrayList <String >();
233+ headers .put (name , values );
234+ values .add (value );
235+ for (int i = 0 ; i < rest .length ; i += 2 ) {
236+ name = rest [i ];
237+ value = rest [i +1 ];
238+ values = headers .get (name );
239+ if (values == null ) {
240+ values = new ArrayList <String >();
241+ headers .put (name , values );
242+ }
243+ values .add (value );
244+ }
245+
246+ return headers ;
247+ }
248+
213249 private static Iterable <HttpRequestor .Header > anyHeaders () {
214250 return Matchers .<Iterable <HttpRequestor .Header >>any ();
215251 }
0 commit comments