Repository: corona-warn-app/cwa-server
File: services/upload/src/main/java/app/coronawarn/server/services/federation/upload/runner/TestDataGeneration.java (Java)

Code before:
  private List<DiagnosisKey> generateFakeKeysForPreviousDay() {
    long timestamp = getCurrentTimestampTruncatedHour()
        .minusDays(retentionDays)
        .toEpochSecond(ZoneOffset.UTC) / 600L;
    logger.info("Deleting test keys with rolling_start_interval_number less than {}", timestamp);
    keyRepository.applyRetentionToTestKeys((int) timestamp);
    int pendingKeys = keyRepository.countPendingKeys();
    logger.info("Found {} pending upload keys on DB", pendingKeys);
    int numberOfKeysToGenerate = maxPendingKeys - pendingKeys;

    if (numberOfKeysToGenerate > 0) {
      LocalDateTime upperHour = getCurrentTimestampTruncatedHour()
          .minusDays(1L)
          .minusHours(2L);
      LocalDateTime lowerHour = upperHour
          .minusDays(1L);

      long hourStart = secondsToHours(lowerHour.toEpochSecond(ZoneOffset.UTC));
      long hourEnd = secondsToHours(upperHour.toEpochSecond(ZoneOffset.UTC));

      logger.info("Generating {} fake upload keys between times {} and {}",
          numberOfKeysToGenerate,
          hourStart,
          hourEnd);
      return IntStream.range(0, numberOfKeysToGenerate)
          .mapToObj(ignoredValue -> generateDiagnosisKey(getRandomBetween(hourStart, hourEnd), "DE"))
          .collect(Collectors.toList());
    } else {
      logger.info("Skipping generation");
      return Collections.emptyList();
    }
  }

  private LocalDateTime getCurrentTimestampTruncatedHour() {
    return LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC).truncatedTo(ChronoUnit.HOURS);
  }

  protected DiagnosisKey generateDiagnosisKey(long timestamp, String country) {
    return FederationUploadKey.from(DiagnosisKey.builder().withKeyData(generateDiagnosisKeyBytes())
        .withRollingStartIntervalNumber(generateRollingStartIntervalNumber(timestamp))
        .withTransmissionRiskLevel(generateTransmissionRiskLevel())
        .withConsentToFederation(true)
        .withCountryCode(country)
        .withDaysSinceOnsetOfSymptoms(1)
        .withSubmissionTimestamp(timestamp)
        .withVisitedCountries(Set.of("FR", "DK"))
        .withReportType(ReportType.CONFIRMED_TEST)
        .build());
  }
}


Review comment (refactor):
I wonder if we are going to run into problems in the future because this was changed to `DiagnosisKey` rather than `FederationUploadKey`. As of now it seems to be all good because `FederationUploadKey` is a superset of `DiagnosisKey`. And even in the context of this class we are not using any specific properties of the Federation model. The only thing I can imagine is that if in the future we have multiple EFGS backends, we would need to refactor this again because TestDataGeneration will have to insert on Federation specific columns.

If you don't see any complications in this code to refactor now, I'd suggest you do it. Otherwise we can just wait and see what happens and change this only if needed. 

Code after:
  private List<FederationUploadKey> generateFakeKeysForPreviousDay() {
    long timestamp = getCurrentTimestampTruncatedHour()
        .minusDays(retentionDays)
        .toEpochSecond(ZoneOffset.UTC) / 600L;
    logger.info("Deleting test keys with rolling_start_interval_number less than {}", timestamp);
    keyRepository.applyRetentionToTestKeys((int) timestamp);
    int pendingKeys = keyRepository.countPendingKeys();
    logger.info("Found {} pending upload keys on DB", pendingKeys);
    int numberOfKeysToGenerate = maxPendingKeys - pendingKeys;

    if (numberOfKeysToGenerate > 0) {
      LocalDateTime upperHour = getCurrentTimestampTruncatedHour()
          .minusDays(1L)
          .minusHours(2L);
      LocalDateTime lowerHour = upperHour
          .minusDays(1L);

      long hourStart = secondsToHours(lowerHour.toEpochSecond(ZoneOffset.UTC));
      long hourEnd = secondsToHours(upperHour.toEpochSecond(ZoneOffset.UTC));

      logger.info("Generating {} fake upload keys between times {} and {}",
          numberOfKeysToGenerate,
          hourStart,
          hourEnd);
      return IntStream.range(0, numberOfKeysToGenerate)
          .mapToObj(ignoredValue -> generateDiagnosisKey(getRandomBetween(hourStart, hourEnd), "DE"))
          .collect(Collectors.toList());
    } else {
      logger.info("Skipping generation");
      return Collections.emptyList();
    }
  }

  private LocalDateTime getCurrentTimestampTruncatedHour() {
    return LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC).truncatedTo(ChronoUnit.HOURS);
  }

  @Override
  protected FederationUploadKey generateDiagnosisKey(long submissionTimestamp, String country) {
    return FederationUploadKey.from(DiagnosisKey.builder().withKeyData(generateDiagnosisKeyBytes())
        .withRollingStartIntervalNumber(generateRollingStartIntervalNumber(submissionTimestamp))
        .withTransmissionRiskLevel(generateTransmissionRiskLevel())
        .withConsentToFederation(true)
        .withCountryCode(country)
        .withDaysSinceOnsetOfSymptoms(1)
        .withSubmissionTimestamp(submissionTimestamp)
        .withVisitedCountries(Set.of("FR", "DK"))
        .withReportType(ReportType.CONFIRMED_TEST)
        .build());
  }
}