package com.nurkiewicz.asyncretry;
import com.nurkiewicz.asyncretry.policy.AbortRetryException;
import org.assertj.core.api.Assertions;
import org.mockito.InOrder;
import org.testng.annotations.Test;
import java.io.IOException;
import java.net.SocketException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static com.nurkiewicz.asyncretry.backoff.FixedIntervalBackoff.DEFAULT_PERIOD_MILLIS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.times;
/**
* @author Tomasz Nurkiewicz
* @since 7/21/13, 7:07 PM
*/
public class AsyncRetryJobTest extends AbstractBaseTestCase {
@Test
public void shouldUnwrapUserFutureAndReturnIt() throws Exception {
//given
final RetryExecutor executor = new AsyncRetryExecutor(schedulerMock);
given(serviceMock.safeAsync()).willReturn(CompletableFuture.completedFuture("42"));
//when
final CompletableFuture<String> future = executor.getFutureWithRetry(ctx -> serviceMock.safeAsync());
//then
assertThat(future.get()).isEqualTo("42");
}
@Test
public void shouldSucceedAfterFewAsynchronousRetries() throws Exception {
//given
final RetryExecutor executor = new AsyncRetryExecutor(schedulerMock);
given(serviceMock.safeAsync()).willReturn(
failedAsync(new SocketException("First")),
failedAsync(new IOException("Second")),
CompletableFuture.completedFuture("42")
);
//when
final CompletableFuture<String> future = executor.getFutureWithRetry(ctx -> serviceMock.safeAsync());
//then
assertThat(future.get()).isEqualTo("42");
}
@Test
public void shouldScheduleTwoTimesWhenRetries() throws Exception {
//given
final RetryExecutor executor = new AsyncRetryExecutor(schedulerMock);
given(serviceMock.safeAsync()).willReturn(
failedAsync(new SocketException("First")),
failedAsync(new IOException("Second")),
CompletableFuture.completedFuture("42")
);
//when
final CompletableFuture<String> future = executor.getFutureWithRetry(ctx -> serviceMock.safeAsync());
//then
future.get();
final InOrder order = inOrder(schedulerMock);
order.verify(schedulerMock).schedule(notNullRunnable(), eq(0L), millis());
order.verify(schedulerMock, times(2)).schedule(notNullRunnable(), eq(DEFAULT_PERIOD_MILLIS), millis());
}
private CompletableFuture<String> failedAsync(Throwable throwable) {
final CompletableFuture<String> future = new CompletableFuture<>();
future.completeExceptionally(throwable);
return future;
}
@Test
public void shouldRethrowOriginalExceptionFromUserFutureCompletion() throws Exception {
//given
final RetryExecutor executor = new AsyncRetryExecutor(schedulerMock).
abortOn(SocketException.class);
given(serviceMock.safeAsync()).willReturn(
failedAsync(new SocketException(DON_T_PANIC))
);
//when
final CompletableFuture<String> future = executor.getFutureWithRetry(ctx -> serviceMock.safeAsync());
//then
assertThat(future.isCompletedExceptionally()).isTrue();
try {
future.get();
failBecauseExceptionWasNotThrown(ExecutionException.class);
} catch (ExecutionException e) {
final Throwable cause = e.getCause();
assertThat(cause).isInstanceOf(SocketException.class);
assertThat(cause).hasMessage(DON_T_PANIC);
}
}
@Test
public void shouldAbortWhenTargetFutureWantsToAbort() throws Exception {
//given
final RetryExecutor executor = new AsyncRetryExecutor(schedulerMock);
given(serviceMock.safeAsync()).willReturn(
failedAsync(new AbortRetryException())
);
//when
final CompletableFuture<String> future = executor.getFutureWithRetry(ctx -> serviceMock.safeAsync());
//then
assertThat(future.isCompletedExceptionally()).isTrue();
try {
future.get();
failBecauseExceptionWasNotThrown(ExecutionException.class);
} catch (ExecutionException e) {
final Throwable cause = e.getCause();
assertThat(cause).isInstanceOf(AbortRetryException.class);
}
}
@Test
public void shouldRethrowExceptionThatWasThrownFromUserTaskBeforeReturningFuture() throws Exception {
//given
final RetryExecutor executor = new AsyncRetryExecutor(schedulerMock).
abortOn(IllegalArgumentException.class);
given(serviceMock.safeAsync()).willThrow(new IllegalArgumentException(DON_T_PANIC));
//when
final CompletableFuture<String> future = executor.getFutureWithRetry(ctx -> serviceMock.safeAsync());
//then
assertThat(future.isCompletedExceptionally()).isTrue();
try {
future.get();
Assertions.failBecauseExceptionWasNotThrown(ExecutionException.class);
} catch (ExecutionException e) {
assertThat(e.getCause()).isInstanceOf(IllegalArgumentException.class);
assertThat(e.getCause()).hasMessage(DON_T_PANIC);
}
}
}