### Question:
RequestDataStep { public CompletableFuture<List<Task<NodeDataRequest>>> requestData( final List<Task<NodeDataRequest>> requestTasks, final BlockHeader blockHeader, final WorldDownloadState downloadState) { final List<Hash> hashes = requestTasks.stream() .map(Task::getData) .map(NodeDataRequest::getHash) .distinct() .collect(Collectors.toList()); return sendRequest(blockHeader, hashes, downloadState) .thenApply( data -> { for (final Task<NodeDataRequest> task : requestTasks) { final NodeDataRequest request = task.getData(); final Bytes matchingData = data.get(request.getHash()); if (matchingData != null) { request.setData(matchingData); } } return requestTasks; }); } RequestDataStep(final EthContext ethContext, final MetricsSystem metricsSystem);   RequestDataStep(
      final BiFunction<List<Hash>, Long, EthTask<Map<Hash, Bytes>>> getNodeDataTaskFactory); CompletableFuture<List<Task<NodeDataRequest>>> requestData(
      final List<Task<NodeDataRequest>> requestTasks,
      final BlockHeader blockHeader,
      final WorldDownloadState downloadState);  }### Answer:
@Test public void shouldRequestDistinctHashesForTasks() { final StubTask task1 = StubTask.forHash(HASH1); final StubTask task2 = StubTask.forHash(HASH2); final StubTask task3 = StubTask.forHash(HASH1); final List<Task<NodeDataRequest>> tasks = asList(task1, task2, task3); when(getNodeDataTaskFactory.apply(asList(HASH1, HASH2), BLOCK_NUMBER)).thenReturn(ethTask); final CompletableFuture<List<Task<NodeDataRequest>>> result = requestDataStep.requestData(tasks, blockHeader, downloadState); assertThat(result).isNotDone(); getDataFuture.complete(ImmutableMap.of(HASH1, DATA1, HASH2, DATA2)); assertThat(result).isDone(); assertThat(result).isCompletedWithValue(tasks); assertThat(task1.getData().getData()).isEqualTo(DATA1); assertThat(task2.getData().getData()).isEqualTo(DATA2); assertThat(task3.getData().getData()).isEqualTo(DATA1); verify(downloadState).requestComplete(true); }
@Test public void shouldReportNoProgressWhenRequestCompletesWithNoData() { final StubTask task1 = StubTask.forHash(HASH1); final List<Task<NodeDataRequest>> tasks = singletonList(task1); when(getNodeDataTaskFactory.apply(singletonList(HASH1), BLOCK_NUMBER)).thenReturn(ethTask); final CompletableFuture<List<Task<NodeDataRequest>>> result = requestDataStep.requestData(tasks, blockHeader, downloadState); assertThat(result).isNotDone(); getDataFuture.complete(emptyMap()); assertThat(result).isDone(); assertThat(result).isCompletedWithValue(tasks); verify(downloadState).requestComplete(false); }
@Test public void shouldNotReportNoProgressWhenTaskFails() { final StubTask task1 = StubTask.forHash(HASH1); final List<Task<NodeDataRequest>> tasks = singletonList(task1); when(getNodeDataTaskFactory.apply(singletonList(HASH1), BLOCK_NUMBER)).thenReturn(ethTask); final CompletableFuture<List<Task<NodeDataRequest>>> result = requestDataStep.requestData(tasks, blockHeader, downloadState); assertThat(result).isNotDone(); getDataFuture.completeExceptionally(new RuntimeException()); assertThat(result).isDone(); assertThat(result).isCompletedWithValue(tasks); verify(downloadState, never()).requestComplete(anyBoolean()); }
@Test public void shouldTrackOutstandingTasks() { final StubTask task1 = StubTask.forHash(HASH1); final List<Task<NodeDataRequest>> tasks = singletonList(task1); when(getNodeDataTaskFactory.apply(singletonList(HASH1), BLOCK_NUMBER)).thenReturn(ethTask); requestDataStep.requestData(tasks, blockHeader, downloadState); verify(downloadState).addOutstandingTask(ethTask); getDataFuture.complete(emptyMap()); verify(downloadState).removeOutstandingTask(ethTask); }