Retire integration with legacy contact discovery system

This commit is contained in:
Jon Chambers
2023-05-02 15:57:03 -04:00
committed by GitHub
parent 8d468d17e3
commit 12b58a31a1
36 changed files with 25 additions and 1686 deletions

View File

@@ -343,30 +343,6 @@ class DynamicConfigurationTest {
}
}
@Test
void testParseDirectoryReconciler() throws JsonProcessingException {
{
final String emptyConfigYaml = REQUIRED_CONFIG.concat("test: true");
final DynamicConfiguration emptyConfig =
DynamicConfigurationManager.parseConfiguration(emptyConfigYaml, DynamicConfiguration.class).orElseThrow();
assertThat(emptyConfig.getDirectoryReconcilerConfiguration().isEnabled()).isTrue();
}
{
final String directoryReconcilerConfig = REQUIRED_CONFIG.concat("""
directoryReconciler:
enabled: false
""");
DynamicDirectoryReconcilerConfiguration directoryReconcilerConfiguration =
DynamicConfigurationManager.parseConfiguration(directoryReconcilerConfig, DynamicConfiguration.class).orElseThrow()
.getDirectoryReconcilerConfiguration();
assertThat(directoryReconcilerConfiguration.isEnabled()).isFalse();
}
}
@Test
void testParseTurnConfig() throws JsonProcessingException {
{

View File

@@ -1,122 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.sqs;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.whispersystems.textsecuregcm.storage.Account;
import software.amazon.awssdk.services.sqs.SqsAsyncClient;
import software.amazon.awssdk.services.sqs.model.MessageAttributeValue;
import software.amazon.awssdk.services.sqs.model.SendMessageRequest;
import software.amazon.awssdk.services.sqs.model.SendMessageResponse;
public class DirectoryQueueTest {
private SqsAsyncClient sqsAsyncClient;
@BeforeEach
void setUp() {
sqsAsyncClient = mock(SqsAsyncClient.class);
when(sqsAsyncClient.sendMessage(any(SendMessageRequest.class)))
.thenReturn(CompletableFuture.completedFuture(SendMessageResponse.builder().build()));
}
@ParameterizedTest
@MethodSource("argumentsForTestRefreshRegisteredUser")
void testRefreshRegisteredUser(final boolean shouldBeVisibleInDirectory, final String expectedAction) {
final DirectoryQueue directoryQueue = new DirectoryQueue(List.of("sqs://test"), sqsAsyncClient);
final Account account = mock(Account.class);
when(account.getNumber()).thenReturn("+18005556543");
when(account.getUuid()).thenReturn(UUID.randomUUID());
when(account.shouldBeVisibleInDirectory()).thenReturn(shouldBeVisibleInDirectory);
directoryQueue.refreshAccount(account);
final ArgumentCaptor<SendMessageRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageRequest.class);
verify(sqsAsyncClient).sendMessage(requestCaptor.capture());
assertEquals(MessageAttributeValue.builder().dataType("String").stringValue(expectedAction).build(),
requestCaptor.getValue().messageAttributes().get("action"));
}
@SuppressWarnings("unused")
private static Stream<Arguments> argumentsForTestRefreshRegisteredUser() {
return Stream.of(
Arguments.of(true, "add"),
Arguments.of(false, "delete"));
}
@Test
void testSendMessageMultipleQueues() {
final DirectoryQueue directoryQueue = new DirectoryQueue(List.of("sqs://first", "sqs://second"), sqsAsyncClient);
final Account account = mock(Account.class);
when(account.getNumber()).thenReturn("+18005556543");
when(account.getUuid()).thenReturn(UUID.randomUUID());
when(account.shouldBeVisibleInDirectory()).thenReturn(true);
directoryQueue.refreshAccount(account);
final ArgumentCaptor<SendMessageRequest> requestCaptor = ArgumentCaptor.forClass(SendMessageRequest.class);
verify(sqsAsyncClient, times(2)).sendMessage(requestCaptor.capture());
for (final SendMessageRequest sendMessageRequest : requestCaptor.getAllValues()) {
assertEquals(MessageAttributeValue.builder().dataType("String").stringValue("add").build(),
sendMessageRequest.messageAttributes().get("action"));
}
}
@Test
void testStop() {
final CompletableFuture<SendMessageResponse> sendMessageFuture = new CompletableFuture<>();
when(sqsAsyncClient.sendMessage(any(SendMessageRequest.class))).thenReturn(sendMessageFuture);
final DirectoryQueue directoryQueue = new DirectoryQueue(List.of("sqs://test"), sqsAsyncClient);
final Account account = mock(Account.class);
when(account.getNumber()).thenReturn("+18005556543");
when(account.getUuid()).thenReturn(UUID.randomUUID());
when(account.shouldBeVisibleInDirectory()).thenReturn(true);
directoryQueue.refreshAccount(account);
final CompletableFuture<Boolean> stopFuture = CompletableFuture.supplyAsync(() -> {
try {
directoryQueue.stop();
return true;
} catch (final Exception e) {
return false;
}
});
assertThrows(TimeoutException.class, () -> stopFuture.get(1, TimeUnit.SECONDS),
"Directory queue should not finish shutting down until all outstanding requests are resolved");
sendMessageFuture.complete(SendMessageResponse.builder().build());
assertTrue(stopFuture.join());
}
}

View File

@@ -33,15 +33,10 @@ import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class AccountsManagerChangeNumberIntegrationTest {
private static final String NUMBERS_TABLE_NAME = "numbers_test";
private static final String PNI_ASSIGNMENT_TABLE_NAME = "pni_assignment_test";
private static final String USERNAMES_TABLE_NAME = "usernames_test";
private static final int SCAN_PAGE_SIZE = 1;
@RegisterExtension
@@ -82,8 +77,7 @@ class AccountsManagerChangeNumberIntegrationTest {
SCAN_PAGE_SIZE);
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.DELETED_ACCOUNTS.tableName(),
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
Tables.DELETED_ACCOUNTS.tableName());
final DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
DYNAMO_DB_EXTENSION.getLegacyDynamoClient(),
@@ -108,7 +102,6 @@ class AccountsManagerChangeNumberIntegrationTest {
phoneNumberIdentifiers,
CACHE_CLUSTER_EXTENSION.getRedisCluster(),
deletedAccountsManager,
mock(DirectoryQueue.class),
mock(Keys.class),
mock(MessagesManager.class),
mock(ProfilesManager.class),

View File

@@ -47,7 +47,6 @@ import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
import org.whispersystems.textsecuregcm.tests.util.JsonHelpers;
@@ -111,7 +110,6 @@ class AccountsManagerConcurrentModificationIntegrationTest {
phoneNumberIdentifiers,
RedisClusterHelper.builder().stringCommands(commands).build(),
deletedAccountsManager,
mock(DirectoryQueue.class),
mock(Keys.class),
mock(MessagesManager.class),
mock(ProfilesManager.class),

View File

@@ -59,7 +59,6 @@ import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
@@ -73,7 +72,6 @@ class AccountsManagerTest {
private Accounts accounts;
private DeletedAccountsManager deletedAccountsManager;
private DirectoryQueue directoryQueue;
private Keys keys;
private MessagesManager messagesManager;
private ProfilesManager profilesManager;
@@ -96,7 +94,6 @@ class AccountsManagerTest {
void setup() throws InterruptedException {
accounts = mock(Accounts.class);
deletedAccountsManager = mock(DeletedAccountsManager.class);
directoryQueue = mock(DirectoryQueue.class);
keys = mock(Keys.class);
messagesManager = mock(MessagesManager.class);
profilesManager = mock(ProfilesManager.class);
@@ -153,7 +150,6 @@ class AccountsManagerTest {
phoneNumberIdentifiers,
RedisClusterHelper.builder().stringCommands(commands).build(),
deletedAccountsManager,
directoryQueue,
keys,
messagesManager,
profilesManager,
@@ -598,10 +594,6 @@ class AccountsManagerTest {
final Account account = accountsManager.create("+18005550123", "password", null, attributes, new ArrayList<>());
assertEquals(discoverable, account.isDiscoverableByPhoneNumber());
if (!discoverable) {
verify(directoryQueue).deleteAccount(account);
}
}
@ParameterizedTest
@@ -615,32 +607,6 @@ class AccountsManagerTest {
assertEquals(hasStorage, account.isStorageSupported());
}
@ParameterizedTest
@MethodSource
void testUpdateDirectoryQueue(final boolean visibleBeforeUpdate, final boolean visibleAfterUpdate,
final boolean expectRefresh) {
final Account account = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
// this sets up the appropriate result for Account#shouldBeVisibleInDirectory
final Device device = generateTestDevice(0);
account.addDevice(device);
account.setDiscoverableByPhoneNumber(visibleBeforeUpdate);
final Account updatedAccount = accountsManager.update(account,
a -> a.setDiscoverableByPhoneNumber(visibleAfterUpdate));
verify(directoryQueue, times(expectRefresh ? 1 : 0)).refreshAccount(updatedAccount);
}
@SuppressWarnings("unused")
private static Stream<Arguments> testUpdateDirectoryQueue() {
return Stream.of(
Arguments.of(false, false, false),
Arguments.of(true, true, false),
Arguments.of(false, true, true),
Arguments.of(true, false, true));
}
@ParameterizedTest
@MethodSource
void testUpdateDeviceLastSeen(final boolean expectUpdate, final long initialLastSeen, final long updatedLastSeen) {
@@ -680,7 +646,6 @@ class AccountsManagerTest {
assertTrue(phoneNumberIdentifiersByE164.containsKey(targetNumber));
verify(directoryQueue).changePhoneNumber(argThat(a -> a.getUuid().equals(uuid)), eq(originalNumber), eq(targetNumber));
verify(keys).delete(originalPni);
verify(keys).delete(phoneNumberIdentifiersByE164.get(targetNumber));
}
@@ -694,7 +659,6 @@ class AccountsManagerTest {
assertEquals(number, account.getNumber());
verify(deletedAccountsManager, never()).lockAndPut(anyString(), anyString(), any());
verify(directoryQueue, never()).changePhoneNumber(any(), any(), any());
verify(keys, never()).delete(any());
}
@@ -736,8 +700,6 @@ class AccountsManagerTest {
assertTrue(phoneNumberIdentifiersByE164.containsKey(targetNumber));
verify(directoryQueue).changePhoneNumber(argThat(a -> a.getUuid().equals(uuid)), eq(originalNumber), eq(targetNumber));
verify(directoryQueue).deleteAccount(existingAccount);
verify(keys).delete(originalPni);
verify(keys).delete(targetPni);
}

View File

@@ -44,7 +44,6 @@ import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.util.AttributeValues;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
@@ -114,7 +113,6 @@ class AccountsManagerUsernameIntegrationTest {
phoneNumberIdentifiers,
CACHE_CLUSTER_EXTENSION.getRedisCluster(),
deletedAccountsManager,
mock(DirectoryQueue.class),
mock(Keys.class),
mock(MessagesManager.class),
mock(ProfilesManager.class),

View File

@@ -5,21 +5,14 @@
package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.lang.Thread.State;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.function.Executable;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class DeletedAccountsManagerTest {
@@ -34,8 +27,7 @@ class DeletedAccountsManagerTest {
@BeforeEach
void setUp() {
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.DELETED_ACCOUNTS.tableName(),
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
Tables.DELETED_ACCOUNTS.tableName());
deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
DYNAMO_DB_EXTENSION.getLegacyDynamoClient(),
@@ -47,7 +39,7 @@ class DeletedAccountsManagerTest {
final UUID uuid = UUID.randomUUID();
final String e164 = "+18005551234";
deletedAccounts.put(uuid, e164, true);
deletedAccounts.put(uuid, e164);
deletedAccountsManager.lockAndTake(e164, maybeUuid -> assertEquals(Optional.of(uuid), maybeUuid));
assertEquals(Optional.empty(), deletedAccounts.findUuid(e164));
}
@@ -57,7 +49,7 @@ class DeletedAccountsManagerTest {
final UUID uuid = UUID.randomUUID();
final String e164 = "+18005551234";
deletedAccounts.put(uuid, e164, true);
deletedAccounts.put(uuid, e164);
assertThrows(RuntimeException.class, () -> deletedAccountsManager.lockAndTake(e164, maybeUuid -> {
assertEquals(Optional.of(uuid), maybeUuid);
@@ -66,73 +58,4 @@ class DeletedAccountsManagerTest {
assertEquals(Optional.of(uuid), deletedAccounts.findUuid(e164));
}
@Test
void testReconciliationLockContention() throws ChunkProcessingFailedException {
final UUID[] uuids = new UUID[3];
final String[] e164s = new String[uuids.length];
for (int i = 0; i < uuids.length; i++) {
uuids[i] = UUID.randomUUID();
e164s[i] = String.format("+1800555%04d", i);
}
final Map<String, UUID> expectedReconciledAccounts = new HashMap<>();
for (int i = 0; i < uuids.length; i++) {
deletedAccounts.put(uuids[i], e164s[i], true);
expectedReconciledAccounts.put(e164s[i], uuids[i]);
}
final UUID replacedUUID = UUID.randomUUID();
final Map<String, UUID> reconciledAccounts = new HashMap<>();
final Thread putThread = new Thread(() -> {
try {
deletedAccountsManager.lockAndPut(e164s[0], () -> replacedUUID);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},
getClass().getSimpleName() + "-put");
final Thread reconcileThread = new Thread(() -> {
try {
deletedAccountsManager.lockAndReconcileAccounts(uuids.length, deletedAccounts -> {
// We hold the lock for the first account, so a thread trying to operate on that first count should block
// waiting for the lock.
putThread.start();
// Make sure the other thread really does actually block at some point
while (putThread.getState() != State.TIMED_WAITING) {
Thread.yield();
}
deletedAccounts.forEach(pair -> reconciledAccounts.put(pair.second(), pair.first()));
return reconciledAccounts.keySet();
});
} catch (ChunkProcessingFailedException e) {
throw new AssertionError(e);
}
}, getClass().getSimpleName() + "-reconcile");
reconcileThread.start();
assertDoesNotThrow((Executable) reconcileThread::join);
assertDoesNotThrow((Executable) putThread::join);
assertEquals(expectedReconciledAccounts, reconciledAccounts);
// The "put" thread should have completed after the reconciliation thread wrapped up. We can verify that's true by
// reconciling again; the updated account (and only that account) should appear in the "needs reconciliation" list.
deletedAccountsManager.lockAndReconcileAccounts(uuids.length, deletedAccounts -> {
assertEquals(1, deletedAccounts.size());
assertEquals(replacedUUID, deletedAccounts.get(0).first());
assertEquals(e164s[0], deletedAccounts.get(0).second());
return List.of(deletedAccounts.get(0).second());
});
}
}

View File

@@ -5,20 +5,13 @@
package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.util.Pair;
class DeletedAccountsTest {
@@ -29,41 +22,7 @@ class DeletedAccountsTest {
@BeforeEach
void setUp() {
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.DELETED_ACCOUNTS.tableName(),
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
}
@Test
void testPutList() {
UUID firstUuid = UUID.randomUUID();
UUID secondUuid = UUID.randomUUID();
UUID thirdUuid = UUID.randomUUID();
String firstNumber = "+14152221234";
String secondNumber = "+14152225678";
String thirdNumber = "+14159998765";
assertTrue(deletedAccounts.listAccountsToReconcile(1).isEmpty());
deletedAccounts.put(firstUuid, firstNumber, true);
deletedAccounts.put(secondUuid, secondNumber, true);
deletedAccounts.put(thirdUuid, thirdNumber, true);
assertEquals(1, deletedAccounts.listAccountsToReconcile(1).size());
assertTrue(deletedAccounts.listAccountsToReconcile(10).containsAll(
List.of(
new Pair<>(firstUuid, firstNumber),
new Pair<>(secondUuid, secondNumber))));
deletedAccounts.markReconciled(List.of(firstNumber, secondNumber));
assertEquals(List.of(new Pair<>(thirdUuid, thirdNumber)), deletedAccounts.listAccountsToReconcile(10));
deletedAccounts.markReconciled(List.of(thirdNumber));
assertTrue(deletedAccounts.listAccountsToReconcile(1).isEmpty());
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.DELETED_ACCOUNTS.tableName());
}
@Test
@@ -73,7 +32,7 @@ class DeletedAccountsTest {
assertEquals(Optional.empty(), deletedAccounts.findUuid(e164));
deletedAccounts.put(uuid, e164, true);
deletedAccounts.put(uuid, e164);
assertEquals(Optional.of(uuid), deletedAccounts.findUuid(e164));
}
@@ -85,7 +44,7 @@ class DeletedAccountsTest {
assertEquals(Optional.empty(), deletedAccounts.findUuid(e164));
deletedAccounts.put(uuid, e164, true);
deletedAccounts.put(uuid, e164);
assertEquals(Optional.of(uuid), deletedAccounts.findUuid(e164));
@@ -94,45 +53,6 @@ class DeletedAccountsTest {
assertEquals(Optional.empty(), deletedAccounts.findUuid(e164));
}
@Test
void testGetAccountsNeedingReconciliation() {
final UUID firstUuid = UUID.randomUUID();
final UUID secondUuid = UUID.randomUUID();
final String firstNumber = "+14152221234";
final String secondNumber = "+14152225678";
final String thirdNumber = "+14159998765";
assertEquals(Collections.emptySet(),
deletedAccounts.getAccountsNeedingReconciliation(List.of(firstNumber, secondNumber, thirdNumber)));
deletedAccounts.put(firstUuid, firstNumber, true);
deletedAccounts.put(secondUuid, secondNumber, true);
assertEquals(Set.of(firstNumber, secondNumber),
deletedAccounts.getAccountsNeedingReconciliation(List.of(firstNumber, secondNumber, thirdNumber)));
}
@Test
void testGetAccountsNeedingReconciliationLargeBatch() {
final int itemCount = (DeletedAccounts.GET_BATCH_SIZE * 3) + 1;
final Set<String> expectedAccountsNeedingReconciliation = new HashSet<>(itemCount);
for (int i = 0; i < itemCount; i++) {
final String e164 = String.format("+18000555%04d", i);
deletedAccounts.put(UUID.randomUUID(), e164, true);
expectedAccountsNeedingReconciliation.add(e164);
}
final Set<String> accountsNeedingReconciliation =
deletedAccounts.getAccountsNeedingReconciliation(expectedAccountsNeedingReconciliation);
assertEquals(expectedAccountsNeedingReconciliation, accountsNeedingReconciliation);
}
@Test
void testFindE164() {
assertEquals(Optional.empty(), deletedAccounts.findE164(UUID.randomUUID()));
@@ -140,7 +60,7 @@ class DeletedAccountsTest {
final UUID uuid = UUID.randomUUID();
final String e164 = "+18005551234";
deletedAccounts.put(uuid, e164, true);
deletedAccounts.put(uuid, e164);
assertEquals(Optional.of(e164), deletedAccounts.findE164(uuid));
}
@@ -153,7 +73,7 @@ class DeletedAccountsTest {
final UUID uuid = UUID.randomUUID();
deletedAccounts.put(uuid, e164, true);
deletedAccounts.put(uuid, e164);
assertEquals(Optional.of(uuid), deletedAccounts.findUuid(e164));
}

View File

@@ -18,20 +18,6 @@ import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
public final class DynamoDbExtensionSchema {
public enum Indexes {
DELETED_ACCOUNTS_NEEDS_RECONCILIATION("needs_reconciliation_test");
private final String name;
public String indexName() {
return name;
}
Indexes(final String name) { this.name = name; }
}
public enum Tables implements DynamoDbExtension.TableSchema {
ACCOUNTS("accounts_test",
@@ -50,22 +36,11 @@ public final class DynamoDbExtensionSchema {
AttributeDefinition.builder()
.attributeName(DeletedAccounts.KEY_ACCOUNT_E164)
.attributeType(ScalarAttributeType.S).build(),
AttributeDefinition.builder()
.attributeName(DeletedAccounts.ATTR_NEEDS_CDS_RECONCILIATION)
.attributeType(ScalarAttributeType.N)
.build(),
AttributeDefinition.builder()
.attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.build()),
List.of(
GlobalSecondaryIndex.builder()
.indexName(Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName())
.keySchema(KeySchemaElement.builder().attributeName(DeletedAccounts.KEY_ACCOUNT_E164).keyType(KeyType.HASH).build(),
KeySchemaElement.builder().attributeName(DeletedAccounts.ATTR_NEEDS_CDS_RECONCILIATION).keyType(KeyType.RANGE).build())
.projection(Projection.builder().projectionType(ProjectionType.INCLUDE).nonKeyAttributes(DeletedAccounts.ATTR_ACCOUNT_UUID).build())
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
.build(),
GlobalSecondaryIndex.builder()
.indexName(DeletedAccounts.UUID_TO_E164_INDEX_NAME)
.keySchema(

View File

@@ -1,103 +0,0 @@
/*
* Copyright 2013 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.tests.controllers;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.HttpHeaders;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
import java.util.Collections;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status.Family;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.controllers.DirectoryController;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
@ExtendWith(DropwizardExtensionsSupport.class)
class DirectoryControllerTest {
private static final ExternalServiceCredentialsGenerator directoryCredentialsGenerator = mock(ExternalServiceCredentialsGenerator.class);
private static final ExternalServiceCredentials validCredentials = new ExternalServiceCredentials("username", "password");
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new DirectoryController(directoryCredentialsGenerator))
.build();
@BeforeEach
void setup() {
when(directoryCredentialsGenerator.generateFor(eq(AuthHelper.VALID_NUMBER))).thenReturn(validCredentials);
}
@Test
void testFeedbackOk() {
Response response =
resources.getJerseyTest()
.target("/v1/directory/feedback-v3/ok")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json("{\"reason\": \"test reason\"}"));
assertThat(response.getStatusInfo().getFamily()).isEqualTo(Family.SUCCESSFUL);
}
@Test
void testGetAuthToken() {
ExternalServiceCredentials token =
resources.getJerseyTest()
.target("/v1/directory/auth")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(ExternalServiceCredentials.class);
assertThat(token.username()).isEqualTo(validCredentials.username());
assertThat(token.password()).isEqualTo(validCredentials.password());
}
@Test
void testDisabledGetAuthToken() {
Response response =
resources.getJerseyTest()
.target("/v1/directory/auth")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(401);
}
@Test
void testContactIntersection() {
Response response =
resources.getJerseyTest()
.target("/v1/directory/tokens/")
.request()
.header("Authorization",
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID,
AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.X_FORWARDED_FOR, "192.168.1.1, 1.1.1.1")
.put(Entity.entity(Collections.emptyMap(), MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(429);
}
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.tests.storage;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationRequest;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationRequest.User;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationResponse;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciler;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciliationClient;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
class DirectoryReconcilerTest {
private static final UUID VALID_UUID = UUID.randomUUID();
private static final String VALID_NUMBER = "+14152222222";
private static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
private static final String UNDISCOVERABLE_NUMBER = "+14153333333";
private final Account visibleAccount = mock(Account.class);
private final Account undiscoverableAccount = mock(Account.class);
private final DirectoryReconciliationClient reconciliationClient = mock(DirectoryReconciliationClient.class);
private final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
private final DirectoryReconciler directoryReconciler = new DirectoryReconciler("test", reconciliationClient,
dynamicConfigurationManager);
private final DirectoryReconciliationResponse successResponse = new DirectoryReconciliationResponse(
DirectoryReconciliationResponse.Status.OK);
@BeforeEach
void setup() {
when(dynamicConfigurationManager.getConfiguration()).thenReturn(new DynamicConfiguration());
when(visibleAccount.getUuid()).thenReturn(VALID_UUID);
when(visibleAccount.getNumber()).thenReturn(VALID_NUMBER);
when(visibleAccount.shouldBeVisibleInDirectory()).thenReturn(true);
when(undiscoverableAccount.getUuid()).thenReturn(UNDISCOVERABLE_UUID);
when(undiscoverableAccount.getNumber()).thenReturn(UNDISCOVERABLE_NUMBER);
when(undiscoverableAccount.shouldBeVisibleInDirectory()).thenReturn(false);
}
@Test
void testCrawlChunkValid() throws AccountDatabaseCrawlerRestartException {
when(reconciliationClient.add(any())).thenReturn(successResponse);
when(reconciliationClient.delete(any())).thenReturn(successResponse);
directoryReconciler.timeAndProcessCrawlChunk(Optional.of(VALID_UUID),
Arrays.asList(visibleAccount, undiscoverableAccount));
ArgumentCaptor<DirectoryReconciliationRequest> chunkRequest = ArgumentCaptor.forClass(
DirectoryReconciliationRequest.class);
verify(reconciliationClient, times(1)).add(chunkRequest.capture());
assertThat(chunkRequest.getValue().getUsers()).isEqualTo(List.of(new User(VALID_UUID, VALID_NUMBER)));
ArgumentCaptor<DirectoryReconciliationRequest> deletesRequest = ArgumentCaptor.forClass(
DirectoryReconciliationRequest.class);
verify(reconciliationClient, times(1)).delete(deletesRequest.capture());
assertThat(deletesRequest.getValue().getUsers()).isEqualTo(
List.of(new User(UNDISCOVERABLE_UUID, UNDISCOVERABLE_NUMBER)));
verifyNoMoreInteractions(reconciliationClient);
}
}