private void shouldCompleteFutureAfterAttemptsInCaseOfExceptionAtAsyncStage(int noOfAttempts) { CompletableFuture<String> failedFuture = new CompletableFuture<>(); failedFuture.completeExceptionally(new WebServiceException("BAM!")); // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()) .willReturn(failedFuture); // Create a Retry with default configuration AsyncRetry retryContext = AsyncRetry.of( "id", RetryConfig .custom() .maxAttempts(noOfAttempts) .build()); // Decorate the invocation of the HelloWorldService Supplier<CompletionStage<String>> supplier = AsyncRetry.decorateCompletionStage( retryContext, scheduler, () -> helloWorldService.returnHelloWorld()); // When Try<String> resultTry = Try.of(() -> awaitResult(supplier.get())); // Then the helloWorldService should be invoked n + 1 times BDDMockito.then(helloWorldService).should(Mockito.times(noOfAttempts)).returnHelloWorld(); Assertions.assertThat(resultTry.isFailure()).isTrue(); Assertions.assertThat(resultTry.getCause().getCause()).isInstanceOf(WebServiceException.class); }
@Test public void waitIntervalOfTenMillisShouldSucceed() { RetryConfig config = RetryConfig.custom().waitDuration(Duration.ofMillis(10)).build(); Assertions.assertThat(config).isNotNull(); }
@Test() public void shouldUseBothRecordToBuildPredicate() { RetryConfig retryConfig = RetryConfig.custom() .retryOnException(TEST_PREDICATE) //1 .retryExceptions(RuntimeException.class, ExtendsExtendsException.class) //2 .ignoreExceptions(ExtendsException.class, ExtendsRuntimeException.class) //3 .build(); final Predicate<? super Throwable> failurePredicate = retryConfig.getExceptionPredicate(); then(failurePredicate.test(new Exception())).isEqualTo(false); // not explicitly included then(failurePredicate.test(new Exception("test"))).isEqualTo(true); // explicitly included by 1 then(failurePredicate.test(new ExtendsError())).isEqualTo(false); // ot explicitly included then(failurePredicate.test(new ExtendsException())).isEqualTo(false); // explicitly excluded by 3 then(failurePredicate.test(new ExtendsException("test"))).isEqualTo(false); // explicitly excluded by 3 even if included by 1 then(failurePredicate.test(new ExtendsException2())).isEqualTo(false); // not explicitly included then(failurePredicate.test(new RuntimeException())).isEqualTo(true); // explicitly included by 2 then(failurePredicate.test(new ExtendsRuntimeException())).isEqualTo(false); // explicitly excluded by 3 then(failurePredicate.test(new ExtendsExtendsException())).isEqualTo(false); // inherits excluded from ExtendsException by 3 }
} else { retry = retryRegistry.retry(name, RetryConfig.custom() .maxAttempts(retryConfig.getMaxAttempts()) .waitDuration(Duration.ofMillis(retryConfig.getWaitDurationInMillis())) .build());
} else { retry = retryRegistry.retry(name, RetryConfig.custom() .maxAttempts(retryConfig.getMaxAttempts()) .waitDuration(Duration.ofMillis(retryConfig.getWaitDurationInMillis())) .build());
@Test public void shouldReturnAfterOneAttemptAndIgnoreException() { // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new WebServiceException("BAM!")); // Create a Retry with default configuration RetryConfig config = RetryConfig.custom() .retryOnException(throwable -> API.Match(throwable).of( API.Case($(Predicates.instanceOf(WebServiceException.class)), false), API.Case($(), true))) .build(); Retry retry = Retry.of("id", config); // Decorate the invocation of the HelloWorldService CheckedFunction0<String> retryableSupplier = Retry .decorateCheckedSupplier(retry, helloWorldService::returnHelloWorld); // When Try<String> result = Try.of(retryableSupplier); // Then the helloWorldService should be invoked only once, because the exception should be rethrown immediately. BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); // and the result should be a failure assertThat(result.isFailure()).isTrue(); // and the returned exception should be of type RuntimeException assertThat(result.failed().get()).isInstanceOf(WebServiceException.class); assertThat(sleptTime).isEqualTo(0); }
@Test public void shouldIgnoreError() { // Given the HelloWorldService throws an exception BDDMockito.willThrow(new WebServiceException("BAM!")).willDoNothing().given(helloWorldService).sayHelloWorld(); // Create a Retry with default configuration RetryConfig config = RetryConfig.custom() .retryOnException(t -> t instanceof IOException) .maxAttempts(3).build(); Retry retry = Retry.of("id", config); TestSubscriber<RetryEvent.Type> testSubscriber = toFlowable(retry.getEventPublisher()) .map(RetryEvent::getEventType) .test(); // Decorate the invocation of the HelloWorldService CheckedRunnable retryableRunnable = Retry.decorateCheckedRunnable(retry, helloWorldService::sayHelloWorld); // When Try<Void> result = Try.run(retryableRunnable); // Then the helloWorldService should be invoked 2 times BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorld(); // and the result should be a sucess Assertions.assertThat(result.isFailure()).isTrue(); Assertions.assertThat(sleptTime).isEqualTo(0); testSubscriber.assertValueCount(1).assertValues(RetryEvent.Type.IGNORED_ERROR); } }
@Test public void shouldNotRetryWithThatResult() throws InterruptedException, ExecutionException, TimeoutException { // Given the HelloWorldService returns Hello world BDDMockito.given(helloWorldService.returnHelloWorld()) .willReturn(completedFuture("Hello world")); // Create a Retry with default configuration final RetryConfig retryConfig = RetryConfig.<String>custom().retryOnResult(s -> s.contains("NoRetry")) .maxAttempts(1) .build(); AsyncRetry retryContext = AsyncRetry.of("id", retryConfig); // Decorate the invocation of the HelloWorldService Supplier<CompletionStage<String>> supplier = AsyncRetry.decorateCompletionStage( retryContext, scheduler, () -> helloWorldService.returnHelloWorld()); // When String result = awaitResult(supplier); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); Assertions.assertThat(result).isEqualTo("Hello world"); // for code quality scan , it does not not recognize assertJ do not why Assert.assertEquals(result, "Hello world"); }
private void shouldCompleteFutureAfterAttemptsInCaseOfRetyOnResultAtAsyncStage(int noOfAttempts, String retryResponse) { // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()) .willReturn(completedFuture("Hello world")); // Create a Retry with default configuration AsyncRetry retryContext = AsyncRetry.of( "id", RetryConfig .<String>custom() .maxAttempts(noOfAttempts) .retryOnResult(s -> s.contains(retryResponse)) .build()); // Decorate the invocation of the HelloWorldService Supplier<CompletionStage<String>> supplier = AsyncRetry.decorateCompletionStage( retryContext, scheduler, () -> helloWorldService.returnHelloWorld()); // When Try<String> resultTry = Try.of(() -> awaitResult(supplier.get())); // Then the helloWorldService should be invoked n + 1 times BDDMockito.then(helloWorldService).should(Mockito.times(noOfAttempts)).returnHelloWorld(); Assert.assertTrue(resultTry.isSuccess()); }
@Test public void shouldTakeIntoAccountBackoffFunction() { // Given the HelloWorldService throws an exception BDDMockito.willThrow(new WebServiceException("BAM!")).given(helloWorldService).sayHelloWorld(); // Create a Retry with a backoff function squaring the interval RetryConfig config = RetryConfig .custom() .intervalFunction(IntervalFunction.of(Duration.ofMillis(500), x -> x * x)) .build(); Retry retry = Retry.of("id", config); // Decorate the invocation of the HelloWorldService CheckedRunnable retryableRunnable = Retry.decorateCheckedRunnable(retry, helloWorldService::sayHelloWorld); // When Try<Void> result = Try.run(retryableRunnable); // Then the slept time should be according to the backoff function BDDMockito.then(helloWorldService).should(Mockito.times(3)).sayHelloWorld(); Assertions.assertThat(sleptTime).isEqualTo( RetryConfig.DEFAULT_WAIT_DURATION + RetryConfig.DEFAULT_WAIT_DURATION*RetryConfig.DEFAULT_WAIT_DURATION); } }
@Test public void shouldNotRetryFromPredicateUsingObservable() { //Given RetryConfig config = RetryConfig.custom() .retryOnException(t -> t instanceof IOException) .maxAttempts(3).build(); Retry retry = Retry.of("testName", config); given(helloWorldService.returnHelloWorld()) .willThrow(new WebServiceException("BAM!")); //When Observable.fromCallable(helloWorldService::returnHelloWorld) .compose(RetryTransformer.of(retry)) .test() .assertError(WebServiceException.class) .assertNotComplete() .assertSubscribed(); //Then BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); Retry.Metrics metrics = retry.getMetrics(); assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0); }
private void shouldCompleteFutureAfterAttemptsInCaseOfExceptionAtSyncStage(int noOfAttempts) { // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()) .willThrow(new WebServiceException("BAM!")); // Create a Retry with default configuration AsyncRetry retryContext = AsyncRetry.of( "id", RetryConfig .custom() .maxAttempts(noOfAttempts) .build()); // Decorate the invocation of the HelloWorldService Supplier<CompletionStage<String>> supplier = AsyncRetry.decorateCompletionStage( retryContext, scheduler, () -> helloWorldService.returnHelloWorld()); // When Try<String> resultTry = Try.of(() -> awaitResult(supplier.get())); // Then the helloWorldService should be invoked n + 1 times BDDMockito.then(helloWorldService).should(Mockito.times(noOfAttempts)).returnHelloWorld(); Assertions.assertThat(resultTry.isFailure()).isTrue(); Assertions.assertThat(resultTry.getCause().getCause()).isInstanceOf(WebServiceException.class); }
@Test public void shouldReturnAfterOneAttemptAndIgnoreException() { // Given the HelloWorldService throws an exception BDDMockito.willThrow(new WebServiceException("BAM!")).given(helloWorldService).sayHelloWorld(); // Create a Retry with default configuration RetryConfig config = RetryConfig.custom() .retryOnException(throwable -> Match(throwable).of( Case($(Predicates.instanceOf(WebServiceException.class)), false), Case($(), true))) .build(); Retry retry = Retry.of("id", config); // Decorate the invocation of the HelloWorldService CheckedRunnable retryableRunnable = Retry.decorateCheckedRunnable(retry, helloWorldService::sayHelloWorld); // When Try<Void> result = Try.run(retryableRunnable); // Then the helloWorldService should be invoked only once, because the exception should be rethrown immediately. BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorld(); // and the result should be a failure Assertions.assertThat(result.isFailure()).isTrue(); // and the returned exception should be of type RuntimeException Assertions.assertThat(result.failed().get()).isInstanceOf(WebServiceException.class); Assertions.assertThat(sleptTime).isEqualTo(0); }
@Test public void shouldTakeIntoAccountBackoffFunction() { // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new WebServiceException("BAM!")); // Create a Retry with a backoff function doubling the interval RetryConfig config = RetryConfig.custom().intervalFunction(IntervalFunction.ofExponentialBackoff(500, 2.0)) .build(); Retry retry = Retry.of("id", config); // Decorate the invocation of the HelloWorldService CheckedFunction0<String> retryableSupplier = Retry .decorateCheckedSupplier(retry, helloWorldService::returnHelloWorld); // When Try<String> result = Try.of(retryableSupplier); // Then the slept time should be according to the backoff function BDDMockito.then(helloWorldService).should(Mockito.times(3)).returnHelloWorld(); assertThat(sleptTime).isEqualTo( RetryConfig.DEFAULT_WAIT_DURATION + RetryConfig.DEFAULT_WAIT_DURATION * 2); } }
@Test public void shouldNotRetryFromPredicateUsingSingle() { //Given RetryConfig config = RetryConfig.custom() .retryOnException(t -> t instanceof IOException) .maxAttempts(3).build(); Retry retry = Retry.of("testName", config); given(helloWorldService.returnHelloWorld()) .willThrow(new WebServiceException("BAM!")); //When Single.fromCallable(helloWorldService::returnHelloWorld) .compose(RetryTransformer.of(retry)) .test() .assertError(WebServiceException.class) .assertNotComplete() .assertSubscribed(); //Then BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); Retry.Metrics metrics = retry.getMetrics(); assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0); }
@Test public void shouldReturnAfterThreeAttemptsAndRecoverWithResult() { // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new WebServiceException("BAM!")) .willReturn("Hello world") .willThrow(new WebServiceException("BAM!")); // Create a Retry with default configuration final RetryConfig tryAgain = RetryConfig.<String>custom().retryOnResult(s -> s.contains("Hello world")) .maxAttempts(3).build(); Retry retry = Retry.of("id", tryAgain); // Decorate the invocation of the HelloWorldService CheckedFunction0<String> retryableSupplier = Retry .decorateCheckedSupplier(retry, helloWorldService::returnHelloWorld); // When Try<String> result = Try.of(retryableSupplier).recover((throwable) -> "Hello world from recovery function"); // Then the helloWorldService should be invoked 3 times BDDMockito.then(helloWorldService).should(Mockito.times(3)).returnHelloWorld(); // and the returned exception should be of type RuntimeException assertThat(result.get()).isEqualTo("Hello world from recovery function"); assertThat(sleptTime).isEqualTo(RetryConfig.DEFAULT_WAIT_DURATION * 2); }
@Test public void shouldNotRetryFromPredicateUsingFlowable() { //Given RetryConfig config = RetryConfig.custom() .retryOnException(t -> t instanceof IOException) .maxAttempts(3).build(); Retry retry = Retry.of("testName", config); given(helloWorldService.returnHelloWorld()) .willThrow(new WebServiceException("BAM!")); //When Flowable.fromCallable(helloWorldService::returnHelloWorld) .compose(RetryTransformer.of(retry)) .test() .assertError(WebServiceException.class) .assertNotComplete() .assertSubscribed(); //Then BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); Retry.Metrics metrics = retry.getMetrics(); assertThat(metrics.getNumberOfFailedCallsWithoutRetryAttempt()).isEqualTo(1); assertThat(metrics.getNumberOfFailedCallsWithRetryAttempt()).isEqualTo(0); }
@Test public void shouldReturnAfterOneAttempt() { // Given the HelloWorldService throws an exception BDDMockito.willThrow(new WebServiceException("BAM!")).given(helloWorldService).sayHelloWorld(); // Create a Retry with default configuration RetryConfig config = RetryConfig.custom().maxAttempts(1).build(); Retry retry = Retry.of("id", config); // Decorate the invocation of the HelloWorldService CheckedRunnable retryableRunnable = Retry.decorateCheckedRunnable(retry, helloWorldService::sayHelloWorld); // When Try<Void> result = Try.run(retryableRunnable); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).sayHelloWorld(); // and the result should be a failure Assertions.assertThat(result.isFailure()).isTrue(); // and the returned exception should be of type RuntimeException Assertions.assertThat(result.failed().get()).isInstanceOf(WebServiceException.class); Assertions.assertThat(sleptTime).isEqualTo(0); }
@Test public void shouldReturnAfterOneAttempt() { // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorld()).willThrow(new WebServiceException("BAM!")); // Create a Retry with custom configuration RetryConfig config = RetryConfig.custom().maxAttempts(1).build(); Retry retry = Retry.of("id", config); // Decorate the invocation of the HelloWorldService CheckedFunction0<String> retryableSupplier = Retry .decorateCheckedSupplier(retry, helloWorldService::returnHelloWorld); // When Try<String> result = Try.of(retryableSupplier); // Then the helloWorldService should be invoked 1 time BDDMockito.then(helloWorldService).should(Mockito.times(1)).returnHelloWorld(); // and the result should be a failure assertThat(result.isFailure()).isTrue(); // and the returned exception should be of type RuntimeException assertThat(result.failed().get()).isInstanceOf(WebServiceException.class); assertThat(sleptTime).isEqualTo(0); }
@Test public void testDecorateCallableWithRetryResult() throws Exception { // Given the HelloWorldService throws an exception BDDMockito.given(helloWorldService.returnHelloWorldWithException()).willThrow(new WebServiceException("BAM!")) .willReturn("Hello world"); // Create a Retry with default configuration final RetryConfig tryAgain = RetryConfig.<String>custom().retryOnResult(s -> s.contains("Hello world")) .maxAttempts(2).build(); Retry retry = Retry.of("id", tryAgain); // Decorate the invocation of the HelloWorldService Callable<String> callable = Retry.decorateCallable(retry, helloWorldService::returnHelloWorldWithException); // When String result = callable.call(); // Then the helloWorldService should be invoked 2 times BDDMockito.then(helloWorldService).should(Mockito.times(2)).returnHelloWorldWithException(); assertThat(result).isEqualTo("Hello world"); assertThat(sleptTime).isEqualTo(RetryConfig.DEFAULT_WAIT_DURATION); }