Make adding/removing devices blocking and remove async update plumbing

This commit is contained in:
Jon Chambers
2026-02-25 00:22:08 -05:00
committed by Jon Chambers
parent 31de1fc1a3
commit 405b04f07b
15 changed files with 240 additions and 266 deletions

View File

@@ -220,7 +220,7 @@ class DeviceControllerTest {
final Optional<ApnRegistrationId> apnRegistrationId,
final Optional<GcmRegistrationId> gcmRegistrationId,
final Optional<String> expectedApnsToken,
final Optional<String> expectedGcmToken) {
final Optional<String> expectedGcmToken) throws LinkDeviceTokenAlreadyUsedException {
final Device existingDevice = mock(Device.class);
when(existingDevice.getId()).thenReturn(Device.PRIMARY_ID);
@@ -249,7 +249,7 @@ class DeviceControllerTest {
final Account a = invocation.getArgument(0);
final DeviceSpec deviceSpec = invocation.getArgument(1);
return CompletableFuture.completedFuture(new Pair<>(a, deviceSpec.toDevice(NEXT_DEVICE_ID, testClock, aciIdentityKey)));
return new Pair<>(a, deviceSpec.toDevice(NEXT_DEVICE_ID, testClock, aciIdentityKey));
});
when(asyncCommands.set(any(), any(), any())).thenReturn(MockRedisFuture.completedFuture(null));
@@ -298,11 +298,12 @@ class DeviceControllerTest {
@CartesianTest
void deviceDowngrade(@CartesianTest.Enum final DeviceCapability capability,
@CartesianTest.Values(booleans = {true, false}) final boolean accountHasCapability,
@CartesianTest.Values(booleans = {true, false}) final boolean requestHasCapability) {
@CartesianTest.Values(booleans = {true, false}) final boolean requestHasCapability)
throws LinkDeviceTokenAlreadyUsedException {
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account));
when(accountsManager.addDevice(any(), any(), any()))
.thenReturn(CompletableFuture.completedFuture(new Pair<>(mock(Account.class), mock(Device.class))));
.thenReturn(new Pair<>(mock(Account.class), mock(Device.class)));
final Device primaryDevice = mock(Device.class);
when(primaryDevice.getId()).thenReturn(Device.PRIMARY_ID);
@@ -392,7 +393,7 @@ class DeviceControllerTest {
}
@Test
void linkDeviceAtomicReusedToken() {
void linkDeviceAtomicReusedToken() throws LinkDeviceTokenAlreadyUsedException {
final Device existingDevice = mock(Device.class);
when(existingDevice.getId()).thenReturn(Device.PRIMARY_ID);
when(account.getDevices()).thenReturn(List.of(existingDevice));
@@ -416,7 +417,7 @@ class DeviceControllerTest {
when(accountsManager.checkDeviceLinkingToken(anyString())).thenReturn(Optional.of(AuthHelper.VALID_UUID));
when(accountsManager.addDevice(any(), any(), any()))
.thenReturn(CompletableFuture.failedFuture(new LinkDeviceTokenAlreadyUsedException()));
.thenThrow(new LinkDeviceTokenAlreadyUsedException());
when(asyncCommands.set(any(), any(), any())).thenReturn(MockRedisFuture.completedFuture(null));
@@ -729,7 +730,8 @@ class DeviceControllerTest {
@ParameterizedTest
@MethodSource
void linkDeviceRegistrationId(final int registrationId, final int pniRegistrationId, final int expectedStatusCode) {
void linkDeviceRegistrationId(final int registrationId, final int pniRegistrationId, final int expectedStatusCode)
throws LinkDeviceTokenAlreadyUsedException {
final Device existingDevice = mock(Device.class);
when(existingDevice.getId()).thenReturn(Device.PRIMARY_ID);
when(account.getDevices()).thenReturn(List.of(existingDevice));
@@ -750,7 +752,7 @@ class DeviceControllerTest {
final Account a = invocation.getArgument(0);
final DeviceSpec deviceSpec = invocation.getArgument(1);
return CompletableFuture.completedFuture(new Pair<>(a, deviceSpec.toDevice(NEXT_DEVICE_ID, testClock, aciIdentityKey)));
return new Pair<>(a, deviceSpec.toDevice(NEXT_DEVICE_ID, testClock, aciIdentityKey));
});
when(accountsManager.checkDeviceLinkingToken(anyString())).thenReturn(Optional.of(AuthHelper.VALID_UUID));
@@ -835,7 +837,7 @@ class DeviceControllerTest {
}
@Test
void maxDevicesTest() {
void maxDevicesTest() throws LinkDeviceTokenAlreadyUsedException {
final List<Device> devices = IntStream.range(0, DeviceController.MAX_DEVICES + 1)
.mapToObj(i -> mock(Device.class))
.toList();
@@ -891,7 +893,7 @@ class DeviceControllerTest {
final byte deviceId = 2;
when(accountsManager.removeDevice(account, deviceId))
.thenReturn(CompletableFuture.completedFuture(account));
.thenReturn(account);
try (final Response response = resources
.getJerseyTest()
@@ -932,7 +934,7 @@ class DeviceControllerTest {
final byte deviceId = 2;
when(accountsManager.removeDevice(AuthHelper.VALID_ACCOUNT_3, deviceId))
.thenReturn(CompletableFuture.completedFuture(account));
.thenReturn(account);
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID_3))
.thenReturn(Optional.of(AuthHelper.VALID_ACCOUNT_3));

View File

@@ -25,7 +25,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@@ -72,7 +71,7 @@ class DevicesGrpcServiceTest extends SimpleBaseGrpcTest<DevicesGrpcService, Devi
.thenReturn(Optional.of(authenticatedAccount));
when(accountsManager.removeDevice(any(), anyByte()))
.thenReturn(CompletableFuture.completedFuture(authenticatedAccount));
.thenReturn(authenticatedAccount);
when(accountsManager.update(any(), any()))
.thenAnswer(invocation -> {

View File

@@ -160,7 +160,6 @@ class AccountsManagerTest {
when(asyncClusterCommands.set(any(), any(), any())).thenReturn(MockRedisFuture.completedFuture("OK"));
when(asyncClusterCommands.setex(any(), anyLong(), any())).thenReturn(MockRedisFuture.completedFuture("OK"));
when(accounts.updateTransactionallyAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(null));
when(accounts.delete(any(), any())).thenReturn(CompletableFuture.completedFuture(null));
doAnswer((Answer<Void>) invocation -> {
@@ -692,15 +691,13 @@ class AccountsManagerTest {
Account account = AccountsHelper.generateTestAccount("+14152222222", List.of(primaryDevice, linkedDevice));
when(accounts.getByAccountIdentifierAsync(account.getUuid()))
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
when(accounts.getByAccountIdentifier(account.getUuid())).thenReturn(Optional.of(account));
when(keysManager.deleteSingleUsePreKeys(any(), anyByte())).thenReturn(CompletableFuture.completedFuture(null));
when(messagesManager.clear(any(), anyByte())).thenReturn(CompletableFuture.completedFuture(null));
assertTrue(account.getDevice(linkedDevice.getId()).isPresent());
account = accountsManager.removeDevice(account, linkedDevice.getId()).join();
account = accountsManager.removeDevice(account, linkedDevice.getId());
assertFalse(account.getDevice(linkedDevice.getId()).isPresent());
verify(messagesManager, times(2)).clear(account.getUuid(), linkedDevice.getId());
@@ -855,7 +852,7 @@ class AccountsManagerTest {
}
@Test
void testAddDevice() {
void testAddDevice() throws LinkDeviceTokenAlreadyUsedException {
final String phoneNumber =
PhoneNumberUtil.getInstance().format(PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -883,8 +880,7 @@ class AccountsManagerTest {
when(keysManager.deleteSingleUsePreKeys(any(), anyByte())).thenReturn(CompletableFuture.completedFuture(null));
when(messagesManager.clear(any(), anyByte())).thenReturn(CompletableFuture.completedFuture(null));
when(accounts.getByAccountIdentifierAsync(aci)).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
when(accounts.updateTransactionallyAsync(any(), any())).thenReturn(CompletableFuture.completedFuture(null));
when(accounts.getByAccountIdentifier(aci)).thenReturn(Optional.of(account));
CLOCK.pin(CLOCK.instant().plusSeconds(60));
@@ -902,8 +898,7 @@ class AccountsManagerTest {
pniSignedPreKey,
aciPqLastResortPreKey,
pniPqLastResortPreKey),
accountsManager.generateLinkDeviceToken(aci))
.join();
accountsManager.generateLinkDeviceToken(aci));
verify(keysManager).deleteSingleUsePreKeys(aci, nextDeviceId);
verify(keysManager).deleteSingleUsePreKeys(pni, nextDeviceId);

View File

@@ -40,7 +40,6 @@ import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
@@ -648,7 +647,7 @@ class AccountsTest {
account.getPrimaryDevice().setName(deviceName);
accounts.updateTransactionallyAsync(account, List.of(TransactWriteItem.builder()
accounts.updateTransactionally(account, List.of(TransactWriteItem.builder()
.put(Put.builder()
.tableName(Tables.CLIENT_RELEASES.tableName())
.item(Map.of(
@@ -656,7 +655,7 @@ class AccountsTest {
ClientReleases.ATTR_VERSION, AttributeValues.fromString("test")
))
.build())
.build())).toCompletableFuture().join();
.build()));
assertArrayEquals(deviceName,
accounts.getByAccountIdentifier(account.getUuid()).orElseThrow().getPrimaryDevice().getName());
@@ -678,8 +677,8 @@ class AccountsTest {
account.setVersion(account.getVersion() - 1);
final CompletionException completionException = assertThrows(CompletionException.class,
() -> accounts.updateTransactionallyAsync(account, List.of(TransactWriteItem.builder()
assertThrows(ContestedOptimisticLockException.class,
() -> accounts.updateTransactionally(account, List.of(TransactWriteItem.builder()
.put(Put.builder()
.tableName(Tables.CLIENT_RELEASES.tableName())
.item(Map.of(
@@ -687,19 +686,17 @@ class AccountsTest {
ClientReleases.ATTR_VERSION, AttributeValues.fromString("test")
))
.build())
.build())).toCompletableFuture().join());
assertInstanceOf(ContestedOptimisticLockException.class, completionException.getCause());
.build())));
}
@Test
void testUpdateTransactionallyWithMockTransactionConflictException() {
final DynamoDbAsyncClient dynamoDbAsyncClient = mock(DynamoDbAsyncClient.class);
final DynamoDbClient dynamoDbClient = mock(DynamoDbClient.class);
accounts = new Accounts(
clock,
mock(DynamoDbClient.class),
dynamoDbAsyncClient,
dynamoDbClient,
mock(DynamoDbAsyncClient.class),
Tables.ACCOUNTS.tableName(),
Tables.NUMBERS.tableName(),
Tables.PNI_ASSIGNMENTS.tableName(),
@@ -707,18 +704,17 @@ class AccountsTest {
Tables.DELETED_ACCOUNTS.tableName(),
Tables.USED_LINK_DEVICE_TOKENS.tableName());
when(dynamoDbAsyncClient.transactWriteItems(any(TransactWriteItemsRequest.class)))
.thenReturn(CompletableFuture.failedFuture(TransactionCanceledException.builder()
when(dynamoDbClient.transactWriteItems(any(TransactWriteItemsRequest.class)))
.thenThrow(TransactionCanceledException.builder()
.cancellationReasons(CancellationReason.builder()
.code("TransactionConflict")
.build())
.build()));
.build());
Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID());
final Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID());
assertThatThrownBy(() -> accounts.updateTransactionallyAsync(account, Collections.emptyList()).toCompletableFuture().join())
.isInstanceOfAny(CompletionException.class)
.hasCauseInstanceOf(ContestedOptimisticLockException.class);
assertThatThrownBy(() -> accounts.updateTransactionally(account, Collections.emptyList()))
.isInstanceOfAny(ContestedOptimisticLockException.class);
}
@Test

View File

@@ -2,7 +2,6 @@ package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -21,7 +20,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -174,7 +172,7 @@ public class AddRemoveDeviceIntegrationTest {
}
@Test
void addDevice() throws InterruptedException {
void addDevice() throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -200,8 +198,7 @@ public class AddRemoveDeviceIntegrationTest {
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
accountsManager.generateLinkDeviceToken(account.getIdentifier(IdentityType.ACI)))
.join();
accountsManager.generateLinkDeviceToken(account.getIdentifier(IdentityType.ACI)));
assertEquals(2, updatedAccountAndDevice.first().getDevices().size());
@@ -223,7 +220,7 @@ public class AddRemoveDeviceIntegrationTest {
}
@Test
void addDeviceReusedToken() throws InterruptedException {
void addDeviceReusedToken() throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -251,14 +248,13 @@ public class AddRemoveDeviceIntegrationTest {
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken)
.join();
linkDeviceToken);
assertEquals(2,
accountsManager.getByAccountIdentifier(updatedAccountAndDevice.first().getUuid()).orElseThrow().getDevices()
.size());
final CompletionException completionException = assertThrows(CompletionException.class,
assertThrows(LinkDeviceTokenAlreadyUsedException.class,
() -> accountsManager.addDevice(account, new DeviceSpec(
"device-name".getBytes(StandardCharsets.UTF_8),
"password",
@@ -273,10 +269,7 @@ public class AddRemoveDeviceIntegrationTest {
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken)
.join());
assertInstanceOf(LinkDeviceTokenAlreadyUsedException.class, completionException.getCause());
linkDeviceToken));
assertEquals(2,
accountsManager.getByAccountIdentifier(updatedAccountAndDevice.first().getUuid()).orElseThrow().getDevices()
@@ -284,7 +277,7 @@ public class AddRemoveDeviceIntegrationTest {
}
@Test
void removeDevice() throws InterruptedException {
void removeDevice() throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -310,12 +303,11 @@ public class AddRemoveDeviceIntegrationTest {
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
accountsManager.generateLinkDeviceToken(account.getIdentifier(IdentityType.ACI)))
.join();
accountsManager.generateLinkDeviceToken(account.getIdentifier(IdentityType.ACI)));
final byte addedDeviceId = updatedAccountAndDevice.second().getId();
final Account updatedAccount = accountsManager.removeDevice(updatedAccountAndDevice.first(), addedDeviceId).join();
final Account updatedAccount = accountsManager.removeDevice(updatedAccountAndDevice.first(), addedDeviceId);
assertEquals(1, updatedAccount.getDevices().size());
@@ -334,7 +326,7 @@ public class AddRemoveDeviceIntegrationTest {
}
@Test
void removeDevicePartialFailure() throws InterruptedException {
void removeDevicePartialFailure() throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -362,16 +354,15 @@ public class AddRemoveDeviceIntegrationTest {
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
accountsManager.generateLinkDeviceToken(account.getIdentifier(IdentityType.ACI)))
.join();
accountsManager.generateLinkDeviceToken(account.getIdentifier(IdentityType.ACI)));
final byte addedDeviceId = updatedAccountAndDevice.second().getId();
when(messagesManager.clear(any(), anyByte()))
.thenReturn(CompletableFuture.failedFuture(new RuntimeException("OH NO")));
assertThrows(CompletionException.class,
() -> accountsManager.removeDevice(updatedAccountAndDevice.first(), addedDeviceId).join());
assertThrows(RuntimeException.class,
() -> accountsManager.removeDevice(updatedAccountAndDevice.first(), addedDeviceId));
final Account retrievedAccount = accountsManager.getByAccountIdentifierAsync(aci).join().orElseThrow();
@@ -393,7 +384,7 @@ public class AddRemoveDeviceIntegrationTest {
}
@Test
void waitForNewLinkedDevice() throws InterruptedException {
void waitForNewLinkedDevice() throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -433,8 +424,7 @@ public class AddRemoveDeviceIntegrationTest {
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken)
.join();
linkDeviceToken);
final Optional<DeviceInfo> maybeDeviceInfo = activeFuture.join();
@@ -447,7 +437,7 @@ public class AddRemoveDeviceIntegrationTest {
}
@Test
void waitForNewLinkedDeviceAlreadyAdded() throws InterruptedException {
void waitForNewLinkedDeviceAlreadyAdded() throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -475,8 +465,7 @@ public class AddRemoveDeviceIntegrationTest {
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken)
.join();
linkDeviceToken);
when(messagesManager.getEarliestUndeliveredTimestampForDevice(account.getUuid(), account.getPrimaryDevice()))
.thenReturn(CompletableFuture.completedFuture(Optional.empty()));
@@ -520,7 +509,7 @@ public class AddRemoveDeviceIntegrationTest {
"10_000,10_001,false", // pending message after now
})
void waitForMessageFetch(long currentTime, Long oldestMessage, boolean shouldWait)
throws InterruptedException {
throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -531,23 +520,21 @@ public class AddRemoveDeviceIntegrationTest {
final String linkDeviceToken = accountsManager.generateLinkDeviceToken(UUID.randomUUID());
final String linkDeviceTokenIdentifier = AccountsManager.getLinkDeviceTokenIdentifier(linkDeviceToken);
final Pair<Account, Device> updatedAccountAndDevice =
accountsManager.addDevice(account, new DeviceSpec(
"device-name".getBytes(StandardCharsets.UTF_8),
"password",
"OWT",
Set.of(),
1,
2,
true,
Optional.empty(),
Optional.empty(),
KeysHelper.signedECPreKey(1, aciKeyPair),
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken)
.join();
accountsManager.addDevice(account, new DeviceSpec(
"device-name".getBytes(StandardCharsets.UTF_8),
"password",
"OWT",
Set.of(),
1,
2,
true,
Optional.empty(),
Optional.empty(),
KeysHelper.signedECPreKey(1, aciKeyPair),
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken);
when(messagesManager.getEarliestUndeliveredTimestampForDevice(account.getUuid(), account.getPrimaryDevice()))
.thenReturn(CompletableFuture.completedFuture(Optional.ofNullable(oldestMessage).map(Instant::ofEpochMilli)));
@@ -564,7 +551,7 @@ public class AddRemoveDeviceIntegrationTest {
@Timeout(value = 10, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
@Test
void waitForMessageFetchRetries()
throws InterruptedException {
throws InterruptedException, LinkDeviceTokenAlreadyUsedException {
final String number = PhoneNumberUtil.getInstance().format(
PhoneNumberUtil.getInstance().getExampleNumber("US"),
PhoneNumberUtil.PhoneNumberFormat.E164);
@@ -577,21 +564,20 @@ public class AddRemoveDeviceIntegrationTest {
clock.pin(Instant.ofEpochMilli(0));
accountsManager.addDevice(account, new DeviceSpec(
"device-name".getBytes(StandardCharsets.UTF_8),
"password",
"OWT",
Set.of(),
1,
2,
true,
Optional.empty(),
Optional.empty(),
KeysHelper.signedECPreKey(1, aciKeyPair),
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken)
.join();
"device-name".getBytes(StandardCharsets.UTF_8),
"password",
"OWT",
Set.of(),
1,
2,
true,
Optional.empty(),
Optional.empty(),
KeysHelper.signedECPreKey(1, aciKeyPair),
KeysHelper.signedECPreKey(2, pniKeyPair),
KeysHelper.signedKEMPreKey(3, aciKeyPair),
KeysHelper.signedKEMPreKey(4, pniKeyPair)),
linkDeviceToken);
when(messagesManager.getEarliestUndeliveredTimestampForDevice(account.getUuid(), account.getPrimaryDevice()))
// Has a message older than the message epoch

View File

@@ -34,13 +34,11 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterEach;
@@ -100,7 +98,7 @@ class MessagePersisterTest {
when(accountsManager.getByAccountIdentifier(DESTINATION_ACCOUNT_UUID)).thenReturn(Optional.of(destinationAccount));
when(accountsManager.removeDevice(any(), anyByte()))
.thenAnswer(invocation -> CompletableFuture.completedFuture(invocation.getArgument(0)));
.thenAnswer(invocation -> invocation.getArgument(0));
when(destinationAccount.getUuid()).thenReturn(DESTINATION_ACCOUNT_UUID);
when(destinationAccount.getIdentifier(IdentityType.ACI)).thenReturn(DESTINATION_ACCOUNT_UUID);
@@ -399,9 +397,9 @@ class MessagePersisterTest {
when(destinationAccount.getDevices()).thenReturn(List.of(primary, activeA, inactiveB, inactiveC, activeD, destination));
when(messagesManager.persistMessages(any(UUID.class), any(), anyList())).thenThrow(ItemCollectionSizeLimitExceededException.builder().build());
when(accountsManager.removeDevice(destinationAccount, DESTINATION_DEVICE_ID)).thenReturn(CompletableFuture.failedFuture(new TimeoutException()));
when(accountsManager.removeDevice(destinationAccount, DESTINATION_DEVICE_ID)).thenThrow(new RuntimeException());
assertThrows(CompletionException.class, () -> messagePersister.persistQueue(destinationAccount, DESTINATION_DEVICE, "test"));
assertThrows(RuntimeException.class, () -> messagePersister.persistQueue(destinationAccount, DESTINATION_DEVICE, "test"));
}
@SuppressWarnings("SameParameterValue")

View File

@@ -19,7 +19,6 @@ import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import net.sourceforge.argparse4j.inf.Namespace;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -70,7 +69,7 @@ class UnlinkDevicesWithIdlePrimaryCommandTest {
void crawlAccounts(final boolean isDryRun) {
final AccountsManager accountsManager = mock(AccountsManager.class);
when(accountsManager.removeDevice(any(), anyByte()))
.thenReturn(CompletableFuture.completedFuture(null));
.thenReturn(null);
final Duration idleDeviceLastSeenDuration =
Duration.ofDays(UnlinkDevicesWithIdlePrimaryCommand.DEFAULT_PRIMARY_IDLE_DAYS).plus(Duration.ofDays(1));