Skip to content

Commit

Permalink
test(backup): verify backup is not re-taken when already exist
Browse files Browse the repository at this point in the history
Updated the test to remove mocking of InProgressBackup. The existing test
was updated to use list instead of getStatus to find duplicate backups.

(cherry picked from commit 8763717)
  • Loading branch information
deepthidevaki committed May 4, 2023
1 parent d0e725d commit 36899fb
Showing 1 changed file with 132 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import io.camunda.zeebe.backup.api.Backup;
import io.camunda.zeebe.backup.api.BackupIdentifier;
import io.camunda.zeebe.backup.api.BackupStatus;
import io.camunda.zeebe.backup.api.BackupStatusCode;
import io.camunda.zeebe.backup.api.BackupStore;
import io.camunda.zeebe.backup.common.BackupIdentifierImpl;
import io.camunda.zeebe.backup.common.BackupStatusImpl;
import io.camunda.zeebe.scheduler.ConcurrencyControl;
import io.camunda.zeebe.scheduler.future.ActorFuture;
import io.camunda.zeebe.scheduler.testing.TestActorFuture;
import io.camunda.zeebe.scheduler.testing.TestConcurrencyControl;
import java.time.Duration;
import java.util.List;
Expand All @@ -40,13 +42,12 @@
@ExtendWith(MockitoExtension.class)
class BackupServiceImplTest {

@Mock InProgressBackup inProgressBackup;
@Mock BackupStore backupStore;

@Mock BackupStatus notExistingBackupStatus;

private BackupServiceImpl backupService;
private final ConcurrencyControl concurrencyControl = new TestConcurrencyControl();
private final TestConcurrencyControl concurrencyControl = new TestConcurrencyControl();

@BeforeEach
void setup() {
Expand All @@ -58,12 +59,19 @@ void setup() {
lenient()
.when(backupStore.getStatus(any()))
.thenReturn(CompletableFuture.completedFuture(notExistingBackupStatus));
lenient()
.when(
backupStore.list(
new BackupIdentifierWildcardImpl(
Optional.empty(), Optional.of(2), Optional.of(3L))))
.thenReturn(CompletableFuture.completedFuture(List.of()));
}

@Test
void shouldTakeBackup() {
// given
mockInProgressBackup();
final ControllableInProgressBackup inProgressBackup = new ControllableInProgressBackup();
mockSaveBackup();

// when
final var result = backupService.takeBackup(inProgressBackup, concurrencyControl);
Expand All @@ -76,10 +84,10 @@ void shouldTakeBackup() {
@Test
void shouldCloseAllInProgressBackupsWhenClosing() {
// given
final InProgressBackup backup1 = mock(InProgressBackup.class);
final InProgressBackup backup2 = mock(InProgressBackup.class);
when(backup1.findSegmentFiles()).thenReturn(concurrencyControl.createFuture());
when(backup2.findSegmentFiles()).thenReturn(concurrencyControl.createFuture());
final ControllableInProgressBackup backup1 =
new ControllableInProgressBackup().waitOnFindSegmentFiles();
final ControllableInProgressBackup backup2 =
new ControllableInProgressBackup().waitOnFindSegmentFiles();

backupService.takeBackup(backup1, concurrencyControl);
backupService.takeBackup(backup2, concurrencyControl);
Expand All @@ -88,14 +96,15 @@ void shouldCloseAllInProgressBackupsWhenClosing() {
backupService.close();

// then
verify(backup1).close();
verify(backup2).close();
assertThat(backup1.isClosed()).isTrue();
assertThat(backup2.isClosed()).isTrue();
}

@Test
void shouldCloseInProgressBackupsAfterBackupIsTaken() {
// given
mockInProgressBackup();
final ControllableInProgressBackup inProgressBackup = new ControllableInProgressBackup();
mockSaveBackup();

// when
final var result = backupService.takeBackup(inProgressBackup, concurrencyControl);
Expand All @@ -107,8 +116,8 @@ void shouldCloseInProgressBackupsAfterBackupIsTaken() {
@Test
void shouldFailBackupWhenNoValidSnapshotFound() {
// given
mockFindSegmentFiles();
when(inProgressBackup.findValidSnapshot()).thenReturn(failedFuture());
final ControllableInProgressBackup inProgressBackup =
new ControllableInProgressBackup().failOnFindValidSnapshot();

// when
final var result = backupService.takeBackup(inProgressBackup, concurrencyControl);
Expand All @@ -118,60 +127,53 @@ void shouldFailBackupWhenNoValidSnapshotFound() {
.failsWithin(Duration.ofMillis(1000))
.withThrowableOfType(ExecutionException.class)
.withMessageContaining("Expected");
verifyInProgressBackupIsCleanedUpAfterFailure();
verifyInProgressBackupIsCleanedUpAfterFailure(inProgressBackup);
}

@Test
void shouldFailBackupWhenSnapshotCannotBeReserved() {
// given
mockFindSegmentFiles();
mockFindValidSnapshot();
when(inProgressBackup.reserveSnapshot()).thenReturn(failedFuture());
final var inProgressBackup = new ControllableInProgressBackup().failOnReserveSnapshot();

// when
final var result = backupService.takeBackup(inProgressBackup, concurrencyControl);

// then
assertThat(result).failsWithin(Duration.ofMillis(100));
verifyInProgressBackupIsCleanedUpAfterFailure();
verifyInProgressBackupIsCleanedUpAfterFailure(inProgressBackup);
}

@Test
void shouldFailBackupWhenSnapshotFilesCannotBeCollected() {
// given
mockFindSegmentFiles();
mockFindValidSnapshot();
mockReserveSnapshot();
when(inProgressBackup.findSnapshotFiles()).thenReturn(failedFuture());
final var inProgressBackup = new ControllableInProgressBackup().failOnFindSnapshotFiles();

// when
final var result = backupService.takeBackup(inProgressBackup, concurrencyControl);

// then
assertThat(result).failsWithin(Duration.ofMillis(100));
verifyInProgressBackupIsCleanedUpAfterFailure();
verifyInProgressBackupIsCleanedUpAfterFailure(inProgressBackup);
}

@Test
void shouldFailBackupWhenSegmentFilesCannotBeCollected() {
// given
when(inProgressBackup.findSegmentFiles()).thenReturn(failedFuture());
final ControllableInProgressBackup inProgressBackup =
new ControllableInProgressBackup().failOnFindSegmentFiles();

// when
final var result = backupService.takeBackup(inProgressBackup, concurrencyControl);

// then
assertThat(result).failsWithin(Duration.ofMillis(100));
verifyInProgressBackupIsCleanedUpAfterFailure();
verifyInProgressBackupIsCleanedUpAfterFailure(inProgressBackup);
}

@Test
void shouldFailBackupIfStoringFailed() {
// given
mockFindValidSnapshot();
mockReserveSnapshot();
mockFindSnapshotFiles();
mockFindSegmentFiles();
final ControllableInProgressBackup inProgressBackup = new ControllableInProgressBackup();
when(backupStore.save(any()))
.thenReturn(CompletableFuture.failedFuture(new RuntimeException("Expected")));

Expand All @@ -180,7 +182,7 @@ void shouldFailBackupIfStoringFailed() {

// then
assertThat(result).failsWithin(Duration.ofMillis(100));
verifyInProgressBackupIsCleanedUpAfterFailure();
verifyInProgressBackupIsCleanedUpAfterFailure(inProgressBackup);
}

@Test
Expand Down Expand Up @@ -310,9 +312,15 @@ void shouldMarkRemainingBackupsAsFailedWhenThrowsError() {
@Test
void shouldNotTakeNewBackupIfBackupAlreadyCompleted() {
// given
final ControllableInProgressBackup inProgressBackup = new ControllableInProgressBackup();
final BackupStatus status = mock(BackupStatus.class);
when(status.statusCode()).thenReturn(BackupStatusCode.COMPLETED);
when(backupStore.getStatus(any())).thenReturn(CompletableFuture.completedFuture(status));
when(backupStore.list(
new BackupIdentifierWildcardImpl(
Optional.empty(),
Optional.of(inProgressBackup.id.partitionId()),
Optional.of(inProgressBackup.checkpointId()))))
.thenReturn(CompletableFuture.completedFuture(List.of(status)));

// when
backupService.takeBackup(inProgressBackup, concurrencyControl).join();
Expand All @@ -327,9 +335,10 @@ void shouldNotTakeNewBackupIfBackupAlreadyCompleted() {
names = {"IN_PROGRESS", "FAILED"})
void shouldNotTakeNewBackupIfBackupAlreadyExists(final BackupStatusCode statusCode) {
// given
final ControllableInProgressBackup inProgressBackup = new ControllableInProgressBackup();
final BackupStatus status = mock(BackupStatus.class);
when(status.statusCode()).thenReturn(statusCode);
when(backupStore.getStatus(any())).thenReturn(CompletableFuture.completedFuture(status));
when(backupStore.list(any())).thenReturn(CompletableFuture.completedFuture(List.of(status)));

// when
assertThat(backupService.takeBackup(inProgressBackup, concurrencyControl))
Expand All @@ -342,44 +351,104 @@ void shouldNotTakeNewBackupIfBackupAlreadyExists(final BackupStatusCode statusCo
}

private ActorFuture<Void> failedFuture() {
final ActorFuture<Void> future = concurrencyControl.createFuture();
future.completeExceptionally(new RuntimeException("Expected"));
return future;
return concurrencyControl.failedFuture(new RuntimeException("Expected"));
}

private void mockInProgressBackup() {
mockFindValidSnapshot();
mockReserveSnapshot();
mockFindSnapshotFiles();
mockFindSegmentFiles();
mockSaveBackup();
}

private void mockFindSnapshotFiles() {
when(inProgressBackup.findSnapshotFiles())
.thenReturn(concurrencyControl.createCompletedFuture());
}

private void mockReserveSnapshot() {
when(inProgressBackup.reserveSnapshot()).thenReturn(concurrencyControl.createCompletedFuture());
}

private void mockFindValidSnapshot() {
when(inProgressBackup.findValidSnapshot())
.thenReturn(concurrencyControl.createCompletedFuture());
private void verifyInProgressBackupIsCleanedUpAfterFailure(
final ControllableInProgressBackup inProgressBackup) {
verify(backupStore).markFailed(any(), any());
assertThat(inProgressBackup.isClosed()).isTrue();
}

private void mockSaveBackup() {
when(backupStore.save(any())).thenReturn(CompletableFuture.completedFuture(null));
}

private void mockFindSegmentFiles() {
when(inProgressBackup.findSegmentFiles())
.thenReturn(concurrencyControl.createCompletedFuture());
}

private void verifyInProgressBackupIsCleanedUpAfterFailure() {
verify(backupStore).markFailed(any(), any());
verify(inProgressBackup).close();
class ControllableInProgressBackup implements InProgressBackup {

private final BackupIdentifier id;
private ActorFuture<Void> findValidSnapshotFuture = TestActorFuture.completedFuture(null);
private ActorFuture<Void> reserveSnapshotFuture = TestActorFuture.completedFuture(null);
private ActorFuture<Void> findSnapshotFilesFuture = TestActorFuture.completedFuture(null);
private ActorFuture<Void> findSegmentFilesFuture = TestActorFuture.completedFuture(null);
private boolean closed;

ControllableInProgressBackup() {
id = new BackupIdentifierImpl(1, 2, 3);
}

@Override
public long checkpointId() {
return id.checkpointId();
}

@Override
public long checkpointPosition() {
return 1;
}

@Override
public BackupIdentifier id() {
return id;
}

@Override
public ActorFuture<Void> findValidSnapshot() {
return findValidSnapshotFuture;
}

@Override
public ActorFuture<Void> reserveSnapshot() {
return reserveSnapshotFuture;
}

@Override
public ActorFuture<Void> findSnapshotFiles() {
return findSnapshotFilesFuture;
}

@Override
public ActorFuture<Void> findSegmentFiles() {
return findSegmentFilesFuture;
}

@Override
public Backup createBackup() {
return null;
}

@Override
public void close() {
closed = true;
}

boolean isClosed() {
return closed;
}

ControllableInProgressBackup waitOnFindSegmentFiles() {
findSegmentFilesFuture = concurrencyControl.createFuture();
return this;
}

ControllableInProgressBackup failOnFindSegmentFiles() {
findSegmentFilesFuture = failedFuture();
return this;
}

ControllableInProgressBackup failOnFindSnapshotFiles() {
findSnapshotFilesFuture = failedFuture();
return this;
}

ControllableInProgressBackup failOnReserveSnapshot() {
reserveSnapshotFuture = failedFuture();
return this;
}

ControllableInProgressBackup failOnFindValidSnapshot() {
findValidSnapshotFuture = failedFuture();
return this;
}
}
}

0 comments on commit 36899fb

Please sign in to comment.