mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 19:28:05 +01:00
Make username-related operations asynchronous
This commit is contained in:
@@ -43,6 +43,7 @@ import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.glassfish.jersey.server.ServerProperties;
|
||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -97,7 +98,6 @@ class AccountControllerTest {
|
||||
private static final String SENDER_OLD = "+14151111111";
|
||||
private static final String SENDER_PIN = "+14153333333";
|
||||
private static final String SENDER_OVER_PIN = "+14154444444";
|
||||
private static final String SENDER_OVER_PREFIX = "+14156666666";
|
||||
private static final String SENDER_PREAUTH = "+14157777777";
|
||||
private static final String SENDER_REG_LOCK = "+14158888888";
|
||||
private static final String SENDER_HAS_STORAGE = "+14159999999";
|
||||
@@ -105,14 +105,12 @@ class AccountControllerTest {
|
||||
private static final String BASE_64_URL_USERNAME_HASH_1 = "9p6Tip7BFefFOJzv4kv4GyXEYsBVfk_WbjNejdlOvQE";
|
||||
private static final String BASE_64_URL_USERNAME_HASH_2 = "NLUom-CHwtemcdvOTTXdmXmzRIV7F05leS8lwkVK_vc";
|
||||
private static final String BASE_64_URL_ENCRYPTED_USERNAME_1 = "md1votbj9r794DsqTNrBqA";
|
||||
private static final String BASE_64_URL_ENCRYPTED_USERNAME_2 = "9hrqVLy59bzgPse-S9NUsA";
|
||||
|
||||
private static final String INVALID_BASE_64_URL_USERNAME_HASH = "fA+VkNbvB6dVfx/6NpaRSK6mvhhAUBgDNWFaD7+7gvs=";
|
||||
private static final String TOO_SHORT_BASE_64_URL_USERNAME_HASH = "P2oMuxx0xgGxSpTO0ACq3IztEOBDaV9t9YFu4bAGpQ";
|
||||
private static final byte[] USERNAME_HASH_1 = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH_1);
|
||||
private static final byte[] USERNAME_HASH_2 = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH_2);
|
||||
private static final byte[] ENCRYPTED_USERNAME_1 = Base64.getUrlDecoder().decode(BASE_64_URL_ENCRYPTED_USERNAME_1);
|
||||
private static final byte[] ENCRYPTED_USERNAME_2 = Base64.getUrlDecoder().decode(BASE_64_URL_ENCRYPTED_USERNAME_2);
|
||||
private static final byte[] INVALID_USERNAME_HASH = Base64.getDecoder().decode(INVALID_BASE_64_URL_USERNAME_HASH);
|
||||
private static final byte[] TOO_SHORT_USERNAME_HASH = Base64.getUrlDecoder().decode(TOO_SHORT_BASE_64_URL_USERNAME_HASH);
|
||||
private static final String BASE_64_URL_ZK_PROOF = "2kambOgmdeeIO0faCMgR6HR4G2BQ5bnhXdIe9ZuZY0NmQXSra5BzDBQ7jzy1cvoEqUHYLpBYMrXudkYPJaWoQg";
|
||||
@@ -142,6 +140,7 @@ class AccountControllerTest {
|
||||
private byte[] registration_lock_key = new byte[32];
|
||||
|
||||
private static final ResourceExtension resources = ResourceExtension.builder()
|
||||
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
|
||||
.addProvider(AuthHelper.getAuthFilter())
|
||||
.addProvider(
|
||||
new PolymorphicAuthValueFactoryProvider.Binder<>(
|
||||
@@ -181,6 +180,8 @@ class AccountControllerTest {
|
||||
when(rateLimiters.forDescriptor(eq(RateLimiters.For.USERNAME_LOOKUP))).thenReturn(usernameLookupLimiter);
|
||||
when(rateLimiters.forDescriptor(eq(RateLimiters.For.CHECK_ACCOUNT_EXISTENCE))).thenReturn(checkAccountExistence);
|
||||
|
||||
when(usernameSetLimiter.validateAsync(any(UUID.class))).thenReturn(CompletableFuture.completedFuture(null));
|
||||
|
||||
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
||||
when(senderPinAccount.getRegistrationLock()).thenReturn(
|
||||
new StoredRegistrationLock(Optional.empty(), Optional.empty(), Instant.ofEpochMilli(System.currentTimeMillis())));
|
||||
@@ -492,19 +493,21 @@ class AccountControllerTest {
|
||||
final boolean passRateLimiting,
|
||||
final boolean validUuidInput,
|
||||
final boolean locateLinkByUuid,
|
||||
final int expectedStatus) throws Exception {
|
||||
final int expectedStatus) {
|
||||
|
||||
MockUtils.updateRateLimiterResponseToAllow(
|
||||
rateLimiters, RateLimiters.For.USERNAME_LINK_LOOKUP_PER_IP, NICE_HOST);
|
||||
MockUtils.updateRateLimiterResponseToFail(
|
||||
rateLimiters, RateLimiters.For.USERNAME_LINK_LOOKUP_PER_IP, RATE_LIMITED_IP_HOST, Duration.ofMinutes(10), false);
|
||||
|
||||
when(accountsManager.getByUsernameLinkHandle(any())).thenReturn(CompletableFuture.completedFuture(Optional.empty()));
|
||||
|
||||
final String uuid = validUuidInput ? UUID.randomUUID().toString() : "invalid-uuid";
|
||||
|
||||
if (validUuidInput && locateLinkByUuid) {
|
||||
final Account account = mock(Account.class);
|
||||
doReturn(Optional.of(RandomUtils.nextBytes(16))).when(account).getEncryptedUsername();
|
||||
doReturn(Optional.of(account)).when(accountsManager).getByUsernameLinkHandle(eq(UUID.fromString(uuid)));
|
||||
when(account.getEncryptedUsername()).thenReturn(Optional.of(RandomUtils.nextBytes(16)));
|
||||
when(accountsManager.getByUsernameLinkHandle(UUID.fromString(uuid))).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
}
|
||||
|
||||
final Invocation.Builder builder = resources.getJerseyTest()
|
||||
@@ -521,9 +524,9 @@ class AccountControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReserveUsernameHash() throws UsernameHashNotAvailableException {
|
||||
void testReserveUsernameHash() {
|
||||
when(accountsManager.reserveUsernameHash(any(), any()))
|
||||
.thenReturn(new AccountsManager.UsernameReservation(null, USERNAME_HASH_1));
|
||||
.thenReturn(CompletableFuture.completedFuture(new AccountsManager.UsernameReservation(null, USERNAME_HASH_1)));
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/accounts/username_hash/reserve")
|
||||
@@ -536,9 +539,9 @@ class AccountControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReserveUsernameHashUnavailable() throws UsernameHashNotAvailableException {
|
||||
void testReserveUsernameHashUnavailable() {
|
||||
when(accountsManager.reserveUsernameHash(any(), anyList()))
|
||||
.thenThrow(new UsernameHashNotAvailableException());
|
||||
.thenReturn(CompletableFuture.failedFuture(new UsernameHashNotAvailableException()));
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/accounts/username_hash/reserve")
|
||||
@@ -608,13 +611,14 @@ class AccountControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfirmUsernameHash()
|
||||
throws UsernameHashNotAvailableException, UsernameReservationNotFoundException, BaseUsernameException {
|
||||
void testConfirmUsernameHash() throws BaseUsernameException {
|
||||
Account account = mock(Account.class);
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
when(account.getUsernameHash()).thenReturn(Optional.of(USERNAME_HASH_1));
|
||||
when(account.getUsernameLinkHandle()).thenReturn(uuid);
|
||||
when(accountsManager.confirmReservedUsernameHash(any(), eq(USERNAME_HASH_1), eq(ENCRYPTED_USERNAME_1))).thenReturn(account);
|
||||
when(accountsManager.confirmReservedUsernameHash(any(), eq(USERNAME_HASH_1), eq(ENCRYPTED_USERNAME_1)))
|
||||
.thenReturn(CompletableFuture.completedFuture(account));
|
||||
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/accounts/username_hash/confirm")
|
||||
@@ -630,13 +634,15 @@ class AccountControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfirmUsernameHashOld()
|
||||
throws UsernameHashNotAvailableException, UsernameReservationNotFoundException, BaseUsernameException {
|
||||
void testConfirmUsernameHashOld() throws BaseUsernameException {
|
||||
Account account = mock(Account.class);
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
when(account.getUsernameHash()).thenReturn(Optional.of(USERNAME_HASH_1));
|
||||
when(account.getUsernameLinkHandle()).thenReturn(null);
|
||||
when(accountsManager.confirmReservedUsernameHash(any(), eq(USERNAME_HASH_1), eq(null))).thenReturn(account);
|
||||
when(accountsManager.confirmReservedUsernameHash(any(), eq(USERNAME_HASH_1), eq(null)))
|
||||
.thenReturn(CompletableFuture.completedFuture(account));
|
||||
|
||||
|
||||
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/accounts/username_hash/confirm")
|
||||
@@ -652,10 +658,10 @@ class AccountControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfirmUnreservedUsernameHash()
|
||||
throws UsernameHashNotAvailableException, UsernameReservationNotFoundException, BaseUsernameException {
|
||||
void testConfirmUnreservedUsernameHash() throws BaseUsernameException {
|
||||
when(accountsManager.confirmReservedUsernameHash(any(), eq(USERNAME_HASH_1), any()))
|
||||
.thenThrow(new UsernameReservationNotFoundException());
|
||||
.thenReturn(CompletableFuture.failedFuture(new UsernameReservationNotFoundException()));
|
||||
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/accounts/username_hash/confirm")
|
||||
@@ -667,10 +673,10 @@ class AccountControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfirmLapsedUsernameHash()
|
||||
throws UsernameHashNotAvailableException, UsernameReservationNotFoundException, BaseUsernameException {
|
||||
void testConfirmLapsedUsernameHash() throws BaseUsernameException {
|
||||
when(accountsManager.confirmReservedUsernameHash(any(), eq(USERNAME_HASH_1), any()))
|
||||
.thenThrow(new UsernameHashNotAvailableException());
|
||||
.thenReturn(CompletableFuture.failedFuture(new UsernameHashNotAvailableException()));
|
||||
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/accounts/username_hash/confirm")
|
||||
@@ -728,6 +734,9 @@ class AccountControllerTest {
|
||||
|
||||
@Test
|
||||
void testDeleteUsername() {
|
||||
when(accountsManager.clearUsernameHash(any()))
|
||||
.thenAnswer(invocation -> CompletableFuture.completedFuture(invocation.getArgument(0)));
|
||||
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
.target("/v1/accounts/username_hash/")
|
||||
@@ -927,7 +936,7 @@ class AccountControllerTest {
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
when(account.getUuid()).thenReturn(uuid);
|
||||
|
||||
when(accountsManager.getByUsernameHash(any())).thenReturn(Optional.of(account));
|
||||
when(accountsManager.getByUsernameHash(any())).thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
Response response = resources.getJerseyTest()
|
||||
.target(String.format("v1/accounts/username_hash/%s", BASE_64_URL_USERNAME_HASH_1))
|
||||
.request()
|
||||
@@ -939,7 +948,7 @@ class AccountControllerTest {
|
||||
|
||||
@Test
|
||||
void testLookupUsernameDoesNotExist() {
|
||||
when(accountsManager.getByUsernameHash(any())).thenReturn(Optional.empty());
|
||||
when(accountsManager.getByUsernameHash(any())).thenReturn(CompletableFuture.completedFuture(Optional.empty()));
|
||||
assertThat(resources.getJerseyTest()
|
||||
.target(String.format("v1/accounts/username_hash/%s", BASE_64_URL_USERNAME_HASH_1))
|
||||
.request()
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.ws.rs.client.Entity;
|
||||
@@ -217,7 +218,7 @@ class ProfileControllerTest {
|
||||
when(accountsManager.getByPhoneNumberIdentifier(AuthHelper.VALID_PNI_TWO)).thenReturn(Optional.of(profileAccount));
|
||||
when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO))).thenReturn(Optional.of(profileAccount));
|
||||
when(accountsManager.getByServiceIdentifier(new PniServiceIdentifier(AuthHelper.VALID_PNI_TWO))).thenReturn(Optional.of(profileAccount));
|
||||
when(accountsManager.getByUsernameHash(USERNAME_HASH)).thenReturn(Optional.of(profileAccount));
|
||||
when(accountsManager.getByUsernameHash(USERNAME_HASH)).thenReturn(CompletableFuture.completedFuture(Optional.of(profileAccount)));
|
||||
|
||||
when(accountsManager.getByE164(AuthHelper.VALID_NUMBER)).thenReturn(Optional.of(capabilitiesAccount));
|
||||
when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(capabilitiesAccount));
|
||||
|
||||
@@ -79,6 +79,7 @@ import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.MockRedisFuture;
|
||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||
import org.whispersystems.textsecuregcm.util.CompletableFutureTestUtil;
|
||||
|
||||
@Timeout(value = 10, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
|
||||
class AccountsManagerTest {
|
||||
@@ -175,7 +176,7 @@ class AccountsManagerTest {
|
||||
|
||||
enrollmentManager = mock(ExperimentEnrollmentManager.class);
|
||||
when(enrollmentManager.isEnrolled(any(UUID.class), eq(AccountsManager.USERNAME_EXPERIMENT_NAME))).thenReturn(true);
|
||||
when(accounts.usernameHashAvailable(any())).thenReturn(true);
|
||||
when(accounts.usernameHashAvailable(any())).thenReturn(CompletableFuture.completedFuture(true));
|
||||
|
||||
final AccountLockManager accountLockManager = mock(AccountLockManager.class);
|
||||
|
||||
@@ -399,21 +400,23 @@ class AccountsManagerTest {
|
||||
@Test
|
||||
void testGetAccountByUsernameHashInCache() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
when(commands.get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1))).thenReturn(uuid.toString());
|
||||
when(commands.get(eq("Account3::" + uuid))).thenReturn(
|
||||
String.format("{\"number\": \"+14152222222\", \"pni\": \"de24dc73-fbd8-41be-a7d5-764c70d9da7e\", \"usernameHash\": \"%s\"}",
|
||||
BASE_64_URL_USERNAME_HASH_1));
|
||||
when(asyncCommands.get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1)))
|
||||
.thenReturn(MockRedisFuture.completedFuture(uuid.toString()));
|
||||
|
||||
Optional<Account> account = accountsManager.getByUsernameHash(USERNAME_HASH_1);
|
||||
when(asyncCommands.get(eq("Account3::" + uuid))).thenReturn(MockRedisFuture.completedFuture(
|
||||
String.format("{\"number\": \"+14152222222\", \"pni\": \"de24dc73-fbd8-41be-a7d5-764c70d9da7e\", \"usernameHash\": \"%s\"}",
|
||||
BASE_64_URL_USERNAME_HASH_1)));
|
||||
|
||||
Optional<Account> account = accountsManager.getByUsernameHash(USERNAME_HASH_1).join();
|
||||
|
||||
assertTrue(account.isPresent());
|
||||
assertEquals(account.get().getNumber(), "+14152222222");
|
||||
assertEquals(UUID.fromString("de24dc73-fbd8-41be-a7d5-764c70d9da7e"), account.get().getPhoneNumberIdentifier());
|
||||
assertArrayEquals(USERNAME_HASH_1, account.get().getUsernameHash().get());
|
||||
|
||||
verify(commands).get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1));
|
||||
verify(commands).get(eq("Account3::" + uuid));
|
||||
verifyNoMoreInteractions(commands);
|
||||
verify(asyncCommands).get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1));
|
||||
verify(asyncCommands).get(eq("Account3::" + uuid));
|
||||
verifyNoMoreInteractions(asyncCommands);
|
||||
|
||||
verifyNoInteractions(accounts);
|
||||
}
|
||||
@@ -577,20 +580,23 @@ class AccountsManagerTest {
|
||||
Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
account.setUsernameHash(USERNAME_HASH_1);
|
||||
|
||||
when(commands.get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1))).thenReturn(null);
|
||||
when(accounts.getByUsernameHash(USERNAME_HASH_1)).thenReturn(Optional.of(account));
|
||||
when(asyncCommands.get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1)))
|
||||
.thenReturn(MockRedisFuture.completedFuture(null));
|
||||
|
||||
Optional<Account> retrieved = accountsManager.getByUsernameHash(USERNAME_HASH_1);
|
||||
when(accounts.getByUsernameHash(USERNAME_HASH_1))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
Optional<Account> retrieved = accountsManager.getByUsernameHash(USERNAME_HASH_1).join();
|
||||
|
||||
assertTrue(retrieved.isPresent());
|
||||
assertSame(retrieved.get(), account);
|
||||
|
||||
verify(commands).get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1));
|
||||
verify(commands).setex(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1), anyLong(), eq(uuid.toString()));
|
||||
verify(commands).setex(eq("AccountMap::" + account.getPhoneNumberIdentifier()), anyLong(), eq(uuid.toString()));
|
||||
verify(commands).setex(eq("AccountMap::+14152222222"), anyLong(), eq(uuid.toString()));
|
||||
verify(commands).setex(eq("Account3::" + uuid), anyLong(), anyString());
|
||||
verifyNoMoreInteractions(commands);
|
||||
verify(asyncCommands).get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1));
|
||||
verify(asyncCommands).setex(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1), anyLong(), eq(uuid.toString()));
|
||||
verify(asyncCommands).setex(eq("AccountMap::" + account.getPhoneNumberIdentifier()), anyLong(), eq(uuid.toString()));
|
||||
verify(asyncCommands).setex(eq("AccountMap::+14152222222"), anyLong(), eq(uuid.toString()));
|
||||
verify(asyncCommands).setex(eq("Account3::" + uuid), anyLong(), anyString());
|
||||
verifyNoMoreInteractions(asyncCommands);
|
||||
|
||||
verify(accounts).getByUsernameHash(USERNAME_HASH_1);
|
||||
verifyNoMoreInteractions(accounts);
|
||||
@@ -763,20 +769,23 @@ class AccountsManagerTest {
|
||||
Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
account.setUsernameHash(USERNAME_HASH_1);
|
||||
|
||||
when(commands.get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1))).thenThrow(new RedisException("OH NO"));
|
||||
when(accounts.getByUsernameHash(USERNAME_HASH_1)).thenReturn(Optional.of(account));
|
||||
when(asyncCommands.get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1)))
|
||||
.thenReturn(MockRedisFuture.failedFuture(new RedisException("OH NO")));
|
||||
|
||||
Optional<Account> retrieved = accountsManager.getByUsernameHash(USERNAME_HASH_1);
|
||||
when(accounts.getByUsernameHash(USERNAME_HASH_1))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
Optional<Account> retrieved = accountsManager.getByUsernameHash(USERNAME_HASH_1).join();
|
||||
|
||||
assertTrue(retrieved.isPresent());
|
||||
assertSame(retrieved.get(), account);
|
||||
|
||||
verify(commands).get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1));
|
||||
verify(commands).setex(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1), anyLong(), eq(uuid.toString()));
|
||||
verify(commands).setex(eq("AccountMap::" + account.getPhoneNumberIdentifier()), anyLong(), eq(uuid.toString()));
|
||||
verify(commands).setex(eq("AccountMap::+14152222222"), anyLong(), eq(uuid.toString()));
|
||||
verify(commands).setex(eq("Account3::" + uuid), anyLong(), anyString());
|
||||
verifyNoMoreInteractions(commands);
|
||||
verify(asyncCommands).get(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1));
|
||||
verify(asyncCommands).setex(eq("UAccountMap::" + BASE_64_URL_USERNAME_HASH_1), anyLong(), eq(uuid.toString()));
|
||||
verify(asyncCommands).setex(eq("AccountMap::" + account.getPhoneNumberIdentifier()), anyLong(), eq(uuid.toString()));
|
||||
verify(asyncCommands).setex(eq("AccountMap::+14152222222"), anyLong(), eq(uuid.toString()));
|
||||
verify(asyncCommands).setex(eq("Account3::" + uuid), anyLong(), anyString());
|
||||
verifyNoMoreInteractions(asyncCommands);
|
||||
|
||||
verify(accounts).getByUsernameHash(USERNAME_HASH_1);
|
||||
verifyNoMoreInteractions(accounts);
|
||||
@@ -1397,7 +1406,8 @@ class AccountsManagerTest {
|
||||
void testReserveUsernameHash() throws UsernameHashNotAvailableException {
|
||||
final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
final List<byte[]> usernameHashes = List.of(new byte[32], new byte[32]);
|
||||
when(accounts.usernameHashAvailable(any())).thenReturn(true);
|
||||
when(accounts.usernameHashAvailable(any())).thenReturn(CompletableFuture.completedFuture(true));
|
||||
when(accounts.reserveUsernameHash(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null));
|
||||
accountsManager.reserveUsernameHash(account, usernameHashes);
|
||||
verify(accounts).reserveUsernameHash(eq(account), eq(new byte[32]), eq(Duration.ofMinutes(5)));
|
||||
}
|
||||
@@ -1405,26 +1415,31 @@ class AccountsManagerTest {
|
||||
@Test
|
||||
void testReserveUsernameHashNotAvailable() {
|
||||
final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
when(accounts.usernameHashAvailable(any())).thenReturn(false);
|
||||
when(accounts.usernameHashAvailable(any())).thenReturn(CompletableFuture.completedFuture(false));
|
||||
|
||||
assertThrows(UsernameHashNotAvailableException.class, () -> accountsManager.reserveUsernameHash(account, List.of(
|
||||
USERNAME_HASH_1, USERNAME_HASH_2)));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(UsernameHashNotAvailableException.class,
|
||||
accountsManager.reserveUsernameHash(account, List.of(USERNAME_HASH_1, USERNAME_HASH_2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReserveUsernameDisabled() {
|
||||
final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
when(enrollmentManager.isEnrolled(account.getUuid(), AccountsManager.USERNAME_EXPERIMENT_NAME)).thenReturn(false);
|
||||
assertThrows(UsernameHashNotAvailableException.class, () -> accountsManager.reserveUsernameHash(account, List.of(
|
||||
USERNAME_HASH_1)));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(UsernameHashNotAvailableException.class,
|
||||
accountsManager.reserveUsernameHash(account, List.of(USERNAME_HASH_1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfirmReservedUsernameHash() throws UsernameHashNotAvailableException, UsernameReservationNotFoundException {
|
||||
final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
setReservationHash(account, USERNAME_HASH_1);
|
||||
when(accounts.usernameHashAvailable(eq(Optional.of(account.getUuid())), eq(USERNAME_HASH_1))).thenReturn(true);
|
||||
accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
when(accounts.usernameHashAvailable(Optional.of(account.getUuid()), USERNAME_HASH_1))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
|
||||
when(accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1))
|
||||
.thenReturn(CompletableFuture.completedFuture(null));
|
||||
|
||||
accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
verify(accounts).confirmUsernameHash(eq(account), eq(USERNAME_HASH_1), eq(ENCRYPTED_USERNAME_1));
|
||||
}
|
||||
|
||||
@@ -1432,9 +1447,10 @@ class AccountsManagerTest {
|
||||
void testConfirmReservedHashNameMismatch() {
|
||||
final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
setReservationHash(account, USERNAME_HASH_1);
|
||||
when(accounts.usernameHashAvailable(eq(Optional.of(account.getUuid())), eq(USERNAME_HASH_1))).thenReturn(true);
|
||||
assertThrows(UsernameReservationNotFoundException.class,
|
||||
() -> accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_2, ENCRYPTED_USERNAME_2));
|
||||
when(accounts.usernameHashAvailable(eq(Optional.of(account.getUuid())), eq(USERNAME_HASH_1)))
|
||||
.thenReturn(CompletableFuture.completedFuture(true));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(UsernameReservationNotFoundException.class,
|
||||
accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_2, ENCRYPTED_USERNAME_2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1442,9 +1458,10 @@ class AccountsManagerTest {
|
||||
final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
// hash was reserved, but the reservation lapsed and another account took it
|
||||
setReservationHash(account, USERNAME_HASH_1);
|
||||
when(accounts.usernameHashAvailable(eq(Optional.of(account.getUuid())), eq(USERNAME_HASH_1))).thenReturn(false);
|
||||
assertThrows(UsernameHashNotAvailableException.class, () -> accountsManager.confirmReservedUsernameHash(account,
|
||||
USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
when(accounts.usernameHashAvailable(eq(Optional.of(account.getUuid())), eq(USERNAME_HASH_1)))
|
||||
.thenReturn(CompletableFuture.completedFuture(false));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(UsernameHashNotAvailableException.class,
|
||||
accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
verify(accounts, never()).confirmUsernameHash(any(), any(), any());
|
||||
}
|
||||
|
||||
@@ -1454,7 +1471,7 @@ class AccountsManagerTest {
|
||||
account.setUsernameHash(USERNAME_HASH_1);
|
||||
|
||||
// reserved username already set, should be treated as a replay
|
||||
accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
verifyNoInteractions(accounts);
|
||||
}
|
||||
|
||||
@@ -1462,16 +1479,19 @@ class AccountsManagerTest {
|
||||
void testConfirmReservedUsernameHashWithNoReservation() {
|
||||
final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(),
|
||||
new ArrayList<>(), new byte[16]);
|
||||
assertThrows(UsernameReservationNotFoundException.class,
|
||||
() -> accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(UsernameReservationNotFoundException.class,
|
||||
accountsManager.confirmReservedUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
verify(accounts, never()).confirmUsernameHash(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearUsernameHash() {
|
||||
when(accounts.clearUsernameHash(any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(null));
|
||||
|
||||
Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
|
||||
account.setUsernameHash(USERNAME_HASH_1);
|
||||
accountsManager.clearUsernameHash(account);
|
||||
accountsManager.clearUsernameHash(account).join();
|
||||
verify(accounts).clearUsernameHash(eq(account));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.storage;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
@@ -45,6 +44,7 @@ import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||
import org.whispersystems.textsecuregcm.util.CompletableFutureTestUtil;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
||||
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
||||
@@ -159,7 +159,10 @@ class AccountsManagerUsernameIntegrationTest {
|
||||
.item(item)
|
||||
.build());
|
||||
}
|
||||
assertThrows(UsernameHashNotAvailableException.class, () -> {accountsManager.reserveUsernameHash(account, usernameHashes);});
|
||||
|
||||
CompletableFutureTestUtil.assertFailsWithCause(UsernameHashNotAvailableException.class,
|
||||
accountsManager.reserveUsernameHash(account, usernameHashes));
|
||||
|
||||
assertThat(accountsManager.getByAccountIdentifier(account.getUuid()).orElseThrow().getUsernameHash()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -185,9 +188,12 @@ class AccountsManagerUsernameIntegrationTest {
|
||||
// first time this is called lie and say the username is available
|
||||
// this simulates seeing an available username and then it being taken
|
||||
// by someone before the write
|
||||
doReturn(true).doCallRealMethod().when(accounts).usernameHashAvailable(any());
|
||||
doReturn(CompletableFuture.completedFuture(true))
|
||||
.doCallRealMethod()
|
||||
.when(accounts).usernameHashAvailable(any());
|
||||
final byte[] username = accountsManager
|
||||
.reserveUsernameHash(account, usernameHashes)
|
||||
.join()
|
||||
.reservedUsernameHash();
|
||||
|
||||
assertArrayEquals(username, availableHash);
|
||||
@@ -204,26 +210,27 @@ class AccountsManagerUsernameIntegrationTest {
|
||||
new ArrayList<>());
|
||||
|
||||
// reserve
|
||||
AccountsManager.UsernameReservation reservation = accountsManager.reserveUsernameHash(account, List.of(
|
||||
USERNAME_HASH_1));
|
||||
AccountsManager.UsernameReservation reservation =
|
||||
accountsManager.reserveUsernameHash(account, List.of(USERNAME_HASH_1)).join();
|
||||
|
||||
assertArrayEquals(reservation.account().getReservedUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
assertThat(accountsManager.getByUsernameHash(reservation.reservedUsernameHash())).isEmpty();
|
||||
assertThat(accountsManager.getByUsernameHash(reservation.reservedUsernameHash()).join()).isEmpty();
|
||||
|
||||
// confirm
|
||||
account = accountsManager.confirmReservedUsernameHash(
|
||||
reservation.account(),
|
||||
reservation.reservedUsernameHash(),
|
||||
ENCRYPTED_USERNAME_1);
|
||||
ENCRYPTED_USERNAME_1).join();
|
||||
assertArrayEquals(account.getUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
assertThat(accountsManager.getByUsernameHash(USERNAME_HASH_1).orElseThrow().getUuid()).isEqualTo(
|
||||
assertThat(accountsManager.getByUsernameHash(USERNAME_HASH_1).join().orElseThrow().getUuid()).isEqualTo(
|
||||
account.getUuid());
|
||||
assertThat(account.getUsernameLinkHandle()).isNotNull();
|
||||
assertThat(accountsManager.getByUsernameLinkHandle(account.getUsernameLinkHandle()).orElseThrow().getUuid())
|
||||
assertThat(accountsManager.getByUsernameLinkHandle(account.getUsernameLinkHandle()).join().orElseThrow().getUuid())
|
||||
.isEqualTo(account.getUuid());
|
||||
|
||||
// clear
|
||||
account = accountsManager.clearUsernameHash(account);
|
||||
assertThat(accountsManager.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
|
||||
account = accountsManager.clearUsernameHash(account).join();
|
||||
assertThat(accountsManager.getByUsernameHash(USERNAME_HASH_1).join()).isEmpty();
|
||||
assertThat(accountsManager.getByAccountIdentifier(account.getUuid()).orElseThrow().getUsernameHash()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -233,8 +240,9 @@ class AccountsManagerUsernameIntegrationTest {
|
||||
|
||||
final Account account = accountsManager.create("+18005551111", "password", null, new AccountAttributes(),
|
||||
new ArrayList<>());
|
||||
AccountsManager.UsernameReservation reservation1 = accountsManager.reserveUsernameHash(account, List.of(
|
||||
USERNAME_HASH_1));
|
||||
|
||||
AccountsManager.UsernameReservation reservation1 =
|
||||
accountsManager.reserveUsernameHash(account, List.of(USERNAME_HASH_1)).join();
|
||||
|
||||
long past = Instant.now().minus(Duration.ofMinutes(1)).getEpochSecond();
|
||||
// force expiration
|
||||
@@ -249,31 +257,32 @@ class AccountsManagerUsernameIntegrationTest {
|
||||
// a different account should be able to reserve it
|
||||
Account account2 = accountsManager.create("+18005552222", "password", null, new AccountAttributes(),
|
||||
new ArrayList<>());
|
||||
final AccountsManager.UsernameReservation reservation2 = accountsManager.reserveUsernameHash(account2, List.of(
|
||||
USERNAME_HASH_1));
|
||||
final AccountsManager.UsernameReservation reservation2 =
|
||||
accountsManager.reserveUsernameHash(account2, List.of(USERNAME_HASH_1)).join();
|
||||
assertArrayEquals(reservation2.reservedUsernameHash(), USERNAME_HASH_1);
|
||||
|
||||
assertThrows(UsernameHashNotAvailableException.class,
|
||||
() -> accountsManager.confirmReservedUsernameHash(reservation1.account(), USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
account2 = accountsManager.confirmReservedUsernameHash(reservation2.account(), USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
assertEquals(accountsManager.getByUsernameHash(USERNAME_HASH_1).orElseThrow().getUuid(), account2.getUuid());
|
||||
CompletableFutureTestUtil.assertFailsWithCause(UsernameHashNotAvailableException.class,
|
||||
accountsManager.confirmReservedUsernameHash(reservation1.account(), USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
account2 = accountsManager.confirmReservedUsernameHash(reservation2.account(), USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
assertEquals(accountsManager.getByUsernameHash(USERNAME_HASH_1).join().orElseThrow().getUuid(), account2.getUuid());
|
||||
assertArrayEquals(account2.getUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUsernameSetReserveAnotherClearSetReserved()
|
||||
throws InterruptedException, UsernameHashNotAvailableException, UsernameReservationNotFoundException {
|
||||
void testUsernameSetReserveAnotherClearSetReserved() throws InterruptedException {
|
||||
Account account = accountsManager.create("+18005551111", "password", null, new AccountAttributes(),
|
||||
new ArrayList<>());
|
||||
|
||||
// Set username hash
|
||||
final AccountsManager.UsernameReservation reservation1 = accountsManager.reserveUsernameHash(account, List.of(
|
||||
USERNAME_HASH_1));
|
||||
account = accountsManager.confirmReservedUsernameHash(reservation1.account(), USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
final AccountsManager.UsernameReservation reservation1 =
|
||||
accountsManager.reserveUsernameHash(account, List.of(USERNAME_HASH_1)).join();
|
||||
|
||||
account = accountsManager.confirmReservedUsernameHash(reservation1.account(), USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
|
||||
// Reserve another hash on the same account
|
||||
final AccountsManager.UsernameReservation reservation2 = accountsManager.reserveUsernameHash(account, List.of(
|
||||
USERNAME_HASH_2));
|
||||
final AccountsManager.UsernameReservation reservation2 =
|
||||
accountsManager.reserveUsernameHash(account, List.of(USERNAME_HASH_2)).join();
|
||||
|
||||
account = reservation2.account();
|
||||
|
||||
assertArrayEquals(account.getReservedUsernameHash().orElseThrow(), USERNAME_HASH_2);
|
||||
@@ -281,12 +290,12 @@ class AccountsManagerUsernameIntegrationTest {
|
||||
assertArrayEquals(account.getEncryptedUsername().orElseThrow(), ENCRYPTED_USERNAME_1);
|
||||
|
||||
// Clear the set username hash but not the reserved one
|
||||
account = accountsManager.clearUsernameHash(account);
|
||||
account = accountsManager.clearUsernameHash(account).join();
|
||||
assertThat(account.getReservedUsernameHash()).isPresent();
|
||||
assertThat(account.getUsernameHash()).isEmpty();
|
||||
|
||||
// Confirm second reservation
|
||||
account = accountsManager.confirmReservedUsernameHash(account, reservation2.reservedUsernameHash(), ENCRYPTED_USERNAME_2);
|
||||
account = accountsManager.confirmReservedUsernameHash(account, reservation2.reservedUsernameHash(), ENCRYPTED_USERNAME_2).join();
|
||||
assertArrayEquals(account.getUsernameHash().orElseThrow(), USERNAME_HASH_2);
|
||||
assertArrayEquals(account.getEncryptedUsername().orElseThrow(), ENCRYPTED_USERNAME_2);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.whispersystems.textsecuregcm.storage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
@@ -43,6 +42,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -61,6 +61,7 @@ import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
|
||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||
import org.whispersystems.textsecuregcm.util.CompletableFutureTestUtil;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import org.whispersystems.textsecuregcm.util.TestClock;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
@@ -147,25 +148,27 @@ class AccountsTest {
|
||||
final byte[] encruptedUsername1 = RandomUtils.nextBytes(32);
|
||||
account.setUsernameLinkDetails(linkHandle1, encruptedUsername1);
|
||||
accounts.update(account);
|
||||
validator.accept(accounts.getByUsernameLinkHandle(linkHandle1), encruptedUsername1);
|
||||
validator.accept(accounts.getByUsernameLinkHandle(linkHandle1).join(), encruptedUsername1);
|
||||
|
||||
// updating username link, storing new one, checking that it can be looked up, checking that old one can't be looked up
|
||||
final UUID linkHandle2 = UUID.randomUUID();
|
||||
final byte[] encruptedUsername2 = RandomUtils.nextBytes(32);
|
||||
account.setUsernameLinkDetails(linkHandle2, encruptedUsername2);
|
||||
accounts.update(account);
|
||||
validator.accept(accounts.getByUsernameLinkHandle(linkHandle2), encruptedUsername2);
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle1).isEmpty());
|
||||
validator.accept(accounts.getByUsernameLinkHandle(linkHandle2).join(), encruptedUsername2);
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle1).join().isEmpty());
|
||||
|
||||
// deleting username link, checking it can't be looked up by either handle
|
||||
account.setUsernameLinkDetails(null, null);
|
||||
accounts.update(account);
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle1).isEmpty());
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle2).isEmpty());
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle1).join().isEmpty());
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle2).join().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsernameLinksViaAccountsManager() throws Exception {
|
||||
@Disabled
|
||||
// TODO: @Sergey: what's the story with this test?
|
||||
public void testUsernameLinksViaAccountsManager() {
|
||||
final AccountsManager accountsManager = new AccountsManager(
|
||||
accounts,
|
||||
mock(PhoneNumberIdentifiers.class),
|
||||
@@ -190,7 +193,7 @@ class AccountsTest {
|
||||
final byte[] encryptedUsername = RandomUtils.nextBytes(32);
|
||||
accountsManager.update(account, a -> a.setUsernameLinkDetails(linkHandle, encryptedUsername));
|
||||
|
||||
final Optional<Account> maybeAccount = accountsManager.getByUsernameLinkHandle(linkHandle);
|
||||
final Optional<Account> maybeAccount = accountsManager.getByUsernameLinkHandle(linkHandle).join();
|
||||
assertTrue(maybeAccount.isPresent());
|
||||
assertTrue(maybeAccount.get().getEncryptedUsername().isPresent());
|
||||
assertArrayEquals(encryptedUsername, maybeAccount.get().getEncryptedUsername().get());
|
||||
@@ -199,7 +202,7 @@ class AccountsTest {
|
||||
final Optional<Account> accountToChange = accountsManager.getByAccountIdentifier(account.getUuid());
|
||||
assertTrue(accountToChange.isPresent());
|
||||
accountsManager.update(accountToChange.get(), a -> a.setDiscoverableByPhoneNumber(!a.isDiscoverableByPhoneNumber()));
|
||||
final Optional<Account> accountAfterChange = accountsManager.getByUsernameLinkHandle(linkHandle);
|
||||
final Optional<Account> accountAfterChange = accountsManager.getByUsernameLinkHandle(linkHandle).join();
|
||||
assertTrue(accountAfterChange.isPresent());
|
||||
assertTrue(accountAfterChange.get().getEncryptedUsername().isPresent());
|
||||
assertArrayEquals(encryptedUsername, accountAfterChange.get().getEncryptedUsername().get());
|
||||
@@ -207,7 +210,7 @@ class AccountsTest {
|
||||
// now deleting the link
|
||||
final Optional<Account> accountToDeleteLink = accountsManager.getByAccountIdentifier(account.getUuid());
|
||||
accountsManager.update(accountToDeleteLink.get(), a -> a.setUsernameLinkDetails(null, null));
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle).isEmpty());
|
||||
assertTrue(accounts.getByUsernameLinkHandle(linkHandle).join().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -391,7 +394,7 @@ class AccountsTest {
|
||||
byteGenerator.nextBytes(encryptedUsername);
|
||||
|
||||
// Set up the existing account to have a username hash
|
||||
accounts.confirmUsernameHash(account, usernameHash, encryptedUsername);
|
||||
accounts.confirmUsernameHash(account, usernameHash, encryptedUsername).join();
|
||||
|
||||
verifyStoredState("+14151112222", account.getUuid(), account.getPhoneNumberIdentifier(), usernameHash, account, true);
|
||||
|
||||
@@ -789,40 +792,40 @@ class AccountsTest {
|
||||
final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isEmpty();
|
||||
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
final UUID oldHandle = account.getUsernameLinkHandle();
|
||||
|
||||
{
|
||||
final Optional<Account> maybeAccount = accounts.getByUsernameHash(USERNAME_HASH_1);
|
||||
|
||||
final Optional<Account> maybeAccount = accounts.getByUsernameHash(USERNAME_HASH_1).join();
|
||||
verifyStoredState(account.getNumber(), account.getUuid(), account.getPhoneNumberIdentifier(), USERNAME_HASH_1, maybeAccount.orElseThrow(), account);
|
||||
final Optional<Account> maybeAccount2 = accounts.getByUsernameLinkHandle(oldHandle);
|
||||
|
||||
final Optional<Account> maybeAccount2 = accounts.getByUsernameLinkHandle(oldHandle).join();
|
||||
verifyStoredState(account.getNumber(), account.getUuid(), account.getPhoneNumberIdentifier(), USERNAME_HASH_1, maybeAccount2.orElseThrow(), account);
|
||||
}
|
||||
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_2, Duration.ofDays(1));
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_2, ENCRYPTED_USERNAME_2);
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_2, Duration.ofDays(1)).join();
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_2, ENCRYPTED_USERNAME_2).join();
|
||||
final UUID newHandle = account.getUsernameLinkHandle();
|
||||
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isEmpty();
|
||||
assertThat(DYNAMO_DB_EXTENSION.getDynamoDbClient()
|
||||
.getItem(GetItemRequest.builder()
|
||||
.tableName(Tables.USERNAMES.tableName())
|
||||
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
|
||||
.build())
|
||||
.item()).isEmpty();
|
||||
assertThat(accounts.getByUsernameLinkHandle(oldHandle)).isEmpty();
|
||||
assertThat(accounts.getByUsernameLinkHandle(oldHandle).join()).isEmpty();
|
||||
|
||||
{
|
||||
final Optional<Account> maybeAccount = accounts.getByUsernameHash(USERNAME_HASH_2);
|
||||
final Optional<Account> maybeAccount = accounts.getByUsernameHash(USERNAME_HASH_2).join();
|
||||
|
||||
assertThat(maybeAccount).isPresent();
|
||||
verifyStoredState(account.getNumber(), account.getUuid(), account.getPhoneNumberIdentifier(),
|
||||
USERNAME_HASH_2, maybeAccount.get(), account);
|
||||
final Optional<Account> maybeAccount2 = accounts.getByUsernameLinkHandle(newHandle);
|
||||
final Optional<Account> maybeAccount2 = accounts.getByUsernameLinkHandle(newHandle).join();
|
||||
verifyStoredState(account.getNumber(), account.getUuid(), account.getPhoneNumberIdentifier(),
|
||||
USERNAME_HASH_2, maybeAccount2.get(), account);
|
||||
}
|
||||
@@ -838,26 +841,26 @@ class AccountsTest {
|
||||
|
||||
// first account reserves and confirms username hash
|
||||
assertThatNoException().isThrownBy(() -> {
|
||||
accounts.reserveUsernameHash(firstAccount, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.confirmUsernameHash(firstAccount, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
accounts.reserveUsernameHash(firstAccount, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
accounts.confirmUsernameHash(firstAccount, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
});
|
||||
|
||||
final Optional<Account> maybeAccount = accounts.getByUsernameHash(USERNAME_HASH_1);
|
||||
final Optional<Account> maybeAccount = accounts.getByUsernameHash(USERNAME_HASH_1).join();
|
||||
|
||||
assertThat(maybeAccount).isPresent();
|
||||
verifyStoredState(firstAccount.getNumber(), firstAccount.getUuid(), firstAccount.getPhoneNumberIdentifier(), USERNAME_HASH_1, maybeAccount.get(), firstAccount);
|
||||
|
||||
// throw an error if second account tries to reserve or confirm the same username hash
|
||||
assertThatExceptionOfType(ContestedOptimisticLockException.class)
|
||||
.isThrownBy(() -> accounts.reserveUsernameHash(secondAccount, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
assertThatExceptionOfType(ContestedOptimisticLockException.class)
|
||||
.isThrownBy(() -> accounts.confirmUsernameHash(secondAccount, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.reserveUsernameHash(secondAccount, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.confirmUsernameHash(secondAccount, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
|
||||
// throw an error if first account tries to reserve or confirm the username hash that it has already confirmed
|
||||
assertThatExceptionOfType(ContestedOptimisticLockException.class)
|
||||
.isThrownBy(() -> accounts.reserveUsernameHash(firstAccount, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
assertThatExceptionOfType(ContestedOptimisticLockException.class)
|
||||
.isThrownBy(() -> accounts.confirmUsernameHash(firstAccount, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.reserveUsernameHash(firstAccount, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.confirmUsernameHash(firstAccount, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
|
||||
assertThat(secondAccount.getReservedUsernameHash()).isEmpty();
|
||||
assertThat(secondAccount.getUsernameHash()).isEmpty();
|
||||
@@ -867,11 +870,11 @@ class AccountsTest {
|
||||
void testConfirmUsernameHashVersionMismatch() {
|
||||
final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
account.setVersion(account.getVersion() + 77);
|
||||
|
||||
assertThatExceptionOfType(ContestedOptimisticLockException.class)
|
||||
.isThrownBy(() -> accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
|
||||
assertThat(account.getUsernameHash()).isEmpty();
|
||||
}
|
||||
@@ -881,13 +884,13 @@ class AccountsTest {
|
||||
final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isPresent();
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isPresent();
|
||||
|
||||
accounts.clearUsernameHash(account);
|
||||
accounts.clearUsernameHash(account).join();
|
||||
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isEmpty();
|
||||
assertThat(accounts.getByAccountIdentifier(account.getUuid()))
|
||||
.hasValueSatisfying(clearedAccount -> assertThat(clearedAccount.getUsernameHash()).isEmpty());
|
||||
}
|
||||
@@ -897,7 +900,7 @@ class AccountsTest {
|
||||
final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
|
||||
assertThatNoException().isThrownBy(() -> accounts.clearUsernameHash(account));
|
||||
assertThatNoException().isThrownBy(() -> accounts.clearUsernameHash(account).join());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -905,12 +908,13 @@ class AccountsTest {
|
||||
final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
|
||||
account.setVersion(account.getVersion() + 12);
|
||||
|
||||
assertThatExceptionOfType(ContestedOptimisticLockException.class).isThrownBy(() -> accounts.clearUsernameHash(account));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.clearUsernameHash(account));
|
||||
|
||||
assertArrayEquals(account.getUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
}
|
||||
@@ -922,21 +926,21 @@ class AccountsTest {
|
||||
final Account account2 = generateAccount("+18005552222", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account2);
|
||||
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
assertArrayEquals(account1.getReservedUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
assertThat(account1.getUsernameHash()).isEmpty();
|
||||
|
||||
// account 2 shouldn't be able to reserve or confirm the same username hash
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.reserveUsernameHash(account2, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.confirmUsernameHash(account2, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.reserveUsernameHash(account2, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.confirmUsernameHash(account2, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isEmpty();
|
||||
|
||||
accounts.confirmUsernameHash(account1, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
accounts.confirmUsernameHash(account1, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
assertThat(account1.getReservedUsernameHash()).isEmpty();
|
||||
assertArrayEquals(account1.getUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).get().getUuid()).isEqualTo(account1.getUuid());
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join().get().getUuid()).isEqualTo(account1.getUuid());
|
||||
|
||||
final Map<String, AttributeValue> usernameConstraintRecord = DYNAMO_DB_EXTENSION.getDynamoDbClient()
|
||||
.getItem(GetItemRequest.builder()
|
||||
@@ -954,17 +958,17 @@ class AccountsTest {
|
||||
final Account account1 = generateAccount("+18005551111", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account1);
|
||||
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
assertThat(accounts.usernameHashAvailable(USERNAME_HASH_1)).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.empty(), USERNAME_HASH_1)).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(UUID.randomUUID()), USERNAME_HASH_1)).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(account1.getUuid()), USERNAME_HASH_1)).isTrue();
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
assertThat(accounts.usernameHashAvailable(USERNAME_HASH_1).join()).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.empty(), USERNAME_HASH_1).join()).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(UUID.randomUUID()), USERNAME_HASH_1).join()).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(account1.getUuid()), USERNAME_HASH_1).join()).isTrue();
|
||||
|
||||
accounts.confirmUsernameHash(account1, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
assertThat(accounts.usernameHashAvailable(USERNAME_HASH_1)).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.empty(), USERNAME_HASH_1)).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(UUID.randomUUID()), USERNAME_HASH_1)).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(account1.getUuid()), USERNAME_HASH_1)).isFalse();
|
||||
accounts.confirmUsernameHash(account1, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
assertThat(accounts.usernameHashAvailable(USERNAME_HASH_1).join()).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.empty(), USERNAME_HASH_1).join()).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(UUID.randomUUID()), USERNAME_HASH_1).join()).isFalse();
|
||||
assertThat(accounts.usernameHashAvailable(Optional.of(account1.getUuid()), USERNAME_HASH_1).join()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -974,13 +978,13 @@ class AccountsTest {
|
||||
final Account account2 = generateAccount("+18005552222", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account2);
|
||||
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
assertArrayEquals(account1.getReservedUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
assertThat(account1.getUsernameHash()).isEmpty();
|
||||
|
||||
// only account1 should be able to confirm the reserved hash
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.confirmUsernameHash(account2, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.confirmUsernameHash(account2, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -990,37 +994,36 @@ class AccountsTest {
|
||||
final Account account2 = generateAccount("+18005552222", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account2);
|
||||
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(2));
|
||||
|
||||
Runnable runnable = () -> accounts.reserveUsernameHash(account2, USERNAME_HASH_1, Duration.ofDays(1));
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(2)).join();
|
||||
|
||||
for (int i = 0; i <= 2; i++) {
|
||||
clock.pin(Instant.EPOCH.plus(Duration.ofDays(i)));
|
||||
assertThrows(ContestedOptimisticLockException.class, runnable::run);
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.reserveUsernameHash(account2, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
}
|
||||
|
||||
// after 2 days, can reserve and confirm the hash
|
||||
clock.pin(Instant.EPOCH.plus(Duration.ofDays(2)).plus(Duration.ofSeconds(1)));
|
||||
runnable.run();
|
||||
accounts.reserveUsernameHash(account2, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
assertEquals(account2.getReservedUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||
|
||||
accounts.confirmUsernameHash(account2, USERNAME_HASH_1, ENCRYPTED_USERNAME_1);
|
||||
accounts.confirmUsernameHash(account2, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(2)));
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.confirmUsernameHash(account1, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).get().getUuid()).isEqualTo(account2.getUuid());
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.reserveUsernameHash(account1, USERNAME_HASH_1, Duration.ofDays(2)));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.confirmUsernameHash(account1, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join().get().getUuid()).isEqualTo(account2.getUuid());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRetryReserveUsernameHash() {
|
||||
final Account account = generateAccount("+18005551111", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(2));
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(2)).join();
|
||||
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(2)),
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(2)),
|
||||
"Shouldn't be able to re-reserve same username hash (would extend ttl)");
|
||||
}
|
||||
|
||||
@@ -1029,10 +1032,10 @@ class AccountsTest {
|
||||
final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
account.setVersion(account.getVersion() + 12);
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
assertThrows(ContestedOptimisticLockException.class,
|
||||
() -> accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1)));
|
||||
CompletableFutureTestUtil.assertFailsWithCause(ContestedOptimisticLockException.class,
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1));
|
||||
assertThat(account.getReservedUsernameHash()).isEmpty();
|
||||
assertThat(account.getUsernameHash()).isEmpty();
|
||||
}
|
||||
@@ -1055,6 +1058,21 @@ class AccountsTest {
|
||||
.forEach(field -> assertFalse(dataMap.containsKey(field)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetByUsernameHashAsync() {
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isEmpty();
|
||||
|
||||
final Account account = generateAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID());
|
||||
accounts.create(account);
|
||||
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isEmpty();
|
||||
|
||||
accounts.reserveUsernameHash(account, USERNAME_HASH_1, Duration.ofDays(1)).join();
|
||||
accounts.confirmUsernameHash(account, USERNAME_HASH_1, ENCRYPTED_USERNAME_1).join();
|
||||
|
||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).join()).isPresent();
|
||||
}
|
||||
|
||||
private static Device generateDevice(long id) {
|
||||
return DevicesHelper.createDevice(id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class CompletableFutureTestUtil {
|
||||
|
||||
private CompletableFutureTestUtil() {
|
||||
}
|
||||
|
||||
public static <T extends Throwable> void assertFailsWithCause(final Class<T> expectedCause, final CompletableFuture<?> completableFuture) {
|
||||
assertFailsWithCause(expectedCause, completableFuture, null);
|
||||
}
|
||||
|
||||
public static <T extends Throwable> void assertFailsWithCause(final Class<T> expectedCause, final CompletableFuture<?> completableFuture, final String message) {
|
||||
final CompletionException completionException = assertThrows(CompletionException.class, completableFuture::join, message);
|
||||
assertTrue(ExceptionUtils.unwrap(completionException).getClass().isAssignableFrom(expectedCause), message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user