### Question:
RetryingDutyLoader implements DutyLoader { @Override public SafeFuture<ScheduledDuties> loadDutiesForEpoch(final UInt64 epoch) { final SafeFuture<ScheduledDuties> duties = new SafeFuture<>(); requestDuties(epoch, duties).propagateTo(duties); return duties; } RetryingDutyLoader(final AsyncRunner asyncRunner, final DutyLoader delegate); @Override SafeFuture<ScheduledDuties> loadDutiesForEpoch(final UInt64 epoch);  }### Answer:
@Test public void shouldReturnDutiesWhenLoadedSuccessfully() { when(delegate.loadDutiesForEpoch(ONE)).thenReturn(SafeFuture.completedFuture(scheduledDuties)); assertThat(dutyLoader.loadDutiesForEpoch(ONE)).isCompletedWithValue(scheduledDuties); }
@Test public void shouldRetryWhenRequestForDutiesFailsBecauseNodeIsSyncing() { when(delegate.loadDutiesForEpoch(ONE)) .thenReturn(NodeSyncingException.failedFuture()) .thenReturn(SafeFuture.completedFuture(scheduledDuties)); final SafeFuture<ScheduledDuties> result = dutyLoader.loadDutiesForEpoch(ONE); assertThat(result).isNotDone(); assertThat(asyncRunner.hasDelayedActions()).isTrue(); asyncRunner.executeQueuedActions(); assertThat(result).isCompletedWithValue(scheduledDuties); }
@Test public void shouldRetryWhenRequestForDutiesFailsBecauseNodeDataIsUnavailable() { when(delegate.loadDutiesForEpoch(ONE)) .thenReturn(SafeFuture.failedFuture(new NodeDataUnavailableException("Sorry"))) .thenReturn(SafeFuture.completedFuture(scheduledDuties)); final SafeFuture<ScheduledDuties> result = dutyLoader.loadDutiesForEpoch(ONE); assertThat(result).isNotDone(); assertThat(asyncRunner.hasDelayedActions()).isTrue(); asyncRunner.executeQueuedActions(); assertThat(result).isCompletedWithValue(scheduledDuties); }
@Test public void shouldRetryWhenUnexpectedErrorOccurs() { when(delegate.loadDutiesForEpoch(ONE)) .thenReturn(SafeFuture.failedFuture(new RuntimeException("No way"))) .thenReturn(SafeFuture.completedFuture(scheduledDuties)); final SafeFuture<ScheduledDuties> result = dutyLoader.loadDutiesForEpoch(ONE); assertThat(result).isNotDone(); assertThat(asyncRunner.hasDelayedActions()).isTrue(); asyncRunner.executeQueuedActions(); assertThat(result).isCompletedWithValue(scheduledDuties); }
@Test public void shouldStopRetryingWhenFutureIsCancelled() { final RuntimeException error = new RuntimeException("No way"); final SafeFuture<ScheduledDuties> delegateResponse = new SafeFuture<>(); when(delegate.loadDutiesForEpoch(ONE)).thenReturn(delegateResponse); final SafeFuture<ScheduledDuties> result = dutyLoader.loadDutiesForEpoch(ONE); verify(delegate).loadDutiesForEpoch(ONE); assertThat(result).isNotDone(); result.cancel(false); delegateResponse.completeExceptionally(error); assertThat(asyncRunner.hasDelayedActions()).isFalse(); verifyNoMoreInteractions(delegate); }