DynamoDBExtension refactor and helpers for our schema (#1327)
There's a lot of boilerplate involved in setting up a DynamoDBExtension, and some tests were creating several extensions rather than one with several tables, which is probably slower than it has to be. This change adds a new DynamoDbExtensionSchema class in which we can define the Dynamo schema for tests, and refactors DynamoDbExtension to make it easy to instantiate a single extension with all the tables one wants (and no more, both to minimize test startup time and to ensure we explicitly test our dependencies and lack thereof). Tests requiring a DynamoDbExtension with a table schema that's not part of the normal Signal schema can instantiate a DynamoDbExtension.RawSchema instead. Test timings are unaffected, at least on my machine. Before: ```[INFO] service ............................................ SUCCESS [01:18 min]``` After: ```[INFO] service ............................................ SUCCESS [01:18 min]``` Co-authored-by: Jonathan Klabunde Tomer <jkt@viola.signal.org>
This commit is contained in:
parent
f5726f63bd
commit
48ebafa4e0
|
@ -45,11 +45,17 @@ import software.amazon.awssdk.services.dynamodb.model.WriteRequest;
|
||||||
|
|
||||||
public class MessagesDynamoDb extends AbstractDynamoDbStore {
|
public class MessagesDynamoDb extends AbstractDynamoDbStore {
|
||||||
|
|
||||||
private static final String KEY_PARTITION = "H";
|
@VisibleForTesting
|
||||||
private static final String KEY_SORT = "S";
|
static final String KEY_PARTITION = "H";
|
||||||
|
|
||||||
private static final String LOCAL_INDEX_MESSAGE_UUID_NAME = "Message_UUID_Index";
|
@VisibleForTesting
|
||||||
private static final String LOCAL_INDEX_MESSAGE_UUID_KEY_SORT = "U";
|
static final String KEY_SORT = "S";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String LOCAL_INDEX_MESSAGE_UUID_NAME = "Message_UUID_Index";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String LOCAL_INDEX_MESSAGE_UUID_KEY_SORT = "U";
|
||||||
|
|
||||||
private static final String KEY_TTL = "E";
|
private static final String KEY_TTL = "E";
|
||||||
private static final String KEY_ENVELOPE_BYTES = "EB";
|
private static final String KEY_ENVELOPE_BYTES = "EB";
|
||||||
|
|
|
@ -34,78 +34,28 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
|
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.Projection;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class AccountsManagerChangeNumberIntegrationTest {
|
class AccountsManagerChangeNumberIntegrationTest {
|
||||||
|
|
||||||
private static final String ACCOUNTS_TABLE_NAME = "accounts_test";
|
|
||||||
private static final String NUMBERS_TABLE_NAME = "numbers_test";
|
private static final String NUMBERS_TABLE_NAME = "numbers_test";
|
||||||
private static final String PNI_ASSIGNMENT_TABLE_NAME = "pni_assignment_test";
|
private static final String PNI_ASSIGNMENT_TABLE_NAME = "pni_assignment_test";
|
||||||
private static final String USERNAMES_TABLE_NAME = "usernames_test";
|
private static final String USERNAMES_TABLE_NAME = "usernames_test";
|
||||||
private static final String PNI_TABLE_NAME = "pni_test";
|
|
||||||
private static final String NEEDS_RECONCILIATION_INDEX_NAME = "needs_reconciliation_test";
|
|
||||||
private static final String DELETED_ACCOUNTS_LOCK_TABLE_NAME = "deleted_accounts_lock_test";
|
|
||||||
private static final int SCAN_PAGE_SIZE = 1;
|
private static final int SCAN_PAGE_SIZE = 1;
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension ACCOUNTS_DYNAMO_EXTENSION = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
|
||||||
.tableName(ACCOUNTS_TABLE_NAME)
|
Tables.ACCOUNTS,
|
||||||
.hashKey(Accounts.KEY_ACCOUNT_UUID)
|
Tables.DELETED_ACCOUNTS,
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
Tables.DELETED_ACCOUNTS_LOCK,
|
||||||
.attributeName(Accounts.KEY_ACCOUNT_UUID)
|
Tables.NUMBERS,
|
||||||
.attributeType(ScalarAttributeType.B)
|
Tables.PNI,
|
||||||
.build())
|
Tables.PNI_ASSIGNMENTS,
|
||||||
.build();
|
Tables.USERNAMES);
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension DELETED_ACCOUNTS_DYNAMO_EXTENSION = DynamoDbExtension.builder()
|
static final RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||||
.tableName("deleted_accounts_test")
|
|
||||||
.hashKey(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S).build())
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.ATTR_NEEDS_CDS_RECONCILIATION)
|
|
||||||
.attributeType(ScalarAttributeType.N)
|
|
||||||
.build())
|
|
||||||
.globalSecondaryIndex(GlobalSecondaryIndex.builder()
|
|
||||||
.indexName(NEEDS_RECONCILIATION_INDEX_NAME)
|
|
||||||
.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())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
static DynamoDbExtension DELETED_ACCOUNTS_LOCK_DYNAMO_EXTENSION = DynamoDbExtension.builder()
|
|
||||||
.tableName(DELETED_ACCOUNTS_LOCK_TABLE_NAME)
|
|
||||||
.hashKey(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S).build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
static DynamoDbExtension PNI_DYNAMO_EXTENSION = DynamoDbExtension.builder()
|
|
||||||
.tableName(PNI_TABLE_NAME)
|
|
||||||
.hashKey(PhoneNumberIdentifiers.KEY_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(PhoneNumberIdentifiers.KEY_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
static RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
|
||||||
|
|
||||||
private ClientPresenceManager clientPresenceManager;
|
private ClientPresenceManager clientPresenceManager;
|
||||||
private DeletedAccounts deletedAccounts;
|
private DeletedAccounts deletedAccounts;
|
||||||
|
@ -115,40 +65,6 @@ class AccountsManagerChangeNumberIntegrationTest {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() throws InterruptedException {
|
void setup() throws InterruptedException {
|
||||||
|
|
||||||
{
|
|
||||||
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(NUMBERS_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().createTable(createNumbersTableRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
CreateTableRequest createPhoneNumberIdentifierTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(PNI_ASSIGNMENT_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().createTable(createPhoneNumberIdentifierTableRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked") final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
@SuppressWarnings("unchecked") final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
||||||
mock(DynamicConfigurationManager.class);
|
mock(DynamicConfigurationManager.class);
|
||||||
|
@ -157,21 +73,21 @@ class AccountsManagerChangeNumberIntegrationTest {
|
||||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||||
|
|
||||||
final Accounts accounts = new Accounts(
|
final Accounts accounts = new Accounts(
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getTableName(),
|
Tables.ACCOUNTS.tableName(),
|
||||||
NUMBERS_TABLE_NAME,
|
Tables.NUMBERS.tableName(),
|
||||||
PNI_ASSIGNMENT_TABLE_NAME,
|
Tables.PNI_ASSIGNMENTS.tableName(),
|
||||||
USERNAMES_TABLE_NAME,
|
Tables.USERNAMES.tableName(),
|
||||||
SCAN_PAGE_SIZE);
|
SCAN_PAGE_SIZE);
|
||||||
|
|
||||||
deletedAccounts = new DeletedAccounts(DELETED_ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient(),
|
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
DELETED_ACCOUNTS_DYNAMO_EXTENSION.getTableName(),
|
Tables.DELETED_ACCOUNTS.tableName(),
|
||||||
NEEDS_RECONCILIATION_INDEX_NAME);
|
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
|
||||||
|
|
||||||
final DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
|
final DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
|
||||||
DELETED_ACCOUNTS_LOCK_DYNAMO_EXTENSION.getLegacyDynamoClient(),
|
DYNAMO_DB_EXTENSION.getLegacyDynamoClient(),
|
||||||
DELETED_ACCOUNTS_LOCK_DYNAMO_EXTENSION.getTableName());
|
Tables.DELETED_ACCOUNTS_LOCK.tableName());
|
||||||
|
|
||||||
final SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
final SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
|
||||||
when(secureStorageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null));
|
when(secureStorageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null));
|
||||||
|
@ -185,7 +101,7 @@ class AccountsManagerChangeNumberIntegrationTest {
|
||||||
clientPresenceManager = mock(ClientPresenceManager.class);
|
clientPresenceManager = mock(ClientPresenceManager.class);
|
||||||
|
|
||||||
final PhoneNumberIdentifiers phoneNumberIdentifiers =
|
final PhoneNumberIdentifiers phoneNumberIdentifiers =
|
||||||
new PhoneNumberIdentifiers(PNI_DYNAMO_EXTENSION.getDynamoDbClient(), PNI_TABLE_NAME);
|
new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.PNI.tableName());
|
||||||
|
|
||||||
accountsManager = new AccountsManager(
|
accountsManager = new AccountsManager(
|
||||||
accounts,
|
accounts,
|
||||||
|
|
|
@ -48,34 +48,23 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
|
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
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.DevicesHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.JsonHelpers;
|
import org.whispersystems.textsecuregcm.tests.util.JsonHelpers;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||||
import org.whispersystems.textsecuregcm.util.Pair;
|
import org.whispersystems.textsecuregcm.util.Pair;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class AccountsManagerConcurrentModificationIntegrationTest {
|
class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
|
|
||||||
private static final String ACCOUNTS_TABLE_NAME = "accounts_test";
|
|
||||||
private static final String NUMBERS_TABLE_NAME = "numbers_test";
|
|
||||||
private static final String PNI_TABLE_NAME = "pni_test";
|
|
||||||
private static final String USERNAMES_TABLE_NAME = "usernames_test";
|
|
||||||
|
|
||||||
private static final int SCAN_PAGE_SIZE = 1;
|
private static final int SCAN_PAGE_SIZE = 1;
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
|
||||||
.tableName(ACCOUNTS_TABLE_NAME)
|
Tables.ACCOUNTS,
|
||||||
.hashKey(Accounts.KEY_ACCOUNT_UUID)
|
Tables.NUMBERS,
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
Tables.PNI_ASSIGNMENTS
|
||||||
.attributeName(Accounts.KEY_ACCOUNT_UUID)
|
);
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private Accounts accounts;
|
private Accounts accounts;
|
||||||
|
|
||||||
|
@ -88,51 +77,17 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() throws InterruptedException {
|
void setup() throws InterruptedException {
|
||||||
|
|
||||||
{
|
|
||||||
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(NUMBERS_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
dynamoDbExtension.getDynamoDbClient().createTable(createNumbersTableRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
CreateTableRequest createPhoneNumberIdentifierTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(PNI_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
dynamoDbExtension.getDynamoDbClient().createTable(createPhoneNumberIdentifierTableRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
@SuppressWarnings("unchecked") final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
||||||
mock(DynamicConfigurationManager.class);
|
mock(DynamicConfigurationManager.class);
|
||||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(new DynamicConfiguration());
|
when(dynamicConfigurationManager.getConfiguration()).thenReturn(new DynamicConfiguration());
|
||||||
|
|
||||||
accounts = new Accounts(
|
accounts = new Accounts(
|
||||||
dynamoDbExtension.getDynamoDbClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
dynamoDbExtension.getTableName(),
|
Tables.ACCOUNTS.tableName(),
|
||||||
NUMBERS_TABLE_NAME,
|
Tables.NUMBERS.tableName(),
|
||||||
PNI_TABLE_NAME,
|
Tables.PNI_ASSIGNMENTS.tableName(),
|
||||||
USERNAMES_TABLE_NAME,
|
Tables.USERNAMES.tableName(),
|
||||||
SCAN_PAGE_SIZE);
|
SCAN_PAGE_SIZE);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,23 +45,14 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
|
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
||||||
|
|
||||||
class AccountsManagerUsernameIntegrationTest {
|
class AccountsManagerUsernameIntegrationTest {
|
||||||
|
|
||||||
private static final String ACCOUNTS_TABLE_NAME = "accounts_test";
|
|
||||||
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 String PNI_TABLE_NAME = "pni_test";
|
|
||||||
private static final String BASE_64_URL_USERNAME_HASH_1 = "9p6Tip7BFefFOJzv4kv4GyXEYsBVfk_WbjNejdlOvQE";
|
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_USERNAME_HASH_2 = "NLUom-CHwtemcdvOTTXdmXmzRIV7F05leS8lwkVK_vc";
|
||||||
private static final int SCAN_PAGE_SIZE = 1;
|
private static final int SCAN_PAGE_SIZE = 1;
|
||||||
|
@ -69,24 +60,12 @@ class AccountsManagerUsernameIntegrationTest {
|
||||||
private static final byte[] USERNAME_HASH_2 = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH_2);
|
private static final byte[] USERNAME_HASH_2 = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH_2);
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension ACCOUNTS_DYNAMO_EXTENSION = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
|
||||||
.tableName(ACCOUNTS_TABLE_NAME)
|
Tables.ACCOUNTS,
|
||||||
.hashKey(Accounts.KEY_ACCOUNT_UUID)
|
Tables.NUMBERS,
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
Tables.USERNAMES,
|
||||||
.attributeName(Accounts.KEY_ACCOUNT_UUID)
|
Tables.PNI,
|
||||||
.attributeType(ScalarAttributeType.B)
|
Tables.PNI_ASSIGNMENTS);
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
static DynamoDbExtension PNI_DYNAMO_EXTENSION = DynamoDbExtension.builder()
|
|
||||||
.tableName(PNI_TABLE_NAME)
|
|
||||||
.hashKey(PhoneNumberIdentifiers.KEY_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(PhoneNumberIdentifiers.KEY_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
static RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||||
|
@ -96,48 +75,6 @@ class AccountsManagerUsernameIntegrationTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() throws InterruptedException {
|
void setup() throws InterruptedException {
|
||||||
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(NUMBERS_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().createTable(createNumbersTableRequest);
|
|
||||||
CreateTableRequest createUsernamesTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(USERNAMES_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_USERNAME_HASH)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_USERNAME_HASH)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().createTable(createUsernamesTableRequest);
|
|
||||||
CreateTableRequest createPhoneNumberIdentifierTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(PNI_ASSIGNMENT_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().createTable(createPhoneNumberIdentifierTableRequest);
|
|
||||||
buildAccountsManager(1, 2, 10);
|
buildAccountsManager(1, 2, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,12 +87,12 @@ class AccountsManagerUsernameIntegrationTest {
|
||||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||||
|
|
||||||
accounts = Mockito.spy(new Accounts(
|
accounts = Mockito.spy(new Accounts(
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getTableName(),
|
Tables.ACCOUNTS.tableName(),
|
||||||
NUMBERS_TABLE_NAME,
|
Tables.NUMBERS.tableName(),
|
||||||
PNI_ASSIGNMENT_TABLE_NAME,
|
Tables.PNI_ASSIGNMENTS.tableName(),
|
||||||
USERNAMES_TABLE_NAME,
|
Tables.USERNAMES.tableName(),
|
||||||
SCAN_PAGE_SIZE));
|
SCAN_PAGE_SIZE));
|
||||||
|
|
||||||
final DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.class);
|
final DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.class);
|
||||||
|
@ -167,7 +104,7 @@ class AccountsManagerUsernameIntegrationTest {
|
||||||
}).when(deletedAccountsManager).lockAndTake(any(), any());
|
}).when(deletedAccountsManager).lockAndTake(any(), any());
|
||||||
|
|
||||||
final PhoneNumberIdentifiers phoneNumberIdentifiers =
|
final PhoneNumberIdentifiers phoneNumberIdentifiers =
|
||||||
new PhoneNumberIdentifiers(PNI_DYNAMO_EXTENSION.getDynamoDbClient(), PNI_TABLE_NAME);
|
new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.PNI.tableName());
|
||||||
|
|
||||||
final ExperimentEnrollmentManager experimentEnrollmentManager = mock(ExperimentEnrollmentManager.class);
|
final ExperimentEnrollmentManager experimentEnrollmentManager = mock(ExperimentEnrollmentManager.class);
|
||||||
when(experimentEnrollmentManager.isEnrolled(any(UUID.class), eq(AccountsManager.USERNAME_EXPERIMENT_NAME)))
|
when(experimentEnrollmentManager.isEnrolled(any(UUID.class), eq(AccountsManager.USERNAME_EXPERIMENT_NAME)))
|
||||||
|
@ -207,8 +144,8 @@ class AccountsManagerUsernameIntegrationTest {
|
||||||
AttributeValues.fromLong(Instant.now().plus(Duration.ofMinutes(1)).getEpochSecond()));
|
AttributeValues.fromLong(Instant.now().plus(Duration.ofMinutes(1)).getEpochSecond()));
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
|
DYNAMO_DB_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
|
||||||
.tableName(USERNAMES_TABLE_NAME)
|
.tableName(Tables.USERNAMES.tableName())
|
||||||
.item(item)
|
.item(item)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
@ -222,8 +159,8 @@ class AccountsManagerUsernameIntegrationTest {
|
||||||
new ArrayList<>());
|
new ArrayList<>());
|
||||||
ArrayList<byte[]> usernameHashes = new ArrayList<>(Arrays.asList(USERNAME_HASH_1, USERNAME_HASH_2));
|
ArrayList<byte[]> usernameHashes = new ArrayList<>(Arrays.asList(USERNAME_HASH_1, USERNAME_HASH_2));
|
||||||
for (byte[] hash : usernameHashes) {
|
for (byte[] hash : usernameHashes) {
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
|
DYNAMO_DB_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
|
||||||
.tableName(USERNAMES_TABLE_NAME)
|
.tableName(Tables.USERNAMES.tableName())
|
||||||
.item(Map.of(
|
.item(Map.of(
|
||||||
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(UUID.randomUUID()),
|
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(UUID.randomUUID()),
|
||||||
Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(hash)))
|
Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(hash)))
|
||||||
|
@ -287,8 +224,8 @@ class AccountsManagerUsernameIntegrationTest {
|
||||||
|
|
||||||
long past = Instant.now().minus(Duration.ofMinutes(1)).getEpochSecond();
|
long past = Instant.now().minus(Duration.ofMinutes(1)).getEpochSecond();
|
||||||
// force expiration
|
// force expiration
|
||||||
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().updateItem(UpdateItemRequest.builder()
|
DYNAMO_DB_EXTENSION.getDynamoDbClient().updateItem(UpdateItemRequest.builder()
|
||||||
.tableName(USERNAMES_TABLE_NAME)
|
.tableName(Tables.USERNAMES.tableName())
|
||||||
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
|
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
|
||||||
.updateExpression("SET #ttl = :ttl")
|
.updateExpression("SET #ttl = :ttl")
|
||||||
.expressionAttributeNames(Map.of("#ttl", Accounts.ATTR_TTL))
|
.expressionAttributeNames(Map.of("#ttl", Accounts.ATTR_TTL))
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
|
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
|
||||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||||
|
@ -45,18 +46,13 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.TestClock;
|
import org.whispersystems.textsecuregcm.util.TestClock;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
|
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
|
import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.Put;
|
import software.amazon.awssdk.services.dynamodb.model.Put;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ReturnValuesOnConditionCheckFailure;
|
import software.amazon.awssdk.services.dynamodb.model.ReturnValuesOnConditionCheckFailure;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.TransactWriteItem;
|
import software.amazon.awssdk.services.dynamodb.model.TransactWriteItem;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.TransactWriteItemsRequest;
|
import software.amazon.awssdk.services.dynamodb.model.TransactWriteItemsRequest;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.TransactionCanceledException;
|
import software.amazon.awssdk.services.dynamodb.model.TransactionCanceledException;
|
||||||
|
@ -65,10 +61,6 @@ import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
||||||
|
|
||||||
class AccountsTest {
|
class AccountsTest {
|
||||||
|
|
||||||
private static final String ACCOUNTS_TABLE_NAME = "accounts_test";
|
|
||||||
private static final String NUMBER_CONSTRAINT_TABLE_NAME = "numbers_test";
|
|
||||||
private static final String PNI_CONSTRAINT_TABLE_NAME = "pni_test";
|
|
||||||
private static final String USERNAME_CONSTRAINT_TABLE_NAME = "username_test";
|
|
||||||
private static final String BASE_64_URL_USERNAME_HASH_1 = "9p6Tip7BFefFOJzv4kv4GyXEYsBVfk_WbjNejdlOvQE";
|
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_USERNAME_HASH_2 = "NLUom-CHwtemcdvOTTXdmXmzRIV7F05leS8lwkVK_vc";
|
||||||
private static final byte[] USERNAME_HASH_1 = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH_1);
|
private static final byte[] USERNAME_HASH_1 = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH_1);
|
||||||
|
@ -77,16 +69,12 @@ class AccountsTest {
|
||||||
private static final int SCAN_PAGE_SIZE = 1;
|
private static final int SCAN_PAGE_SIZE = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
|
||||||
.tableName(ACCOUNTS_TABLE_NAME)
|
Tables.ACCOUNTS,
|
||||||
.hashKey(Accounts.KEY_ACCOUNT_UUID)
|
Tables.NUMBERS,
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
Tables.PNI_ASSIGNMENTS,
|
||||||
.attributeName(Accounts.KEY_ACCOUNT_UUID)
|
Tables.USERNAMES);
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private final TestClock clock = TestClock.pinned(Instant.EPOCH);
|
private final TestClock clock = TestClock.pinned(Instant.EPOCH);
|
||||||
private DynamicConfigurationManager<DynamicConfiguration> mockDynamicConfigManager;
|
private DynamicConfigurationManager<DynamicConfiguration> mockDynamicConfigManager;
|
||||||
|
@ -94,50 +82,6 @@ class AccountsTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setupAccountsDao() {
|
void setupAccountsDao() {
|
||||||
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(NUMBER_CONSTRAINT_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
dynamoDbExtension.getDynamoDbClient().createTable(createNumbersTableRequest);
|
|
||||||
|
|
||||||
CreateTableRequest createPhoneNumberIdentifierTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(PNI_CONSTRAINT_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_PNI_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
dynamoDbExtension.getDynamoDbClient().createTable(createPhoneNumberIdentifierTableRequest);
|
|
||||||
|
|
||||||
CreateTableRequest createUsernamesTableRequest = CreateTableRequest.builder()
|
|
||||||
.tableName(USERNAME_CONSTRAINT_TABLE_NAME)
|
|
||||||
.keySchema(KeySchemaElement.builder()
|
|
||||||
.attributeName(Accounts.ATTR_USERNAME_HASH)
|
|
||||||
.keyType(KeyType.HASH)
|
|
||||||
.build())
|
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
|
||||||
.attributeName(Accounts.ATTR_USERNAME_HASH)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
dynamoDbExtension.getDynamoDbClient().createTable(createUsernamesTableRequest);
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") DynamicConfigurationManager<DynamicConfiguration> m = mock(DynamicConfigurationManager.class);
|
@SuppressWarnings("unchecked") DynamicConfigurationManager<DynamicConfiguration> m = mock(DynamicConfigurationManager.class);
|
||||||
mockDynamicConfigManager = m;
|
mockDynamicConfigManager = m;
|
||||||
|
@ -147,12 +91,12 @@ class AccountsTest {
|
||||||
|
|
||||||
this.accounts = new Accounts(
|
this.accounts = new Accounts(
|
||||||
clock,
|
clock,
|
||||||
dynamoDbExtension.getDynamoDbClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
dynamoDbExtension.getTableName(),
|
Tables.ACCOUNTS.tableName(),
|
||||||
NUMBER_CONSTRAINT_TABLE_NAME,
|
Tables.NUMBERS.tableName(),
|
||||||
PNI_CONSTRAINT_TABLE_NAME,
|
Tables.PNI_ASSIGNMENTS.tableName(),
|
||||||
USERNAME_CONSTRAINT_TABLE_NAME,
|
Tables.USERNAMES.tableName(),
|
||||||
SCAN_PAGE_SIZE);
|
SCAN_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +191,7 @@ class AccountsTest {
|
||||||
final TransactWriteItem phoneNumberConstraintPut = TransactWriteItem.builder()
|
final TransactWriteItem phoneNumberConstraintPut = TransactWriteItem.builder()
|
||||||
.put(
|
.put(
|
||||||
Put.builder()
|
Put.builder()
|
||||||
.tableName(NUMBER_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.NUMBERS.tableName())
|
||||||
.item(Map.of(
|
.item(Map.of(
|
||||||
Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(account.getNumber()),
|
Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(account.getNumber()),
|
||||||
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(account.getUuid())))
|
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(account.getUuid())))
|
||||||
|
@ -264,7 +208,7 @@ class AccountsTest {
|
||||||
|
|
||||||
final TransactWriteItem accountPut = TransactWriteItem.builder()
|
final TransactWriteItem accountPut = TransactWriteItem.builder()
|
||||||
.put(Put.builder()
|
.put(Put.builder()
|
||||||
.tableName(ACCOUNTS_TABLE_NAME)
|
.tableName(Tables.ACCOUNTS.tableName())
|
||||||
.conditionExpression("attribute_not_exists(#number) OR #number = :number")
|
.conditionExpression("attribute_not_exists(#number) OR #number = :number")
|
||||||
.expressionAttributeNames(Map.of("#number", Accounts.ATTR_ACCOUNT_E164))
|
.expressionAttributeNames(Map.of("#number", Accounts.ATTR_ACCOUNT_E164))
|
||||||
.expressionAttributeValues(Map.of(":number", AttributeValues.fromString(account.getNumber())))
|
.expressionAttributeValues(Map.of(":number", AttributeValues.fromString(account.getNumber())))
|
||||||
|
@ -277,7 +221,7 @@ class AccountsTest {
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
dynamoDbExtension.getDynamoDbClient().transactWriteItems(TransactWriteItemsRequest.builder()
|
DYNAMO_DB_EXTENSION.getDynamoDbClient().transactWriteItems(TransactWriteItemsRequest.builder()
|
||||||
.transactItems(phoneNumberConstraintPut, accountPut)
|
.transactItems(phoneNumberConstraintPut, accountPut)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
@ -389,8 +333,8 @@ class AccountsTest {
|
||||||
|
|
||||||
final DynamoDbAsyncClient dynamoDbAsyncClient = mock(DynamoDbAsyncClient.class);
|
final DynamoDbAsyncClient dynamoDbAsyncClient = mock(DynamoDbAsyncClient.class);
|
||||||
accounts = new Accounts(mock(DynamoDbClient.class),
|
accounts = new Accounts(mock(DynamoDbClient.class),
|
||||||
dynamoDbAsyncClient, dynamoDbExtension.getTableName(),
|
dynamoDbAsyncClient, Tables.ACCOUNTS.tableName(),
|
||||||
NUMBER_CONSTRAINT_TABLE_NAME, PNI_CONSTRAINT_TABLE_NAME, USERNAME_CONSTRAINT_TABLE_NAME, SCAN_PAGE_SIZE);
|
Tables.NUMBERS.tableName(), Tables.PNI_ASSIGNMENTS.tableName(), Tables.USERNAMES.tableName(), SCAN_PAGE_SIZE);
|
||||||
|
|
||||||
Exception e = TransactionConflictException.builder().build();
|
Exception e = TransactionConflictException.builder().build();
|
||||||
e = wrapException ? new CompletionException(e) : e;
|
e = wrapException ? new CompletionException(e) : e;
|
||||||
|
@ -612,8 +556,8 @@ class AccountsTest {
|
||||||
final UUID existingPhoneNumberIdentifier = UUID.randomUUID();
|
final UUID existingPhoneNumberIdentifier = UUID.randomUUID();
|
||||||
|
|
||||||
// Artificially inject a conflicting PNI entry
|
// Artificially inject a conflicting PNI entry
|
||||||
dynamoDbExtension.getDynamoDbClient().putItem(PutItemRequest.builder()
|
DYNAMO_DB_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
|
||||||
.tableName(PNI_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.PNI_ASSIGNMENTS.tableName())
|
||||||
.item(Map.of(
|
.item(Map.of(
|
||||||
Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(existingPhoneNumberIdentifier),
|
Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(existingPhoneNumberIdentifier),
|
||||||
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(existingAccountIdentifier)))
|
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(existingAccountIdentifier)))
|
||||||
|
@ -649,9 +593,9 @@ class AccountsTest {
|
||||||
accounts.confirmUsernameHash(account, USERNAME_HASH_2);
|
accounts.confirmUsernameHash(account, USERNAME_HASH_2);
|
||||||
|
|
||||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
|
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
|
||||||
assertThat(dynamoDbExtension.getDynamoDbClient()
|
assertThat(DYNAMO_DB_EXTENSION.getDynamoDbClient()
|
||||||
.getItem(GetItemRequest.builder()
|
.getItem(GetItemRequest.builder()
|
||||||
.tableName(USERNAME_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.USERNAMES.tableName())
|
||||||
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
|
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
|
||||||
.build())
|
.build())
|
||||||
.item()).isEmpty();
|
.item()).isEmpty();
|
||||||
|
@ -775,9 +719,9 @@ class AccountsTest {
|
||||||
assertArrayEquals(account1.getUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
assertArrayEquals(account1.getUsernameHash().orElseThrow(), USERNAME_HASH_1);
|
||||||
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).get().getUuid()).isEqualTo(account1.getUuid());
|
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1).get().getUuid()).isEqualTo(account1.getUuid());
|
||||||
|
|
||||||
final Map<String, AttributeValue> usernameConstraintRecord = dynamoDbExtension.getDynamoDbClient()
|
final Map<String, AttributeValue> usernameConstraintRecord = DYNAMO_DB_EXTENSION.getDynamoDbClient()
|
||||||
.getItem(GetItemRequest.builder()
|
.getItem(GetItemRequest.builder()
|
||||||
.tableName(USERNAME_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.USERNAMES.tableName())
|
||||||
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
|
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
|
||||||
.build())
|
.build())
|
||||||
.item();
|
.item();
|
||||||
|
@ -892,9 +836,9 @@ class AccountsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPhoneNumberConstraintExists(final String number, final UUID uuid) {
|
private void assertPhoneNumberConstraintExists(final String number, final UUID uuid) {
|
||||||
final GetItemResponse numberConstraintResponse = dynamoDbExtension.getDynamoDbClient().getItem(
|
final GetItemResponse numberConstraintResponse = DYNAMO_DB_EXTENSION.getDynamoDbClient().getItem(
|
||||||
GetItemRequest.builder()
|
GetItemRequest.builder()
|
||||||
.tableName(NUMBER_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.NUMBERS.tableName())
|
||||||
.key(Map.of(Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(number)))
|
.key(Map.of(Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(number)))
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
@ -903,9 +847,9 @@ class AccountsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPhoneNumberConstraintDoesNotExist(final String number) {
|
private void assertPhoneNumberConstraintDoesNotExist(final String number) {
|
||||||
final GetItemResponse numberConstraintResponse = dynamoDbExtension.getDynamoDbClient().getItem(
|
final GetItemResponse numberConstraintResponse = DYNAMO_DB_EXTENSION.getDynamoDbClient().getItem(
|
||||||
GetItemRequest.builder()
|
GetItemRequest.builder()
|
||||||
.tableName(NUMBER_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.NUMBERS.tableName())
|
||||||
.key(Map.of(Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(number)))
|
.key(Map.of(Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(number)))
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
@ -913,9 +857,9 @@ class AccountsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPhoneNumberIdentifierConstraintExists(final UUID phoneNumberIdentifier, final UUID uuid) {
|
private void assertPhoneNumberIdentifierConstraintExists(final UUID phoneNumberIdentifier, final UUID uuid) {
|
||||||
final GetItemResponse pniConstraintResponse = dynamoDbExtension.getDynamoDbClient().getItem(
|
final GetItemResponse pniConstraintResponse = DYNAMO_DB_EXTENSION.getDynamoDbClient().getItem(
|
||||||
GetItemRequest.builder()
|
GetItemRequest.builder()
|
||||||
.tableName(PNI_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.PNI_ASSIGNMENTS.tableName())
|
||||||
.key(Map.of(Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(phoneNumberIdentifier)))
|
.key(Map.of(Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(phoneNumberIdentifier)))
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
@ -924,9 +868,9 @@ class AccountsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPhoneNumberIdentifierConstraintDoesNotExist(final UUID phoneNumberIdentifier) {
|
private void assertPhoneNumberIdentifierConstraintDoesNotExist(final UUID phoneNumberIdentifier) {
|
||||||
final GetItemResponse pniConstraintResponse = dynamoDbExtension.getDynamoDbClient().getItem(
|
final GetItemResponse pniConstraintResponse = DYNAMO_DB_EXTENSION.getDynamoDbClient().getItem(
|
||||||
GetItemRequest.builder()
|
GetItemRequest.builder()
|
||||||
.tableName(PNI_CONSTRAINT_TABLE_NAME)
|
.tableName(Tables.PNI_ASSIGNMENTS.tableName())
|
||||||
.key(Map.of(Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(phoneNumberIdentifier)))
|
.key(Map.of(Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(phoneNumberIdentifier)))
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
@ -934,10 +878,10 @@ class AccountsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyStoredState(String number, UUID uuid, UUID pni, byte[] usernameHash, Account expecting, boolean canonicallyDiscoverable) {
|
private void verifyStoredState(String number, UUID uuid, UUID pni, byte[] usernameHash, Account expecting, boolean canonicallyDiscoverable) {
|
||||||
final DynamoDbClient db = dynamoDbExtension.getDynamoDbClient();
|
final DynamoDbClient db = DYNAMO_DB_EXTENSION.getDynamoDbClient();
|
||||||
|
|
||||||
final GetItemResponse get = db.getItem(GetItemRequest.builder()
|
final GetItemResponse get = db.getItem(GetItemRequest.builder()
|
||||||
.tableName(dynamoDbExtension.getTableName())
|
.tableName(Tables.ACCOUNTS.tableName())
|
||||||
.key(Map.of(Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)))
|
.key(Map.of(Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)))
|
||||||
.consistentRead(true)
|
.consistentRead(true)
|
||||||
.build());
|
.build());
|
||||||
|
|
|
@ -19,75 +19,27 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.Projection;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class DeletedAccountsManagerTest {
|
class DeletedAccountsManagerTest {
|
||||||
|
|
||||||
private static final String NEEDS_RECONCILIATION_INDEX_NAME = "needs_reconciliation_test";
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static final DynamoDbExtension DELETED_ACCOUNTS_DYNAMODB_EXTENSION = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION =
|
||||||
.tableName("deleted_accounts_test")
|
new DynamoDbExtension(Tables.DELETED_ACCOUNTS, Tables.DELETED_ACCOUNTS_LOCK);
|
||||||
.hashKey(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S).build())
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.ATTR_NEEDS_CDS_RECONCILIATION)
|
|
||||||
.attributeType(ScalarAttributeType.N)
|
|
||||||
.build())
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.globalSecondaryIndex(GlobalSecondaryIndex.builder()
|
|
||||||
.indexName(NEEDS_RECONCILIATION_INDEX_NAME)
|
|
||||||
.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(GlobalSecondaryIndex.builder()
|
|
||||||
.indexName(DeletedAccounts.UUID_TO_E164_INDEX_NAME)
|
|
||||||
.keySchema(
|
|
||||||
KeySchemaElement.builder().attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID).keyType(KeyType.HASH).build()
|
|
||||||
)
|
|
||||||
.projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build())
|
|
||||||
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
static DynamoDbExtension DELETED_ACCOUNTS_LOCK_DYNAMODB_EXTENSION = DynamoDbExtension.builder()
|
|
||||||
.tableName("deleted_accounts_lock_test")
|
|
||||||
.hashKey(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S).build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private DeletedAccounts deletedAccounts;
|
private DeletedAccounts deletedAccounts;
|
||||||
private DeletedAccountsManager deletedAccountsManager;
|
private DeletedAccountsManager deletedAccountsManager;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
deletedAccounts = new DeletedAccounts(DELETED_ACCOUNTS_DYNAMODB_EXTENSION.getDynamoDbClient(),
|
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
DELETED_ACCOUNTS_DYNAMODB_EXTENSION.getTableName(),
|
Tables.DELETED_ACCOUNTS.tableName(),
|
||||||
NEEDS_RECONCILIATION_INDEX_NAME);
|
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
|
||||||
|
|
||||||
deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
|
deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
|
||||||
DELETED_ACCOUNTS_LOCK_DYNAMODB_EXTENSION.getLegacyDynamoClient(),
|
DYNAMO_DB_EXTENSION.getLegacyDynamoClient(),
|
||||||
DELETED_ACCOUNTS_LOCK_DYNAMODB_EXTENSION.getTableName());
|
Tables.DELETED_ACCOUNTS_LOCK.tableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -16,62 +16,22 @@ import java.util.UUID;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
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;
|
import org.whispersystems.textsecuregcm.util.Pair;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.Projection;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class DeletedAccountsTest {
|
class DeletedAccountsTest {
|
||||||
|
|
||||||
private static final String NEEDS_RECONCILIATION_INDEX_NAME = "needs_reconciliation_test";
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.DELETED_ACCOUNTS);
|
||||||
.tableName("deleted_accounts_test")
|
|
||||||
.hashKey(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.KEY_ACCOUNT_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S).build())
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.ATTR_NEEDS_CDS_RECONCILIATION)
|
|
||||||
.attributeType(ScalarAttributeType.N)
|
|
||||||
.build())
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.globalSecondaryIndex(GlobalSecondaryIndex.builder()
|
|
||||||
.indexName(NEEDS_RECONCILIATION_INDEX_NAME)
|
|
||||||
.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(GlobalSecondaryIndex.builder()
|
|
||||||
.indexName(DeletedAccounts.UUID_TO_E164_INDEX_NAME)
|
|
||||||
.keySchema(
|
|
||||||
KeySchemaElement.builder().attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID).keyType(KeyType.HASH).build()
|
|
||||||
)
|
|
||||||
.projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build())
|
|
||||||
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private DeletedAccounts deletedAccounts;
|
private DeletedAccounts deletedAccounts;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
deletedAccounts = new DeletedAccounts(dynamoDbExtension.getDynamoDbClient(),
|
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getTableName(),
|
Tables.DELETED_ACCOUNTS.tableName(),
|
||||||
NEEDS_RECONCILIATION_INDEX_NAME);
|
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021-2023 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
|
||||||
import com.almworks.sqlite4java.SQLite;
|
import com.almworks.sqlite4java.SQLite;
|
||||||
|
@ -31,7 +36,23 @@ import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
||||||
|
|
||||||
public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback {
|
public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback {
|
||||||
|
|
||||||
static final String DEFAULT_TABLE_NAME = "test_table";
|
public interface TableSchema {
|
||||||
|
public String tableName();
|
||||||
|
public String hashKeyName();
|
||||||
|
public String rangeKeyName();
|
||||||
|
public List<AttributeDefinition> attributeDefinitions();
|
||||||
|
public List<GlobalSecondaryIndex> globalSecondaryIndexes();
|
||||||
|
public List<LocalSecondaryIndex> localSecondaryIndexes();
|
||||||
|
}
|
||||||
|
|
||||||
|
record RawSchema(
|
||||||
|
String tableName,
|
||||||
|
String hashKeyName,
|
||||||
|
String rangeKeyName,
|
||||||
|
List<AttributeDefinition> attributeDefinitions,
|
||||||
|
List<GlobalSecondaryIndex> globalSecondaryIndexes,
|
||||||
|
List<LocalSecondaryIndex> localSecondaryIndexes
|
||||||
|
) implements TableSchema { }
|
||||||
|
|
||||||
static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = ProvisionedThroughput.builder()
|
static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = ProvisionedThroughput.builder()
|
||||||
.readCapacityUnits(20L)
|
.readCapacityUnits(20L)
|
||||||
|
@ -42,42 +63,14 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
|
||||||
|
|
||||||
private DynamoDBProxyServer server;
|
private DynamoDBProxyServer server;
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private final String tableName;
|
private final List<TableSchema> schemas;
|
||||||
private final String hashKeyName;
|
|
||||||
private final String rangeKeyName;
|
|
||||||
|
|
||||||
private final List<AttributeDefinition> attributeDefinitions;
|
|
||||||
private final List<GlobalSecondaryIndex> globalSecondaryIndexes;
|
|
||||||
private final List<LocalSecondaryIndex> localSecondaryIndexes;
|
|
||||||
|
|
||||||
private final long readCapacityUnits;
|
|
||||||
private final long writeCapacityUnits;
|
|
||||||
|
|
||||||
private DynamoDbClient dynamoDB2;
|
private DynamoDbClient dynamoDB2;
|
||||||
private DynamoDbAsyncClient dynamoAsyncDB2;
|
private DynamoDbAsyncClient dynamoAsyncDB2;
|
||||||
private AmazonDynamoDB legacyDynamoClient;
|
private AmazonDynamoDB legacyDynamoClient;
|
||||||
|
|
||||||
private DynamoDbExtension(String tableName, String hashKey, String rangeKey,
|
public DynamoDbExtension(TableSchema... schemas) {
|
||||||
List<AttributeDefinition> attributeDefinitions, List<GlobalSecondaryIndex> globalSecondaryIndexes,
|
this.schemas = List.of(schemas);
|
||||||
final List<LocalSecondaryIndex> localSecondaryIndexes,
|
|
||||||
long readCapacityUnits,
|
|
||||||
long writeCapacityUnits) {
|
|
||||||
|
|
||||||
this.tableName = tableName;
|
|
||||||
this.hashKeyName = hashKey;
|
|
||||||
this.rangeKeyName = rangeKey;
|
|
||||||
this.localSecondaryIndexes = localSecondaryIndexes;
|
|
||||||
|
|
||||||
this.readCapacityUnits = readCapacityUnits;
|
|
||||||
this.writeCapacityUnits = writeCapacityUnits;
|
|
||||||
|
|
||||||
this.attributeDefinitions = attributeDefinitions;
|
|
||||||
this.globalSecondaryIndexes = globalSecondaryIndexes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DynamoDbExtensionBuilder builder() {
|
|
||||||
return new DynamoDbExtensionBuilder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadLibrary() {
|
private static void loadLibrary() {
|
||||||
|
@ -114,32 +107,33 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
|
||||||
|
|
||||||
initializeClient();
|
initializeClient();
|
||||||
|
|
||||||
createTable();
|
createTables();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTable() {
|
private void createTables() {
|
||||||
|
schemas.stream().forEach(this::createTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTable(TableSchema schema) {
|
||||||
KeySchemaElement[] keySchemaElements;
|
KeySchemaElement[] keySchemaElements;
|
||||||
if (rangeKeyName == null) {
|
if (schema.rangeKeyName() == null) {
|
||||||
keySchemaElements = new KeySchemaElement[] {
|
keySchemaElements = new KeySchemaElement[] {
|
||||||
KeySchemaElement.builder().attributeName(hashKeyName).keyType(KeyType.HASH).build(),
|
KeySchemaElement.builder().attributeName(schema.hashKeyName()).keyType(KeyType.HASH).build(),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
keySchemaElements = new KeySchemaElement[] {
|
keySchemaElements = new KeySchemaElement[] {
|
||||||
KeySchemaElement.builder().attributeName(hashKeyName).keyType(KeyType.HASH).build(),
|
KeySchemaElement.builder().attributeName(schema.hashKeyName()).keyType(KeyType.HASH).build(),
|
||||||
KeySchemaElement.builder().attributeName(rangeKeyName).keyType(KeyType.RANGE).build(),
|
KeySchemaElement.builder().attributeName(schema.rangeKeyName()).keyType(KeyType.RANGE).build(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
final CreateTableRequest createTableRequest = CreateTableRequest.builder()
|
final CreateTableRequest createTableRequest = CreateTableRequest.builder()
|
||||||
.tableName(tableName)
|
.tableName(schema.tableName())
|
||||||
.keySchema(keySchemaElements)
|
.keySchema(keySchemaElements)
|
||||||
.attributeDefinitions(attributeDefinitions.isEmpty() ? null : attributeDefinitions)
|
.attributeDefinitions(schema.attributeDefinitions().isEmpty() ? null : schema.attributeDefinitions())
|
||||||
.globalSecondaryIndexes(globalSecondaryIndexes.isEmpty() ? null : globalSecondaryIndexes)
|
.globalSecondaryIndexes(schema.globalSecondaryIndexes().isEmpty() ? null : schema.globalSecondaryIndexes())
|
||||||
.localSecondaryIndexes(localSecondaryIndexes.isEmpty() ? null : localSecondaryIndexes)
|
.localSecondaryIndexes(schema.localSecondaryIndexes().isEmpty() ? null : schema.localSecondaryIndexes())
|
||||||
.provisionedThroughput(ProvisionedThroughput.builder()
|
.provisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT)
|
||||||
.readCapacityUnits(readCapacityUnits)
|
|
||||||
.writeCapacityUnits(writeCapacityUnits)
|
|
||||||
.build())
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
getDynamoDbClient().createTable(createTableRequest);
|
getDynamoDbClient().createTable(createTableRequest);
|
||||||
|
@ -178,60 +172,6 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DynamoDbExtensionBuilder {
|
|
||||||
|
|
||||||
private String tableName = DEFAULT_TABLE_NAME;
|
|
||||||
|
|
||||||
private String hashKey;
|
|
||||||
private String rangeKey;
|
|
||||||
|
|
||||||
private final List<AttributeDefinition> attributeDefinitions = new ArrayList<>();
|
|
||||||
private final List<GlobalSecondaryIndex> globalSecondaryIndexes = new ArrayList<>();
|
|
||||||
private final List<LocalSecondaryIndex> localSecondaryIndexes = new ArrayList<>();
|
|
||||||
|
|
||||||
private final long readCapacityUnits = DEFAULT_PROVISIONED_THROUGHPUT.readCapacityUnits();
|
|
||||||
private final long writeCapacityUnits = DEFAULT_PROVISIONED_THROUGHPUT.writeCapacityUnits();
|
|
||||||
|
|
||||||
private DynamoDbExtensionBuilder() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbExtensionBuilder tableName(String databaseName) {
|
|
||||||
this.tableName = databaseName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbExtensionBuilder hashKey(String hashKey) {
|
|
||||||
this.hashKey = hashKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbExtensionBuilder rangeKey(String rangeKey) {
|
|
||||||
this.rangeKey = rangeKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbExtensionBuilder attributeDefinition(AttributeDefinition attributeDefinition) {
|
|
||||||
attributeDefinitions.add(attributeDefinition);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbExtensionBuilder globalSecondaryIndex(GlobalSecondaryIndex index) {
|
|
||||||
globalSecondaryIndexes.add(index);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbExtensionBuilder localSecondaryIndex(LocalSecondaryIndex index) {
|
|
||||||
localSecondaryIndexes.add(index);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbExtension build() {
|
|
||||||
return new DynamoDbExtension(tableName, hashKey, rangeKey,
|
|
||||||
attributeDefinitions, globalSecondaryIndexes, localSecondaryIndexes, readCapacityUnits, writeCapacityUnits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbClient getDynamoDbClient() {
|
public DynamoDbClient getDynamoDbClient() {
|
||||||
return dynamoDB2;
|
return dynamoDB2;
|
||||||
}
|
}
|
||||||
|
@ -243,8 +183,4 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
|
||||||
public AmazonDynamoDB getLegacyDynamoClient() {
|
public AmazonDynamoDB getLegacyDynamoClient() {
|
||||||
return legacyDynamoClient;
|
return legacyDynamoClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTableName() {
|
|
||||||
return tableName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.Projection;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
|
||||||
|
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
||||||
|
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",
|
||||||
|
Accounts.KEY_ACCOUNT_UUID,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(Accounts.KEY_ACCOUNT_UUID)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
DELETED_ACCOUNTS("deleted_accounts_test",
|
||||||
|
DeletedAccounts.KEY_ACCOUNT_E164,
|
||||||
|
null,
|
||||||
|
List.of(
|
||||||
|
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(
|
||||||
|
KeySchemaElement.builder().attributeName(DeletedAccounts.ATTR_ACCOUNT_UUID).keyType(KeyType.HASH).build()
|
||||||
|
)
|
||||||
|
.projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build())
|
||||||
|
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
|
||||||
|
.build()),
|
||||||
|
List.of()
|
||||||
|
),
|
||||||
|
|
||||||
|
DELETED_ACCOUNTS_LOCK("deleted_accounts_lock_test",
|
||||||
|
DeletedAccounts.KEY_ACCOUNT_E164,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(DeletedAccounts.KEY_ACCOUNT_E164)
|
||||||
|
.attributeType(ScalarAttributeType.S).build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
NUMBERS("numbers_test",
|
||||||
|
Accounts.ATTR_ACCOUNT_E164,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
KEYS("keys_test",
|
||||||
|
Keys.KEY_ACCOUNT_UUID,
|
||||||
|
Keys.KEY_DEVICE_ID_KEY_ID,
|
||||||
|
List.of(
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(Keys.KEY_ACCOUNT_UUID)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build(),
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(Keys.KEY_DEVICE_ID_KEY_ID)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
PNI("pni_test",
|
||||||
|
PhoneNumberIdentifiers.KEY_E164,
|
||||||
|
null,
|
||||||
|
List.of(
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(PhoneNumberIdentifiers.KEY_E164)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build(),
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(PhoneNumberIdentifiers.ATTR_PHONE_NUMBER_IDENTIFIER)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(GlobalSecondaryIndex.builder()
|
||||||
|
.indexName(PhoneNumberIdentifiers.INDEX_NAME)
|
||||||
|
.projection(Projection.builder()
|
||||||
|
.projectionType(ProjectionType.KEYS_ONLY)
|
||||||
|
.build())
|
||||||
|
.keySchema(KeySchemaElement.builder().keyType(KeyType.HASH)
|
||||||
|
.attributeName(PhoneNumberIdentifiers.ATTR_PHONE_NUMBER_IDENTIFIER)
|
||||||
|
.build())
|
||||||
|
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
|
||||||
|
.build()),
|
||||||
|
List.of()),
|
||||||
|
|
||||||
|
PNI_ASSIGNMENTS("pni_assignment_test",
|
||||||
|
Accounts.ATTR_PNI_UUID,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(Accounts.ATTR_PNI_UUID)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
ISSUED_RECEIPTS("issued_receipts_test",
|
||||||
|
IssuedReceiptsManager.KEY_PROCESSOR_ITEM_ID,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(IssuedReceiptsManager.KEY_PROCESSOR_ITEM_ID)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
MESSAGES("messages_test",
|
||||||
|
MessagesDynamoDb.KEY_PARTITION,
|
||||||
|
MessagesDynamoDb.KEY_SORT,
|
||||||
|
List.of(
|
||||||
|
AttributeDefinition.builder().attributeName(MessagesDynamoDb.KEY_PARTITION).attributeType(ScalarAttributeType.B).build(),
|
||||||
|
AttributeDefinition.builder().attributeName(MessagesDynamoDb.KEY_SORT).attributeType(ScalarAttributeType.B).build(),
|
||||||
|
AttributeDefinition.builder().attributeName(MessagesDynamoDb.LOCAL_INDEX_MESSAGE_UUID_KEY_SORT)
|
||||||
|
.attributeType(ScalarAttributeType.B).build()),
|
||||||
|
List.of(),
|
||||||
|
List.of(LocalSecondaryIndex.builder()
|
||||||
|
.indexName(MessagesDynamoDb.LOCAL_INDEX_MESSAGE_UUID_NAME)
|
||||||
|
.keySchema(
|
||||||
|
KeySchemaElement.builder().attributeName(MessagesDynamoDb.KEY_PARTITION).keyType(KeyType.HASH).build(),
|
||||||
|
KeySchemaElement.builder()
|
||||||
|
.attributeName(MessagesDynamoDb.LOCAL_INDEX_MESSAGE_UUID_KEY_SORT)
|
||||||
|
.keyType(KeyType.RANGE)
|
||||||
|
.build())
|
||||||
|
.projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build())
|
||||||
|
.build())),
|
||||||
|
|
||||||
|
PROFILES("profiles_test",
|
||||||
|
Profiles.KEY_ACCOUNT_UUID,
|
||||||
|
Profiles.ATTR_VERSION,
|
||||||
|
List.of(
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(Profiles.KEY_ACCOUNT_UUID)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build(),
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(Profiles.ATTR_VERSION)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
PUSH_CHALLENGES("push_challenge_test",
|
||||||
|
PushChallengeDynamoDb.KEY_ACCOUNT_UUID,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(PushChallengeDynamoDb.KEY_ACCOUNT_UUID)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
REDEEMED_RECEIPTS("redeemed_receipts_test",
|
||||||
|
RedeemedReceiptsManager.KEY_SERIAL,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(RedeemedReceiptsManager.KEY_SERIAL)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
REGISTRATION_RECOVERY_PASSWORDS("registration_recovery_passwords_test",
|
||||||
|
RegistrationRecoveryPasswords.KEY_E164,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(RegistrationRecoveryPasswords.KEY_E164)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
REMOTE_CONFIGS("remote_configs_test",
|
||||||
|
RemoteConfigs.KEY_NAME,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(RemoteConfigs.KEY_NAME)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
REPORT_MESSAGES("report_messages_test",
|
||||||
|
ReportMessageDynamoDb.KEY_HASH,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(ReportMessageDynamoDb.KEY_HASH)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
SUBSCRIPTIONS("subscriptions_test",
|
||||||
|
SubscriptionManager.KEY_USER,
|
||||||
|
null,
|
||||||
|
List.of(
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(SubscriptionManager.KEY_USER)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build(),
|
||||||
|
AttributeDefinition.builder()
|
||||||
|
.attributeName(SubscriptionManager.KEY_PROCESSOR_ID_CUSTOMER_ID)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(GlobalSecondaryIndex.builder()
|
||||||
|
.indexName(SubscriptionManager.INDEX_NAME)
|
||||||
|
.keySchema(KeySchemaElement.builder()
|
||||||
|
.attributeName(SubscriptionManager.KEY_PROCESSOR_ID_CUSTOMER_ID)
|
||||||
|
.keyType(KeyType.HASH)
|
||||||
|
.build())
|
||||||
|
.projection(Projection.builder()
|
||||||
|
.projectionType(ProjectionType.KEYS_ONLY)
|
||||||
|
.build())
|
||||||
|
.provisionedThroughput(ProvisionedThroughput.builder()
|
||||||
|
.readCapacityUnits(20L)
|
||||||
|
.writeCapacityUnits(20L)
|
||||||
|
.build())
|
||||||
|
.build()),
|
||||||
|
List.of()),
|
||||||
|
|
||||||
|
USERNAMES("usernames_test",
|
||||||
|
Accounts.ATTR_USERNAME_HASH,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(Accounts.ATTR_USERNAME_HASH)
|
||||||
|
.attributeType(ScalarAttributeType.B)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
VERIFICATION_CODES("verification_codes_test",
|
||||||
|
VerificationCodeStore.KEY_E164,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(VerificationCodeStore.KEY_E164)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of()),
|
||||||
|
|
||||||
|
VERIFICATION_SESSIONS("verification_sessions_test",
|
||||||
|
VerificationSessions.KEY_KEY,
|
||||||
|
null,
|
||||||
|
List.of(AttributeDefinition.builder()
|
||||||
|
.attributeName(VerificationSessions.KEY_KEY)
|
||||||
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(), List.of());
|
||||||
|
|
||||||
|
private final String tableName;
|
||||||
|
private final String hashKeyName;
|
||||||
|
private final String rangeKeyName;
|
||||||
|
private final List<AttributeDefinition> attributeDefinitions;
|
||||||
|
private final List<GlobalSecondaryIndex> globalSecondaryIndexes;
|
||||||
|
private final List<LocalSecondaryIndex> localSecondaryIndexes;
|
||||||
|
|
||||||
|
Tables(
|
||||||
|
final String tableName,
|
||||||
|
final String hashKeyName,
|
||||||
|
final String rangeKeyName,
|
||||||
|
final List<AttributeDefinition> attributeDefinitions,
|
||||||
|
final List<GlobalSecondaryIndex> globalSecondaryIndexes,
|
||||||
|
final List<LocalSecondaryIndex> localSecondaryIndexes
|
||||||
|
) {
|
||||||
|
this.tableName = tableName;
|
||||||
|
this.hashKeyName = hashKeyName;
|
||||||
|
this.rangeKeyName = rangeKeyName;
|
||||||
|
this.attributeDefinitions = attributeDefinitions;
|
||||||
|
this.globalSecondaryIndexes = globalSecondaryIndexes;
|
||||||
|
this.localSecondaryIndexes = localSecondaryIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String tableName() {
|
||||||
|
return tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String hashKeyName() {
|
||||||
|
return hashKeyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String rangeKeyName() {
|
||||||
|
return rangeKeyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AttributeDefinition> attributeDefinitions() {
|
||||||
|
return attributeDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GlobalSecondaryIndex> globalSecondaryIndexes() {
|
||||||
|
return globalSecondaryIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<LocalSecondaryIndex> localSecondaryIndexes() {
|
||||||
|
return localSecondaryIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,24 +20,15 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest;
|
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest;
|
||||||
import org.whispersystems.textsecuregcm.subscriptions.SubscriptionProcessor;
|
import org.whispersystems.textsecuregcm.subscriptions.SubscriptionProcessor;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class IssuedReceiptsManagerTest {
|
class IssuedReceiptsManagerTest {
|
||||||
|
|
||||||
private static final long NOW_EPOCH_SECONDS = 1_500_000_000L;
|
private static final long NOW_EPOCH_SECONDS = 1_500_000_000L;
|
||||||
private static final String ISSUED_RECEIPTS_TABLE_NAME = "issued_receipts";
|
|
||||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.ISSUED_RECEIPTS);
|
||||||
.tableName(ISSUED_RECEIPTS_TABLE_NAME)
|
|
||||||
.hashKey(IssuedReceiptsManager.KEY_PROCESSOR_ITEM_ID)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(IssuedReceiptsManager.KEY_PROCESSOR_ITEM_ID)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
ReceiptCredentialRequest receiptCredentialRequest;
|
ReceiptCredentialRequest receiptCredentialRequest;
|
||||||
IssuedReceiptsManager issuedReceiptsManager;
|
IssuedReceiptsManager issuedReceiptsManager;
|
||||||
|
@ -48,9 +39,9 @@ class IssuedReceiptsManagerTest {
|
||||||
byte[] generator = new byte[16];
|
byte[] generator = new byte[16];
|
||||||
SECURE_RANDOM.nextBytes(generator);
|
SECURE_RANDOM.nextBytes(generator);
|
||||||
issuedReceiptsManager = new IssuedReceiptsManager(
|
issuedReceiptsManager = new IssuedReceiptsManager(
|
||||||
ISSUED_RECEIPTS_TABLE_NAME,
|
Tables.ISSUED_RECEIPTS.tableName(),
|
||||||
Duration.ofDays(90),
|
Duration.ofDays(90),
|
||||||
dynamoDbExtension.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
generator);
|
generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,38 +15,22 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.entities.PreKey;
|
import org.whispersystems.textsecuregcm.entities.PreKey;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class KeysTest {
|
class KeysTest {
|
||||||
|
|
||||||
private static final String TABLE_NAME = "Signal_Keys_Test";
|
|
||||||
|
|
||||||
private Keys keys;
|
private Keys keys;
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.KEYS);
|
||||||
.tableName(TABLE_NAME)
|
|
||||||
.hashKey(Keys.KEY_ACCOUNT_UUID)
|
|
||||||
.rangeKey(Keys.KEY_DEVICE_ID_KEY_ID)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(Keys.KEY_ACCOUNT_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.attributeDefinition(
|
|
||||||
AttributeDefinition.builder()
|
|
||||||
.attributeName(Keys.KEY_DEVICE_ID_KEY_ID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final UUID ACCOUNT_UUID = UUID.randomUUID();
|
private static final UUID ACCOUNT_UUID = UUID.randomUUID();
|
||||||
private static final long DEVICE_ID = 1L;
|
private static final long DEVICE_ID = 1L;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
keys = new Keys(dynamoDbExtension.getDynamoDbClient(), TABLE_NAME);
|
keys = new Keys(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.KEYS.tableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -35,7 +35,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||||
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbExtension;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
||||||
|
@ -44,7 +44,7 @@ import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
|
||||||
class MessagePersisterIntegrationTest {
|
class MessagePersisterIntegrationTest {
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build();
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.MESSAGES);
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||||
|
@ -74,8 +74,8 @@ class MessagePersisterIntegrationTest {
|
||||||
|
|
||||||
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
|
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
|
||||||
messageDeletionExecutorService = Executors.newSingleThreadExecutor();
|
messageDeletionExecutorService = Executors.newSingleThreadExecutor();
|
||||||
final MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(),
|
final MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getDynamoDbAsyncClient(), MessagesDynamoDbExtension.TABLE_NAME, Duration.ofDays(14),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.MESSAGES.tableName(), Duration.ofDays(14),
|
||||||
messageDeletionExecutorService);
|
messageDeletionExecutorService);
|
||||||
final AccountsManager accountsManager = mock(AccountsManager.class);
|
final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
|
|
||||||
|
@ -164,10 +164,10 @@ class MessagePersisterIntegrationTest {
|
||||||
|
|
||||||
messagePersister.stop();
|
messagePersister.stop();
|
||||||
|
|
||||||
DynamoDbClient dynamoDB = dynamoDbExtension.getDynamoDbClient();
|
DynamoDbClient dynamoDB = DYNAMO_DB_EXTENSION.getDynamoDbClient();
|
||||||
|
|
||||||
final List<MessageProtos.Envelope> persistedMessages =
|
final List<MessageProtos.Envelope> persistedMessages =
|
||||||
dynamoDB.scan(ScanRequest.builder().tableName(MessagesDynamoDbExtension.TABLE_NAME).build()).items().stream()
|
dynamoDB.scan(ScanRequest.builder().tableName(Tables.MESSAGES.tableName()).build()).items().stream()
|
||||||
.map(item -> {
|
.map(item -> {
|
||||||
try {
|
try {
|
||||||
return MessagesDynamoDb.convertItemToEnvelope(item);
|
return MessagesDynamoDb.convertItemToEnvelope(item);
|
||||||
|
|
|
@ -26,8 +26,8 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.MessageHelper;
|
import org.whispersystems.textsecuregcm.tests.util.MessageHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbExtension;
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
|
@ -77,13 +77,13 @@ class MessagesDynamoDbTest {
|
||||||
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build();
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.MESSAGES);
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
messageDeletionExecutorService = Executors.newSingleThreadExecutor();
|
messageDeletionExecutorService = Executors.newSingleThreadExecutor();
|
||||||
messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(),
|
messagesDynamoDb = new MessagesDynamoDb(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getDynamoDbAsyncClient(), MessagesDynamoDbExtension.TABLE_NAME, Duration.ofDays(14),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.MESSAGES.tableName(), Duration.ofDays(14),
|
||||||
messageDeletionExecutorService);
|
messageDeletionExecutorService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ class MessagesDynamoDbTest {
|
||||||
.thenRequest(halfOfMessageLoadLimit)
|
.thenRequest(halfOfMessageLoadLimit)
|
||||||
.expectNextCount(halfOfMessageLoadLimit)
|
.expectNextCount(halfOfMessageLoadLimit)
|
||||||
// the first 100 should be fetched and buffered, but further requests should fail
|
// the first 100 should be fetched and buffered, but further requests should fail
|
||||||
.then(() -> dynamoDbExtension.stopServer())
|
.then(() -> DYNAMO_DB_EXTENSION.stopServer())
|
||||||
.thenRequest(halfOfMessageLoadLimit)
|
.thenRequest(halfOfMessageLoadLimit)
|
||||||
.expectNextCount(halfOfMessageLoadLimit)
|
.expectNextCount(halfOfMessageLoadLimit)
|
||||||
// we’ve consumed all the buffered messages, so a single request will fail
|
// we’ve consumed all the buffered messages, so a single request will fail
|
||||||
|
|
|
@ -14,48 +14,19 @@ import java.util.UUID;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.Projection;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class PhoneNumberIdentifiersTest {
|
class PhoneNumberIdentifiersTest {
|
||||||
|
|
||||||
private static final String PNI_TABLE_NAME = "pni_test";
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
|
static DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.PNI);
|
||||||
.tableName(PNI_TABLE_NAME)
|
|
||||||
.hashKey(PhoneNumberIdentifiers.KEY_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(PhoneNumberIdentifiers.KEY_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(PhoneNumberIdentifiers.ATTR_PHONE_NUMBER_IDENTIFIER)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.globalSecondaryIndex(GlobalSecondaryIndex.builder()
|
|
||||||
.indexName(PhoneNumberIdentifiers.INDEX_NAME)
|
|
||||||
.projection(Projection.builder()
|
|
||||||
.projectionType(ProjectionType.KEYS_ONLY)
|
|
||||||
.build())
|
|
||||||
.keySchema(KeySchemaElement.builder().keyType(KeyType.HASH)
|
|
||||||
.attributeName(PhoneNumberIdentifiers.ATTR_PHONE_NUMBER_IDENTIFIER)
|
|
||||||
.build())
|
|
||||||
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private PhoneNumberIdentifiers phoneNumberIdentifiers;
|
private PhoneNumberIdentifiers phoneNumberIdentifiers;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
phoneNumberIdentifiers = new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), PNI_TABLE_NAME);
|
phoneNumberIdentifiers = new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
|
Tables.PNI.tableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -12,9 +12,8 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -27,30 +26,16 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
public abstract class ProfilesTest {
|
public abstract class ProfilesTest {
|
||||||
|
|
||||||
private static final String PROFILES_TABLE_NAME = "profiles_test";
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.PROFILES);
|
||||||
.tableName(PROFILES_TABLE_NAME)
|
|
||||||
.hashKey(Profiles.KEY_ACCOUNT_UUID)
|
|
||||||
.rangeKey(Profiles.ATTR_VERSION)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(Profiles.KEY_ACCOUNT_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(Profiles.ATTR_VERSION)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private Profiles profiles;
|
private Profiles profiles;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
profiles = new Profiles(dynamoDbExtension.getDynamoDbClient(),
|
profiles = new Profiles(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
PROFILES_TABLE_NAME);
|
Tables.PROFILES.tableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -18,8 +18,7 @@ import java.util.UUID;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class PushChallengeDynamoDbTest {
|
class PushChallengeDynamoDbTest {
|
||||||
|
|
||||||
|
@ -28,22 +27,16 @@ class PushChallengeDynamoDbTest {
|
||||||
private static final long CURRENT_TIME_MILLIS = 1_000_000_000;
|
private static final long CURRENT_TIME_MILLIS = 1_000_000_000;
|
||||||
|
|
||||||
private static final Random RANDOM = new Random();
|
private static final Random RANDOM = new Random();
|
||||||
private static final String TABLE_NAME = "push_challenge_test";
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.PUSH_CHALLENGES);
|
||||||
.tableName(TABLE_NAME)
|
|
||||||
.hashKey(PushChallengeDynamoDb.KEY_ACCOUNT_UUID)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(PushChallengeDynamoDb.KEY_ACCOUNT_UUID)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
this.pushChallengeDynamoDb = new PushChallengeDynamoDb(dynamoDbExtension.getDynamoDbClient(), TABLE_NAME, Clock.fixed(
|
this.pushChallengeDynamoDb = new PushChallengeDynamoDb(
|
||||||
Instant.ofEpochMilli(CURRENT_TIME_MILLIS), ZoneId.systemDefault()));
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
|
Tables.PUSH_CHALLENGES.tableName(),
|
||||||
|
Clock.fixed(Instant.ofEpochMilli(CURRENT_TIME_MILLIS), ZoneId.systemDefault()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -21,17 +21,14 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.util.MockUtils;
|
import org.whispersystems.textsecuregcm.util.MockUtils;
|
||||||
import org.whispersystems.textsecuregcm.util.MutableClock;
|
import org.whispersystems.textsecuregcm.util.MutableClock;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
public class RegistrationRecoveryTest {
|
public class RegistrationRecoveryTest {
|
||||||
|
|
||||||
private static final String TABLE_NAME = "registration_recovery_passwords";
|
|
||||||
|
|
||||||
private static final MutableClock CLOCK = MockUtils.mutableClock(0);
|
private static final MutableClock CLOCK = MockUtils.mutableClock(0);
|
||||||
|
|
||||||
private static final Duration EXPIRATION = Duration.ofSeconds(1000);
|
private static final Duration EXPIRATION = Duration.ofSeconds(1000);
|
||||||
|
@ -43,14 +40,8 @@ public class RegistrationRecoveryTest {
|
||||||
private static final SaltedTokenHash ANOTHER_HASH = SaltedTokenHash.generateFor("pass2");
|
private static final SaltedTokenHash ANOTHER_HASH = SaltedTokenHash.generateFor("pass2");
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
private static final DynamoDbExtension DB_EXTENSION = DynamoDbExtension.builder()
|
private static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
|
||||||
.tableName(TABLE_NAME)
|
Tables.REGISTRATION_RECOVERY_PASSWORDS);
|
||||||
.hashKey(RegistrationRecoveryPasswords.KEY_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(RegistrationRecoveryPasswords.KEY_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private RegistrationRecoveryPasswords store;
|
private RegistrationRecoveryPasswords store;
|
||||||
|
|
||||||
|
@ -60,10 +51,10 @@ public class RegistrationRecoveryTest {
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
CLOCK.setTimeMillis(Clock.systemUTC().millis());
|
CLOCK.setTimeMillis(Clock.systemUTC().millis());
|
||||||
store = new RegistrationRecoveryPasswords(
|
store = new RegistrationRecoveryPasswords(
|
||||||
DB_EXTENSION.getTableName(),
|
Tables.REGISTRATION_RECOVERY_PASSWORDS.tableName(),
|
||||||
EXPIRATION,
|
EXPIRATION,
|
||||||
DB_EXTENSION.getDynamoDbClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
DB_EXTENSION.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
CLOCK
|
CLOCK
|
||||||
);
|
);
|
||||||
manager = new RegistrationRecoveryPasswordsManager(store);
|
manager = new RegistrationRecoveryPasswordsManager(store);
|
||||||
|
@ -147,8 +138,8 @@ public class RegistrationRecoveryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long fetchTimestamp(final String number) throws ExecutionException, InterruptedException {
|
private static long fetchTimestamp(final String number) throws ExecutionException, InterruptedException {
|
||||||
return DB_EXTENSION.getDynamoDbAsyncClient().getItem(GetItemRequest.builder()
|
return DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient().getItem(GetItemRequest.builder()
|
||||||
.tableName(DB_EXTENSION.getTableName())
|
.tableName(Tables.REGISTRATION_RECOVERY_PASSWORDS.tableName())
|
||||||
.key(Map.of(RegistrationRecoveryPasswords.KEY_E164, AttributeValues.fromString(number)))
|
.key(Map.of(RegistrationRecoveryPasswords.KEY_E164, AttributeValues.fromString(number)))
|
||||||
.build())
|
.build())
|
||||||
.thenApply(getItemResponse -> {
|
.thenApply(getItemResponse -> {
|
||||||
|
|
|
@ -9,8 +9,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -18,23 +17,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class RemoteConfigsTest {
|
class RemoteConfigsTest {
|
||||||
|
|
||||||
private static final String REMOTE_CONFIGS_TABLE_NAME = "remote_configs_test";
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.REMOTE_CONFIGS);
|
||||||
.tableName(REMOTE_CONFIGS_TABLE_NAME)
|
|
||||||
.hashKey(RemoteConfigs.KEY_NAME)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(RemoteConfigs.KEY_NAME)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private RemoteConfigs remoteConfigs;
|
private RemoteConfigs remoteConfigs;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
remoteConfigs = new RemoteConfigs(dynamoDbExtension.getDynamoDbClient(), REMOTE_CONFIGS_TABLE_NAME);
|
remoteConfigs = new RemoteConfigs(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.REMOTE_CONFIGS.tableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -14,30 +14,23 @@ import java.util.UUID;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
import org.whispersystems.textsecuregcm.util.UUIDUtil;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class ReportMessageDynamoDbTest {
|
class ReportMessageDynamoDbTest {
|
||||||
|
|
||||||
private ReportMessageDynamoDb reportMessageDynamoDb;
|
private ReportMessageDynamoDb reportMessageDynamoDb;
|
||||||
|
|
||||||
private static final String TABLE_NAME = "report_message_test";
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.REPORT_MESSAGES);
|
||||||
.tableName(TABLE_NAME)
|
|
||||||
.hashKey(ReportMessageDynamoDb.KEY_HASH)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(ReportMessageDynamoDb.KEY_HASH)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
this.reportMessageDynamoDb = new ReportMessageDynamoDb(dynamoDbExtension.getDynamoDbClient(), TABLE_NAME, Duration.ofDays(1));
|
this.reportMessageDynamoDb = new ReportMessageDynamoDb(
|
||||||
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
|
Tables.REPORT_MESSAGES.tableName(),
|
||||||
|
Duration.ofDays(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -39,14 +40,17 @@ class SerializedExpireableJsonDynamoStoreTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
|
||||||
.tableName(TABLE_NAME)
|
new DynamoDbExtension.RawSchema(
|
||||||
.hashKey(SerializedExpireableJsonDynamoStore.KEY_KEY)
|
TABLE_NAME,
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
SerializedExpireableJsonDynamoStore.KEY_KEY,
|
||||||
.attributeName(SerializedExpireableJsonDynamoStore.KEY_KEY)
|
null,
|
||||||
.attributeType(ScalarAttributeType.S)
|
List.of(AttributeDefinition.builder()
|
||||||
.build())
|
.attributeName(SerializedExpireableJsonDynamoStore.KEY_KEY)
|
||||||
.build();
|
.attributeType(ScalarAttributeType.S)
|
||||||
|
.build()),
|
||||||
|
List.of(),
|
||||||
|
List.of()));
|
||||||
|
|
||||||
private SerializedExpireableJsonDynamoStore<T> store;
|
private SerializedExpireableJsonDynamoStore<T> store;
|
||||||
|
|
||||||
|
|
|
@ -28,49 +28,16 @@ import org.whispersystems.textsecuregcm.storage.SubscriptionManager.GetResult;
|
||||||
import org.whispersystems.textsecuregcm.storage.SubscriptionManager.Record;
|
import org.whispersystems.textsecuregcm.storage.SubscriptionManager.Record;
|
||||||
import org.whispersystems.textsecuregcm.subscriptions.ProcessorCustomer;
|
import org.whispersystems.textsecuregcm.subscriptions.ProcessorCustomer;
|
||||||
import org.whispersystems.textsecuregcm.subscriptions.SubscriptionProcessor;
|
import org.whispersystems.textsecuregcm.subscriptions.SubscriptionProcessor;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.Projection;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class SubscriptionManagerTest {
|
class SubscriptionManagerTest {
|
||||||
|
|
||||||
private static final long NOW_EPOCH_SECONDS = 1_500_000_000L;
|
private static final long NOW_EPOCH_SECONDS = 1_500_000_000L;
|
||||||
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(3);
|
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(3);
|
||||||
private static final String SUBSCRIPTIONS_TABLE_NAME = "subscriptions";
|
|
||||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder().
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.SUBSCRIPTIONS);
|
||||||
tableName(SUBSCRIPTIONS_TABLE_NAME).
|
|
||||||
hashKey(SubscriptionManager.KEY_USER).
|
|
||||||
attributeDefinition(AttributeDefinition.builder().
|
|
||||||
attributeName(SubscriptionManager.KEY_USER).
|
|
||||||
attributeType(ScalarAttributeType.B).
|
|
||||||
build()).
|
|
||||||
attributeDefinition(AttributeDefinition.builder().
|
|
||||||
attributeName(SubscriptionManager.KEY_PROCESSOR_ID_CUSTOMER_ID).
|
|
||||||
attributeType(ScalarAttributeType.B).
|
|
||||||
build()).
|
|
||||||
globalSecondaryIndex(GlobalSecondaryIndex.builder().
|
|
||||||
indexName(SubscriptionManager.INDEX_NAME).
|
|
||||||
keySchema(KeySchemaElement.builder().
|
|
||||||
attributeName(SubscriptionManager.KEY_PROCESSOR_ID_CUSTOMER_ID).
|
|
||||||
keyType(KeyType.HASH).
|
|
||||||
build()).
|
|
||||||
projection(Projection.builder().
|
|
||||||
projectionType(ProjectionType.KEYS_ONLY).
|
|
||||||
build()).
|
|
||||||
provisionedThroughput(ProvisionedThroughput.builder().
|
|
||||||
readCapacityUnits(20L).
|
|
||||||
writeCapacityUnits(20L).
|
|
||||||
build()).
|
|
||||||
build()).
|
|
||||||
build();
|
|
||||||
|
|
||||||
byte[] user;
|
byte[] user;
|
||||||
byte[] password;
|
byte[] password;
|
||||||
|
@ -85,7 +52,7 @@ class SubscriptionManagerTest {
|
||||||
customer = Base64.getEncoder().encodeToString(getRandomBytes(16));
|
customer = Base64.getEncoder().encodeToString(getRandomBytes(16));
|
||||||
created = Instant.ofEpochSecond(NOW_EPOCH_SECONDS);
|
created = Instant.ofEpochSecond(NOW_EPOCH_SECONDS);
|
||||||
subscriptionManager = new SubscriptionManager(
|
subscriptionManager = new SubscriptionManager(
|
||||||
SUBSCRIPTIONS_TABLE_NAME, dynamoDbExtension.getDynamoDbAsyncClient());
|
Tables.SUBSCRIPTIONS.tableName(), DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -19,15 +19,12 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class VerificationCodeStoreTest {
|
class VerificationCodeStoreTest {
|
||||||
|
|
||||||
private VerificationCodeStore verificationCodeStore;
|
private VerificationCodeStore verificationCodeStore;
|
||||||
|
|
||||||
private static final String TABLE_NAME = "verification_code_test";
|
|
||||||
|
|
||||||
private static final String PHONE_NUMBER = "+14151112222";
|
private static final String PHONE_NUMBER = "+14151112222";
|
||||||
|
|
||||||
private static final long VALID_TIMESTAMP = Instant.now().toEpochMilli();
|
private static final long VALID_TIMESTAMP = Instant.now().toEpochMilli();
|
||||||
|
@ -35,18 +32,12 @@ class VerificationCodeStoreTest {
|
||||||
Duration.ofHours(1)).toEpochMilli();
|
Duration.ofHours(1)).toEpochMilli();
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.VERIFICATION_CODES);
|
||||||
.tableName(TABLE_NAME)
|
|
||||||
.hashKey(VerificationCodeStore.KEY_E164)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(VerificationCodeStore.KEY_E164)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
verificationCodeStore = new VerificationCodeStore(DYNAMO_DB_EXTENSION.getDynamoDbClient(), TABLE_NAME);
|
verificationCodeStore = new VerificationCodeStore(
|
||||||
|
DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.VERIFICATION_CODES.tableName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -21,32 +21,23 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.whispersystems.textsecuregcm.registration.VerificationSession;
|
import org.whispersystems.textsecuregcm.registration.VerificationSession;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
|
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class VerificationSessionsTest {
|
class VerificationSessionsTest {
|
||||||
|
|
||||||
private static final String TABLE_NAME = "verification_sessions_test";
|
|
||||||
|
|
||||||
private static final Clock clock = Clock.systemUTC();
|
private static final Clock clock = Clock.systemUTC();
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.VERIFICATION_SESSIONS);
|
||||||
.tableName(TABLE_NAME)
|
|
||||||
.hashKey(VerificationSessions.KEY_KEY)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(VerificationSessions.KEY_KEY)
|
|
||||||
.attributeType(ScalarAttributeType.S)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private VerificationSessions verificationSessions;
|
private VerificationSessions verificationSessions;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
verificationSessions = new VerificationSessions(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), TABLE_NAME, clock);
|
verificationSessions = new VerificationSessions(
|
||||||
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.VERIFICATION_SESSIONS.tableName(), clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -21,27 +21,18 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||||
import org.signal.libsignal.zkgroup.receipts.ReceiptSerial;
|
import org.signal.libsignal.zkgroup.receipts.ReceiptSerial;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager;
|
import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
import org.whispersystems.textsecuregcm.util.TestClock;
|
import org.whispersystems.textsecuregcm.util.TestClock;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
class RedeemedReceiptsManagerTest {
|
class RedeemedReceiptsManagerTest {
|
||||||
|
|
||||||
private static final long NOW_EPOCH_SECONDS = 1_500_000_000L;
|
private static final long NOW_EPOCH_SECONDS = 1_500_000_000L;
|
||||||
private static final String REDEEMED_RECEIPTS_TABLE_NAME = "redeemed_receipts";
|
|
||||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.REDEEMED_RECEIPTS);
|
||||||
.tableName(REDEEMED_RECEIPTS_TABLE_NAME)
|
|
||||||
.hashKey(RedeemedReceiptsManager.KEY_SERIAL)
|
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
|
||||||
.attributeName(RedeemedReceiptsManager.KEY_SERIAL)
|
|
||||||
.attributeType(ScalarAttributeType.B)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Clock clock = TestClock.pinned(Instant.ofEpochSecond(NOW_EPOCH_SECONDS));
|
Clock clock = TestClock.pinned(Instant.ofEpochSecond(NOW_EPOCH_SECONDS));
|
||||||
ReceiptSerial receiptSerial;
|
ReceiptSerial receiptSerial;
|
||||||
|
@ -53,7 +44,10 @@ class RedeemedReceiptsManagerTest {
|
||||||
SECURE_RANDOM.nextBytes(receiptSerialBytes);
|
SECURE_RANDOM.nextBytes(receiptSerialBytes);
|
||||||
receiptSerial = new ReceiptSerial(receiptSerialBytes);
|
receiptSerial = new ReceiptSerial(receiptSerialBytes);
|
||||||
redeemedReceiptsManager = new RedeemedReceiptsManager(
|
redeemedReceiptsManager = new RedeemedReceiptsManager(
|
||||||
clock, REDEEMED_RECEIPTS_TABLE_NAME, dynamoDbExtension.getDynamoDbAsyncClient(), Duration.ofDays(90));
|
clock,
|
||||||
|
Tables.REDEEMED_RECEIPTS.tableName(),
|
||||||
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
|
Duration.ofDays(90));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Signal Messenger, LLC
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.whispersystems.textsecuregcm.tests.util;
|
|
||||||
|
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.KeyType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.Projection;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
|
|
||||||
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
|
||||||
|
|
||||||
public class MessagesDynamoDbExtension {
|
|
||||||
|
|
||||||
public static final String TABLE_NAME = "Signal_Messages_UnitTest";
|
|
||||||
|
|
||||||
public static DynamoDbExtension build() {
|
|
||||||
return DynamoDbExtension.builder()
|
|
||||||
.tableName(TABLE_NAME)
|
|
||||||
.hashKey("H")
|
|
||||||
.rangeKey("S")
|
|
||||||
.attributeDefinition(
|
|
||||||
AttributeDefinition.builder().attributeName("H").attributeType(ScalarAttributeType.B).build())
|
|
||||||
.attributeDefinition(
|
|
||||||
AttributeDefinition.builder().attributeName("S").attributeType(ScalarAttributeType.B).build())
|
|
||||||
.attributeDefinition(
|
|
||||||
AttributeDefinition.builder().attributeName("U").attributeType(ScalarAttributeType.B).build())
|
|
||||||
.localSecondaryIndex(LocalSecondaryIndex.builder().indexName("Message_UUID_Index")
|
|
||||||
.keySchema(KeySchemaElement.builder().attributeName("H").keyType(KeyType.HASH).build(),
|
|
||||||
KeySchemaElement.builder().attributeName("U").keyType(KeyType.RANGE).build())
|
|
||||||
.projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build())
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -51,11 +51,11 @@ import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesCache;
|
import org.whispersystems.textsecuregcm.storage.MessagesCache;
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb;
|
||||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbExtension;
|
|
||||||
import org.whispersystems.textsecuregcm.util.Pair;
|
import org.whispersystems.textsecuregcm.util.Pair;
|
||||||
import org.whispersystems.websocket.WebSocketClient;
|
import org.whispersystems.websocket.WebSocketClient;
|
||||||
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
|
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
|
||||||
|
@ -65,7 +65,7 @@ import reactor.core.scheduler.Schedulers;
|
||||||
class WebSocketConnectionIntegrationTest {
|
class WebSocketConnectionIntegrationTest {
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build();
|
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.MESSAGES);
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||||
|
@ -90,8 +90,8 @@ class WebSocketConnectionIntegrationTest {
|
||||||
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
|
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
|
||||||
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||||
REDIS_CLUSTER_EXTENSION.getRedisCluster(), sharedExecutorService, messageDeliveryScheduler, sharedExecutorService, Clock.systemUTC());
|
REDIS_CLUSTER_EXTENSION.getRedisCluster(), sharedExecutorService, messageDeliveryScheduler, sharedExecutorService, Clock.systemUTC());
|
||||||
messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(),
|
messagesDynamoDb = new MessagesDynamoDb(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getDynamoDbAsyncClient(), MessagesDynamoDbExtension.TABLE_NAME, Duration.ofDays(7),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.MESSAGES.tableName(), Duration.ofDays(7),
|
||||||
sharedExecutorService);
|
sharedExecutorService);
|
||||||
reportMessageManager = mock(ReportMessageManager.class);
|
reportMessageManager = mock(ReportMessageManager.class);
|
||||||
account = mock(Account.class);
|
account = mock(Account.class);
|
||||||
|
|
Loading…
Reference in New Issue