### Question:
ApiRateLimiter { public Object run( long clientCustomerId, final Object obj, final Method method, final Object[] args) throws Throwable { Callable<Object> callable = new Callable<Object>() { @Override public Object call() throws Exception { return method.invoke(obj, args); } }; String actionDescription = method.getClass().getSimpleName() + "." + method.getName(); ApiRetryHelper<Object> retryHelper = new ApiRetryHelper<Object>(clientCustomerId, callable, actionDescription, retryStrategy); try { return retryHelper.callWithRetries(); } catch (ApiInvocationException e) { throw e.getCause(); } } ApiRateLimiter(ApiRetryStrategy retryStrategy); Object run(
      long clientCustomerId, final Object obj, final Method method, final Object[] args);  }### Answer:
@Test public void testPass() throws Throwable { when(mockService.invoke()).thenReturn(DUMMY_OBJECT); rateLimiter.run(TEST_CID, mockService, method, EMPTY_ARGS); }
@Test public void testPassAfterOneRetry() throws Throwable { when(mockService.invoke()).thenThrow(rateExceededException).thenReturn(DUMMY_OBJECT); long startTime = System.currentTimeMillis(); rateLimiter.run(TEST_CID, mockService, method, EMPTY_ARGS); long endTime = System.currentTimeMillis(); long duration = endTime - startTime; long minWaitMillis = SECONDS.toMillis(RETRY_AFTER_SECONDS * ApiServicesRetryStrategy.MIN_WAIT_TIME_MULTIPLIER); assertTrue("Unexpected execution duration!", duration > minWaitMillis); }
@Test public void testFailWithRateExceededError() throws Throwable { when(mockService.invoke()).thenThrow(rateExceededException); long startTime = System.currentTimeMillis(); try { rateLimiter.run(TEST_CID, mockService, method, EMPTY_ARGS); } catch (Throwable e) { assertExceptionType(e, ApiException.class); } long endTime = System.currentTimeMillis(); long duration = endTime - startTime; int waitedAttempts = MAX_ATTEMPTS_ON_RATE_EXCEEDED_ERROR - 1; long minWaitMillis = SECONDS.toMillis( RETRY_AFTER_SECONDS * ApiServicesRetryStrategy.MIN_WAIT_TIME_MULTIPLIER * waitedAttempts); assertTrue("Unexpected execution duration!", duration > minWaitMillis); }
@Test public void testMaxWaitTimeWithRateExceededError() throws Throwable { when(mockService.invoke()).thenThrow(rateExceededExceptionLong); try { rateLimiter.run(TEST_CID, mockService, method, EMPTY_ARGS); } catch (Throwable e) { assertExceptionType(e, RateLimiterException.class); assertExceptionCause(e, ApiException.class); } }
@Test public void testFailWithOtherApiException() throws Throwable { when(mockService.invoke()).thenThrow(otherApiException); thrown.expect(ApiException.class); rateLimiter.run(TEST_CID, mockService, method, EMPTY_ARGS); }
@Test public void testFailWithRemoteException() throws Throwable { when(mockService.invoke()).thenThrow(remoteException); thrown.expect(RemoteException.class); rateLimiter.run(TEST_CID, mockService, method, EMPTY_ARGS); }