public void processIssuesByBatch(DbSession dbSession, Set<String> issueKeysSnapshot, Consumer<List<IssueDto>> listConsumer, Predicate<? super IssueDto> filter) {
    boolean hasMoreIssues = !issueKeysSnapshot.isEmpty();
    long offset = 0;

    List<IssueDto> issueDtos = new ArrayList<>();

    while (hasMoreIssues) {
      Set<String> page = paginate(issueKeysSnapshot, offset);
      List<IssueDto> nextOpenIssues = nextOpenIssues(dbSession, page)
        .stream()
        .filter(filter)
        .toList();
      issueDtos.addAll(nextOpenIssues);
      offset += page.size();
      hasMoreIssues = offset < issueKeysSnapshot.size();
    }

    listConsumer.accept(issueDtos);
  }
@Test
  public void processIssuesByBatch_correctly_processes_all_issues_regardless_of_creation_timestamp() {
    var pullActionIssuesRetriever = new PullActionIssuesRetriever(dbClient, queryParams);
    List<IssueDto> issuesWithSameCreationTimestamp = IntStream.rangeClosed(1, 100).mapToObj(i -> new IssueDto()
      .setKee(Integer.toString(i)).setCreatedAt(100L)).toList();
    when(issueDao.selectByBranch(any(), any(), any()))
      .thenReturn(issuesWithSameCreationTimestamp);
    List<IssueDto> returnedDtos = new ArrayList<>();
    Consumer<List<IssueDto>> listConsumer = returnedDtos::addAll;

    Set<String> issueKeysSnapshot = issuesWithSameCreationTimestamp.stream().map(IssueDto::getKee).collect(Collectors.toSet());
    pullActionIssuesRetriever.processIssuesByBatch(dbClient.openSession(true), issueKeysSnapshot, listConsumer, issueDto -> true);

    assertThat(returnedDtos).hasSize(100);
  }