Skip to content

Commit

Permalink
Fix flaky db test + added tests #3705
Browse files Browse the repository at this point in the history
- randomness test fixed
- added tests for not found variants for
  nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan
- improved tests
  • Loading branch information
de-jcup committed Dec 5, 2024
1 parent a78848d commit 5795a83
Showing 1 changed file with 92 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ void nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan_one_ended_one_canceled
assertEquals(2, list.size());
}

/*
* This test checks that nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan
* does really fetch jobs in a random way. To check this, different jobs are
* created and fetched multiple times.
*
* When at least one job has been fetched two times and another one one time
* this is enough here to check for randomness.
*/
@Test
void nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan_randomization_works() {
/* prepare */
Expand All @@ -235,39 +243,86 @@ void nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan_randomization_works()
assertEquals(0, newJob5.getEncryptionCipherPoolId());
assertEquals(0, newJob6.getEncryptionCipherPoolId());

Map<UUID, AtomicInteger> map = new LinkedHashMap<>();
Map<UUID, AtomicInteger> randomCheckInfoMap = new LinkedHashMap<>();

/* execute */
for (int i = 0; i < 30; i++) {
int amountOfTries = 0;
while (amountOfTries < 1000) {
amountOfTries++;

List<ScheduleSecHubJob> list = jobRepository.nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan(1L, 1);
for (ScheduleSecHubJob job : list) {
AtomicInteger atomic = map.computeIfAbsent(job.getUUID(), (uuid) -> new AtomicInteger(0));
AtomicInteger atomic = randomCheckInfoMap.computeIfAbsent(job.getUUID(), (uuid) -> new AtomicInteger(0));
atomic.incrementAndGet();
}
if (isRandomEnough(randomCheckInfoMap)) {
break;
}
}

/* test */
assertEquals(6, map.size()); // when calling n times, we expect every entry is contained
System.out.println("map:" + map);

// dump info
System.out.println("amount of tries:" + amountOfTries + " random checks were done this way:" + randomCheckInfoMap);

// when calling n times, we expect every entry is contained
assertTrue(isRandomEnough(randomCheckInfoMap), "Even after " + amountOfTries + " tries, random check returned false!");

}

@Test
void nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan_2_entries_always_returned() {
@ParameterizedTest
@ValueSource(longs = { 0L, 1L, 100L })
void nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan_lower_encryption_entries_are_returned(long encryptionPoolId) {
/* prepare */
jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.STARTED).create();
jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.READY_TO_START).create();
jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.CANCEL_REQUESTED).create();
ScheduleSecHubJob newJob4 = jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.ENDED).create();
jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.STARTED).encryptionPoolId(encryptionPoolId).create();
jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.READY_TO_START).encryptionPoolId(encryptionPoolId).create();
jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.CANCEL_REQUESTED).encryptionPoolId(encryptionPoolId).create();
ScheduleSecHubJob newJob4 = jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.ENDED).encryptionPoolId(encryptionPoolId).create();

long newerEncryptionIdOnQuery = encryptionPoolId + 1;

/* execute */
List<ScheduleSecHubJob> list = jobRepository.nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan(1L, 10);
List<ScheduleSecHubJob> list = jobRepository.nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan(newerEncryptionIdOnQuery, 10);

/* test */
assertFalse(list.isEmpty());
assertTrue(list.contains(newJob4));
}

@ParameterizedTest
@ValueSource(longs = { 0L, 1L, 100L })
void nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan_no_entries_returned_when_same_encryption_id_already_set(long encryptionPoolId) {
/* prepare */
jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.STARTED).encryptionPoolId(encryptionPoolId).create();
jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.READY_TO_START).encryptionPoolId(encryptionPoolId).create();
jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.CANCEL_REQUESTED).encryptionPoolId(encryptionPoolId).create();
jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.ENDED).encryptionPoolId(encryptionPoolId).create();

/* execute */
List<ScheduleSecHubJob> list = jobRepository.nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan(encryptionPoolId, 10);

/* test */
assertTrue(list.isEmpty());
}

@ParameterizedTest
@ValueSource(longs = { 0L, 1L, 100L })
void nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan_no_entries_returned_when_newer_encryption_id_already_set(long encryptionPoolId) {
/* prepare */
long newerEncryptionPoolId = encryptionPoolId + 1;

jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.STARTED).encryptionPoolId(newerEncryptionPoolId).create();
jobCreator.project("p1").module(ModuleGroup.STATIC).being(ExecutionState.READY_TO_START).encryptionPoolId(newerEncryptionPoolId).create();
jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.CANCEL_REQUESTED).encryptionPoolId(newerEncryptionPoolId).create();
jobCreator.project("p2").module(ModuleGroup.STATIC).being(ExecutionState.ENDED).encryptionPoolId(newerEncryptionPoolId).create();

/* execute */
List<ScheduleSecHubJob> list = jobRepository.nextCanceledOrEndedJobsWithEncryptionPoolIdLowerThan(encryptionPoolId, 10);

/* test */
assertTrue(list.isEmpty());
}

@Test
void markJobsAsSuspended() {
/* prepare */
Expand Down Expand Up @@ -1446,6 +1501,31 @@ private void createAndCheckAvailable() {
}
}

private boolean isRandomEnough(Map<UUID, AtomicInteger> map) {
// If at least one is fetched twice and at least one is fetched one time ->
// enough for
// randomness check (means not serial).
boolean foundAtLeastOneWithOneFetch = false;
boolean foundAtLeastOneWithTwoFetches = false;

boolean randomEnough = false;
for (AtomicInteger ai : map.values()) {
if (ai.intValue() == 1) {
foundAtLeastOneWithOneFetch = true;
}
if (ai.intValue() == 2) {
foundAtLeastOneWithTwoFetches = true;
}
randomEnough = foundAtLeastOneWithOneFetch && foundAtLeastOneWithTwoFetches;

if (randomEnough) {
break;
}
}

return randomEnough;
}

@TestConfiguration
@EnableAutoConfiguration
public static class SimpleTestConfiguration {
Expand Down

0 comments on commit 5795a83

Please sign in to comment.