Make backupDir/mediaDir indirect

This commit is contained in:
Ravi Khadiwala
2024-03-25 15:14:29 -05:00
committed by ravi-signal
parent de37141812
commit 831c9ff5bf
10 changed files with 551 additions and 217 deletions

View File

@@ -29,6 +29,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -53,6 +54,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.zkgroup.VerificationFailedException;
@@ -80,6 +82,7 @@ public class BackupManagerTest {
private final RemoteStorageManager remoteStorageManager = mock(RemoteStorageManager.class);
private final byte[] backupKey = TestRandomUtil.nextBytes(32);
private final UUID aci = UUID.randomUUID();
private final SecureRandom secureRandom = new SecureRandom();
private BackupManager backupManager;
private BackupsDb backupsDb;
@@ -109,14 +112,13 @@ public class BackupManagerTest {
testClock.pin(now);
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), backupTier);
final String encodedBackupId = Base64.getUrlEncoder().encodeToString(hashedBackupId(backupUser.backupId()));
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
verify(tusCredentialGenerator, times(1))
.generateUpload("%s/%s".formatted(encodedBackupId, BackupManager.MESSAGE_BACKUP_NAME));
.generateUpload("%s/%s".formatted(backupUser.backupDir(), BackupManager.MESSAGE_BACKUP_NAME));
final BackupManager.BackupInfo info = backupManager.backupInfo(backupUser).join();
assertThat(info.backupSubdir()).isEqualTo(encodedBackupId);
assertThat(info.backupSubdir()).isEqualTo(backupUser.backupDir()).isNotBlank();
assertThat(info.messageBackupKey()).isEqualTo(BackupManager.MESSAGE_BACKUP_NAME);
assertThat(info.mediaUsedSpace()).isEqualTo(Optional.empty());
@@ -344,9 +346,7 @@ public class BackupManagerTest {
@Test
public void quotaEnforcementRecalculation() {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupTier.MEDIA);
final String backupMediaPrefix = "%s/%s/".formatted(
BackupManager.encodeBackupIdForCdn(backupUser),
BackupManager.MEDIA_DIRECTORY_NAME);
final String backupMediaPrefix = "%s/%s/".formatted(backupUser.backupDir(), backupUser.mediaDir());
// on recalculation, say there's actually 10 bytes left
when(remoteStorageManager.calculateBytesUsed(eq(backupMediaPrefix)))
@@ -383,9 +383,7 @@ public class BackupManagerTest {
final long mediaToAddSize,
boolean shouldAccept) {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupTier.MEDIA);
final String backupMediaPrefix = "%s/%s/".formatted(
BackupManager.encodeBackupIdForCdn(backupUser),
BackupManager.MEDIA_DIRECTORY_NAME);
final String backupMediaPrefix = "%s/%s/".formatted(backupUser.backupDir(), backupUser.mediaDir());
// set the backupsDb to be out of quota at t=0
testClock.pin(Instant.ofEpochSecond(0));
@@ -410,9 +408,7 @@ public class BackupManagerTest {
public void list(final String cursorVal) {
final Optional<String> cursor = Optional.of(cursorVal).filter(StringUtils::isNotBlank);
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupTier.MEDIA);
final String backupMediaPrefix = "%s/%s/".formatted(
BackupManager.encodeBackupIdForCdn(backupUser),
BackupManager.MEDIA_DIRECTORY_NAME);
final String backupMediaPrefix = "%s/%s/".formatted(backupUser.backupDir(), backupUser.mediaDir());
when(remoteStorageManager.cdnNumber()).thenReturn(13);
when(remoteStorageManager.list(eq(backupMediaPrefix), eq(cursor), eq(17L)))
@@ -437,9 +433,9 @@ public class BackupManagerTest {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupTier.MEDIA);
final byte[] mediaId = TestRandomUtil.nextBytes(16);
final String backupMediaKey = "%s/%s/%s".formatted(
BackupManager.encodeBackupIdForCdn(backupUser),
BackupManager.MEDIA_DIRECTORY_NAME,
BackupManager.encodeForCdn(mediaId));
backupUser.backupDir(),
backupUser.mediaDir(),
BackupManager.encodeMediaIdForCdn(mediaId));
backupsDb.setMediaUsage(backupUser, new UsageInfo(100, 1000)).join();
@@ -474,9 +470,9 @@ public class BackupManagerTest {
TestRandomUtil.nextBytes(15));
descriptors.add(descriptor);
final String backupMediaKey = "%s/%s/%s".formatted(
BackupManager.encodeBackupIdForCdn(backupUser),
BackupManager.MEDIA_DIRECTORY_NAME,
BackupManager.encodeForCdn(descriptor.key()));
backupUser.backupDir(),
backupUser.mediaDir(),
BackupManager.encodeMediaIdForCdn(descriptor.key()));
initialBytes += i;
// fail 2 deletions, otherwise return the corresponding object's size as i
@@ -501,9 +497,9 @@ public class BackupManagerTest {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupTier.MEDIA);
final byte[] mediaId = TestRandomUtil.nextBytes(16);
final String backupMediaKey = "%s/%s/%s".formatted(
BackupManager.encodeBackupIdForCdn(backupUser),
BackupManager.MEDIA_DIRECTORY_NAME,
BackupManager.encodeForCdn(mediaId));
backupUser.backupDir(),
backupUser.mediaDir(),
BackupManager.encodeMediaIdForCdn(mediaId));
backupsDb.setMediaUsage(backupUser, new UsageInfo(100, 5)).join();
@@ -545,7 +541,7 @@ public class BackupManagerTest {
.map(ExpiredBackup::hashedBackupId)
.map(ByteBuffer::wrap)
.allMatch(expectedHashes::contains)).isTrue();
assertThat(expired.stream().allMatch(eb -> eb.backupTierToRemove() == BackupTier.MESSAGES)).isTrue();
assertThat(expired.stream().allMatch(eb -> eb.expirationType() == ExpiredBackup.ExpirationType.ALL)).isTrue();
// on next iteration, backup i should be expired
expectedHashes.add(ByteBuffer.wrap(hashedBackupId(backupUsers.get(i).backupId())));
@@ -572,50 +568,53 @@ public class BackupManagerTest {
assertThat(getExpired.apply(Instant.ofEpochSecond(6)))
.hasSize(1).first()
.matches(eb -> eb.backupTierToRemove() == BackupTier.MEDIA, "is media tier");
.matches(eb -> eb.expirationType() == ExpiredBackup.ExpirationType.MEDIA, "is media tier");
assertThat(getExpired.apply(Instant.ofEpochSecond(7)))
.hasSize(1).first()
.matches(eb -> eb.backupTierToRemove() == BackupTier.MESSAGES, "is messages tier");
.matches(eb -> eb.expirationType() == ExpiredBackup.ExpirationType.ALL, "is messages tier");
}
@ParameterizedTest
@EnumSource(mode = EnumSource.Mode.INCLUDE, names = {"MESSAGES", "MEDIA"})
public void deleteBackup(BackupTier backupTier) {
@EnumSource(mode = EnumSource.Mode.INCLUDE, names = {"MEDIA", "ALL"})
public void expireBackup(ExpiredBackup.ExpirationType expirationType) {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupTier.MEDIA);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
final String mediaPrefix = "%s/%s/"
.formatted(BackupManager.encodeBackupIdForCdn(backupUser), BackupManager.MEDIA_DIRECTORY_NAME);
when(remoteStorageManager.list(eq(mediaPrefix), eq(Optional.empty()), anyLong()))
final String expectedPrefixToDelete = switch (expirationType) {
case ALL -> backupUser.backupDir();
case MEDIA -> backupUser.backupDir() + "/" + backupUser.mediaDir();
case GARBAGE_COLLECTION -> throw new IllegalArgumentException();
} + "/";
when(remoteStorageManager.list(eq(expectedPrefixToDelete), eq(Optional.empty()), anyLong()))
.thenReturn(CompletableFuture.completedFuture(new RemoteStorageManager.ListResult(List.of(
new RemoteStorageManager.ListResult.Entry("abc", 1),
new RemoteStorageManager.ListResult.Entry("def", 1),
new RemoteStorageManager.ListResult.Entry("ghi", 1)), Optional.empty())));
when(remoteStorageManager.delete(anyString())).thenReturn(CompletableFuture.completedFuture(1L));
backupManager.deleteBackup(backupTier, hashedBackupId(backupUser.backupId())).join();
backupManager.expireBackup(expiredBackup(expirationType, backupUser)).join();
verify(remoteStorageManager, times(1)).list(anyString(), any(), anyLong());
verify(remoteStorageManager, times(1)).delete(mediaPrefix + "abc");
verify(remoteStorageManager, times(1)).delete(mediaPrefix + "def");
verify(remoteStorageManager, times(1)).delete(mediaPrefix + "ghi");
verify(remoteStorageManager, times(backupTier == BackupTier.MESSAGES ? 1 : 0))
.delete("%s/%s".formatted(BackupManager.encodeBackupIdForCdn(backupUser), BackupManager.MESSAGE_BACKUP_NAME));
verify(remoteStorageManager, times(1)).delete(expectedPrefixToDelete + "abc");
verify(remoteStorageManager, times(1)).delete(expectedPrefixToDelete + "def");
verify(remoteStorageManager, times(1)).delete(expectedPrefixToDelete + "ghi");
verifyNoMoreInteractions(remoteStorageManager);
final BackupsDb.TimestampedUsageInfo usage = backupsDb.getMediaUsage(backupUser).join();
assertThat(usage.usageInfo().bytesUsed()).isEqualTo(0L);
assertThat(usage.usageInfo().numObjects()).isEqualTo(0L);
if (backupTier == BackupTier.MEDIA) {
// should have deleted all the media, but left the backup descriptor in place
assertThatNoException().isThrownBy(() -> backupsDb.describeBackup(backupUser).join());
} else {
if (expirationType == ExpiredBackup.ExpirationType.ALL) {
// should have deleted the db row for the backup
assertThat(CompletableFutureTestUtil.assertFailsWithCause(
StatusRuntimeException.class,
backupsDb.describeBackup(backupUser))
.getStatus().getCode())
.isEqualTo(Status.NOT_FOUND.getCode());
} else {
// should have deleted all the media, but left the backup descriptor in place
assertThatNoException().isThrownBy(() -> backupsDb.describeBackup(backupUser).join());
}
}
@@ -623,8 +622,9 @@ public class BackupManagerTest {
public void deleteBackupPaginated() {
final AuthenticatedBackupUser backupUser = backupUser(TestRandomUtil.nextBytes(16), BackupTier.MEDIA);
backupManager.createMessageBackupUploadDescriptor(backupUser).join();
final String mediaPrefix = "%s/%s/".formatted(BackupManager.encodeBackupIdForCdn(backupUser),
BackupManager.MEDIA_DIRECTORY_NAME);
final ExpiredBackup expiredBackup = expiredBackup(ExpiredBackup.ExpirationType.MEDIA, backupUser);
final String mediaPrefix = expiredBackup.prefixToDelete() + "/";
// Return 1 item per page. Initially the provided cursor is empty and we'll return the cursor string "1".
// When we get the cursor "1", we'll return "2", when "2" we'll return empty indicating listing
@@ -647,7 +647,7 @@ public class BackupManagerTest {
}));
});
when(remoteStorageManager.delete(anyString())).thenReturn(CompletableFuture.completedFuture(1L));
backupManager.deleteBackup(BackupTier.MEDIA, hashedBackupId(backupUser.backupId())).join();
backupManager.expireBackup(expiredBackup).join();
verify(remoteStorageManager, times(3)).list(anyString(), any(), anyLong());
verify(remoteStorageManager, times(1)).delete(mediaPrefix + "abc");
verify(remoteStorageManager, times(1)).delete(mediaPrefix + "def");
@@ -655,6 +655,19 @@ public class BackupManagerTest {
verifyNoMoreInteractions(remoteStorageManager);
}
private static ExpiredBackup expiredBackup(final ExpiredBackup.ExpirationType expirationType,
final AuthenticatedBackupUser backupUser) {
return new ExpiredBackup(
hashedBackupId(backupUser.backupId()),
expirationType,
Instant.now(),
switch (expirationType) {
case ALL -> backupUser.backupDir();
case MEDIA -> backupUser.backupDir() + "/" + backupUser.mediaDir();
case GARBAGE_COLLECTION -> null;
});
}
private Map<String, AttributeValue> getBackupItem(final AuthenticatedBackupUser backupUser) {
return DYNAMO_DB_EXTENSION.getDynamoDbClient().getItem(GetItemRequest.builder()
.tableName(DynamoDbExtensionSchema.Tables.BACKUPS.tableName())
@@ -688,6 +701,14 @@ public class BackupManagerTest {
}
private AuthenticatedBackupUser backupUser(final byte[] backupId, final BackupTier backupTier) {
return new AuthenticatedBackupUser(backupId, backupTier);
// Won't actually validate the public key, but need to have a public key to perform BackupsDB operations
byte[] privateKey = new byte[32];
ByteBuffer.wrap(privateKey).put(backupId);
try {
backupsDb.setPublicKey(backupId, backupTier, Curve.decodePrivatePoint(privateKey).publicKey()).join();
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
return new AuthenticatedBackupUser(backupId, backupTier, BackupsDb.generateDirName(secureRandom), BackupsDb.generateDirName(secureRandom));
}
}

View File

@@ -6,21 +6,15 @@
package org.whispersystems.textsecuregcm.backup;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.signal.libsignal.protocol.ecc.Curve;
import org.whispersystems.textsecuregcm.auth.AuthenticatedBackupUser;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema;
@@ -28,6 +22,12 @@ import org.whispersystems.textsecuregcm.util.CompletableFutureTestUtil;
import org.whispersystems.textsecuregcm.util.TestClock;
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
import reactor.core.scheduler.Schedulers;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
public class BackupsDbTest {
@@ -75,7 +75,7 @@ public class BackupsDbTest {
if (mediaAlreadyExists) {
this.backupsDb.trackMedia(backupUser, 1, 10).join();
}
backupsDb.setMediaUsage(backupUser, new UsageInfo( 113, 17)).join();
backupsDb.setMediaUsage(backupUser, new UsageInfo(113, 17)).join();
final BackupsDb.TimestampedUsageInfo info = backupsDb.getMediaUsage(backupUser).join();
assertThat(info.lastRecalculationTime()).isEqualTo(Instant.ofEpochSecond(5));
assertThat(info.usageInfo().bytesUsed()).isEqualTo(113L);
@@ -87,6 +87,7 @@ public class BackupsDbTest {
final byte[] backupId = TestRandomUtil.nextBytes(16);
// Refresh media/messages at t=0
testClock.pin(Instant.ofEpochSecond(0L));
backupsDb.setPublicKey(backupId, BackupTier.MEDIA, Curve.generateKeyPair().getPublicKey()).join();
this.backupsDb.ttlRefresh(backupUser(backupId, BackupTier.MEDIA)).join();
// refresh only messages at t=2
@@ -100,10 +101,11 @@ public class BackupsDbTest {
List<ExpiredBackup> expired = expiredBackups.apply(Instant.ofEpochSecond(1));
assertThat(expired).hasSize(1).first()
.matches(eb -> eb.backupTierToRemove() == BackupTier.MEDIA);
.matches(eb -> eb.expirationType() == ExpiredBackup.ExpirationType.MEDIA);
// Expire the media
backupsDb.clearMediaUsage(expired.get(0).hashedBackupId()).join();
backupsDb.startExpiration(expired.get(0)).join();
backupsDb.finishExpiration(expired.get(0)).join();
// should be nothing to expire at t=1
assertThat(expiredBackups.apply(Instant.ofEpochSecond(1))).isEmpty();
@@ -111,16 +113,100 @@ public class BackupsDbTest {
// at t=3, should now expire messages as well
expired = expiredBackups.apply(Instant.ofEpochSecond(3));
assertThat(expired).hasSize(1).first()
.matches(eb -> eb.backupTierToRemove() == BackupTier.MESSAGES);
.matches(eb -> eb.expirationType() == ExpiredBackup.ExpirationType.ALL);
// Expire the messages
backupsDb.deleteBackup(expired.get(0).hashedBackupId()).join();
backupsDb.startExpiration(expired.get(0)).join();
backupsDb.finishExpiration(expired.get(0)).join();
// should be nothing to expire at t=3
assertThat(expiredBackups.apply(Instant.ofEpochSecond(3))).isEmpty();
}
@ParameterizedTest
@EnumSource(names = {"MEDIA", "ALL"})
public void expirationFailed(ExpiredBackup.ExpirationType expirationType) {
final byte[] backupId = TestRandomUtil.nextBytes(16);
// Refresh media/messages at t=0
testClock.pin(Instant.ofEpochSecond(0L));
backupsDb.setPublicKey(backupId, BackupTier.MEDIA, Curve.generateKeyPair().getPublicKey()).join();
this.backupsDb.ttlRefresh(backupUser(backupId, BackupTier.MEDIA)).join();
if (expirationType == ExpiredBackup.ExpirationType.MEDIA) {
// refresh only messages at t=2 so that we only expire media at t=1
testClock.pin(Instant.ofEpochSecond(2L));
this.backupsDb.ttlRefresh(backupUser(backupId, BackupTier.MESSAGES)).join();
}
final Function<Instant, Optional<ExpiredBackup>> expiredBackups = purgeTime -> {
final List<ExpiredBackup> res = backupsDb
.getExpiredBackups(1, Schedulers.immediate(), purgeTime)
.collectList()
.block();
assertThat(res).hasSizeLessThanOrEqualTo(1);
return res.stream().findFirst();
};
BackupsDb.AuthenticationData info = backupsDb.retrieveAuthenticationData(backupId).join().get();
final String originalBackupDir = info.backupDir();
final String originalMediaDir = info.mediaDir();
ExpiredBackup expired = expiredBackups.apply(Instant.ofEpochSecond(1)).get();
assertThat(expired).matches(eb -> eb.expirationType() == expirationType);
// expire but fail (don't call finishExpiration)
backupsDb.startExpiration(expired).join();
info = backupsDb.retrieveAuthenticationData(backupId).join().get();
if (expirationType == ExpiredBackup.ExpirationType.MEDIA) {
// Media expiration should swap the media name and keep the backup name, marking the old media name for expiration
assertThat(expired.prefixToDelete())
.isEqualTo(originalBackupDir + "/" + originalMediaDir)
.withFailMessage("Should expire media directory, expired %s", expired.prefixToDelete());
assertThat(info.backupDir()).isEqualTo(originalBackupDir).withFailMessage("should keep backupDir");
assertThat(info.mediaDir()).isNotEqualTo(originalMediaDir).withFailMessage("should change mediaDir");
} else {
// Full expiration should swap the media name and the backup name, marking the old backup name for expiration
assertThat(expired.prefixToDelete())
.isEqualTo(originalBackupDir)
.withFailMessage("Should expire whole backupDir, expired %s", expired.prefixToDelete());
assertThat(info.backupDir()).isNotEqualTo(originalBackupDir).withFailMessage("should change backupDir");
assertThat(info.mediaDir()).isNotEqualTo(originalMediaDir).withFailMessage("should change mediaDir");
}
final String expiredPrefix = expired.prefixToDelete();
// We failed, so we should see the same prefix on the next expiration listing
expired = expiredBackups.apply(Instant.ofEpochSecond(1)).get();
assertThat(expired).matches(eb -> eb.expirationType() == ExpiredBackup.ExpirationType.GARBAGE_COLLECTION,
"Expiration should be garbage collection ");
assertThat(expired.prefixToDelete()).isEqualTo(expiredPrefix);
backupsDb.startExpiration(expired).join();
// Successfully finish the expiration
backupsDb.finishExpiration(expired).join();
Optional<ExpiredBackup> opt = expiredBackups.apply(Instant.ofEpochSecond(1));
if (expirationType == ExpiredBackup.ExpirationType.MEDIA) {
// should be nothing to expire at t=1
assertThat(opt).isEmpty();
// The backup should still exist
backupsDb.describeBackup(backupUser(backupId, BackupTier.MEDIA)).join();
} else {
// Cleaned up the failed attempt, now should tell us to clean the whole backup
assertThat(opt.get()).matches(eb -> eb.expirationType() == ExpiredBackup.ExpirationType.ALL,
"Expiration should be all ");
backupsDb.startExpiration(opt.get()).join();
backupsDb.finishExpiration(opt.get()).join();
// The backup entry should be gone
assertThat(CompletableFutureTestUtil.assertFailsWithCause(StatusRuntimeException.class,
backupsDb.describeBackup(backupUser(backupId, BackupTier.MEDIA)))
.getStatus().getCode())
.isEqualTo(Status.Code.NOT_FOUND);
assertThat(expiredBackups.apply(Instant.ofEpochSecond(10))).isEmpty();
}
}
private AuthenticatedBackupUser backupUser(final byte[] backupId, final BackupTier backupTier) {
return new AuthenticatedBackupUser(backupId, backupTier);
return new AuthenticatedBackupUser(backupId, backupTier, "myBackupDir", "myMediaDir");
}
}

View File

@@ -36,6 +36,7 @@ import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.checkerframework.checker.units.qual.A;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.jupiter.api.BeforeEach;
@@ -289,17 +290,16 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupTier.MEDIA, backupKey, aci);
when(backupManager.authenticateBackupUser(any(), any()))
.thenReturn(CompletableFuture.completedFuture(
new AuthenticatedBackupUser(presentation.getBackupId(), BackupTier.MEDIA)));
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupTier.MEDIA)));
when(backupManager.backupInfo(any())).thenReturn(CompletableFuture.completedFuture(new BackupManager.BackupInfo(
1, "subdir", "filename", Optional.empty())));
1, "myBackupDir", "myMediaDir", "filename", Optional.empty())));
final ArchiveController.BackupInfoResponse response = resources.getJerseyTest()
.target("v1/archives")
.request()
.header("X-Signal-ZK-Auth", Base64.getEncoder().encodeToString(presentation.serialize()))
.header("X-Signal-ZK-Auth-Signature", "aaa")
.get(ArchiveController.BackupInfoResponse.class);
assertThat(response.backupDir()).isEqualTo("subdir");
assertThat(response.backupDir()).isEqualTo("myBackupDir");
assertThat(response.backupName()).isEqualTo("filename");
assertThat(response.cdn()).isEqualTo(1);
assertThat(response.usedSpace()).isNull();
@@ -310,8 +310,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupTier.MEDIA, backupKey, aci);
when(backupManager.authenticateBackupUser(any(), any()))
.thenReturn(CompletableFuture.completedFuture(
new AuthenticatedBackupUser(presentation.getBackupId(), BackupTier.MEDIA)));
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupTier.MEDIA)));
when(backupManager.canStoreMedia(any(), anyLong())).thenReturn(CompletableFuture.completedFuture(true));
when(backupManager.copyToBackup(any(), anyInt(), any(), anyInt(), any(), any()))
.thenAnswer(invocation -> {
@@ -361,8 +360,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupTier.MEDIA, backupKey, aci);
when(backupManager.authenticateBackupUser(any(), any()))
.thenReturn(CompletableFuture.completedFuture(
new AuthenticatedBackupUser(presentation.getBackupId(), BackupTier.MEDIA)));
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupTier.MEDIA)));
final byte[][] mediaIds = IntStream.range(0, 3).mapToObj(i -> TestRandomUtil.nextBytes(15)).toArray(byte[][]::new);
when(backupManager.canStoreMedia(any(), anyLong())).thenReturn(CompletableFuture.completedFuture(true));
@@ -417,8 +415,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupTier.MEDIA, backupKey, aci);
when(backupManager.authenticateBackupUser(any(), any()))
.thenReturn(CompletableFuture.completedFuture(
new AuthenticatedBackupUser(presentation.getBackupId(), BackupTier.MEDIA)));
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupTier.MEDIA)));
when(backupManager.canStoreMedia(any(), eq(1L + 2L + 3L)))
.thenReturn(CompletableFuture.completedFuture(false));
@@ -448,8 +445,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(
BackupTier.MEDIA, backupKey, aci);
when(backupManager.authenticateBackupUser(any(), any()))
.thenReturn(CompletableFuture.completedFuture(
new AuthenticatedBackupUser(presentation.getBackupId(), BackupTier.MEDIA)));
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupTier.MEDIA)));
final byte[] mediaId = TestRandomUtil.nextBytes(15);
final Optional<String> expectedCursor = cursorProvided ? Optional.of("myCursor") : Optional.empty();
@@ -484,8 +480,7 @@ public class ArchiveControllerTest {
final BackupAuthCredentialPresentation presentation = backupAuthTestUtil.getPresentation(BackupTier.MEDIA,
backupKey, aci);
when(backupManager.authenticateBackupUser(any(), any()))
.thenReturn(CompletableFuture.completedFuture(
new AuthenticatedBackupUser(presentation.getBackupId(), BackupTier.MEDIA)));
.thenReturn(CompletableFuture.completedFuture(backupUser(presentation.getBackupId(), BackupTier.MEDIA)));
final ArchiveController.DeleteMedia deleteRequest = new ArchiveController.DeleteMedia(
IntStream
@@ -503,4 +498,8 @@ public class ArchiveControllerTest {
.post(Entity.json(deleteRequest));
assertThat(response.getStatus()).isEqualTo(204);
}
private static AuthenticatedBackupUser backupUser(byte[] backupId, BackupTier backupTier) {
return new AuthenticatedBackupUser(backupId, backupTier, "myBackupDir", "myMediaDir");
}
}