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:
Jonathan Klabunde Tomer 2023-04-03 13:08:43 -07:00 committed by GitHub
parent f5726f63bd
commit 48ebafa4e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 574 additions and 827 deletions

View File

@ -45,11 +45,17 @@ import software.amazon.awssdk.services.dynamodb.model.WriteRequest;
public class MessagesDynamoDb extends AbstractDynamoDbStore {
private static final String KEY_PARTITION = "H";
private static final String KEY_SORT = "S";
@VisibleForTesting
static final String KEY_PARTITION = "H";
private static final String LOCAL_INDEX_MESSAGE_UUID_NAME = "Message_UUID_Index";
private static final String LOCAL_INDEX_MESSAGE_UUID_KEY_SORT = "U";
@VisibleForTesting
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_ENVELOPE_BYTES = "EB";

View File

@ -34,78 +34,28 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
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;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class AccountsManagerChangeNumberIntegrationTest {
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 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;
@RegisterExtension
static DynamoDbExtension ACCOUNTS_DYNAMO_EXTENSION = DynamoDbExtension.builder()
.tableName(ACCOUNTS_TABLE_NAME)
.hashKey(Accounts.KEY_ACCOUNT_UUID)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(Accounts.KEY_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
Tables.ACCOUNTS,
Tables.DELETED_ACCOUNTS,
Tables.DELETED_ACCOUNTS_LOCK,
Tables.NUMBERS,
Tables.PNI,
Tables.PNI_ASSIGNMENTS,
Tables.USERNAMES);
@RegisterExtension
static DynamoDbExtension DELETED_ACCOUNTS_DYNAMO_EXTENSION = DynamoDbExtension.builder()
.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();
static final RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
private ClientPresenceManager clientPresenceManager;
private DeletedAccounts deletedAccounts;
@ -115,40 +65,6 @@ class AccountsManagerChangeNumberIntegrationTest {
@BeforeEach
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 =
mock(DynamicConfigurationManager.class);
@ -157,21 +73,21 @@ class AccountsManagerChangeNumberIntegrationTest {
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
final Accounts accounts = new Accounts(
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient(),
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbAsyncClient(),
ACCOUNTS_DYNAMO_EXTENSION.getTableName(),
NUMBERS_TABLE_NAME,
PNI_ASSIGNMENT_TABLE_NAME,
USERNAMES_TABLE_NAME,
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
Tables.ACCOUNTS.tableName(),
Tables.NUMBERS.tableName(),
Tables.PNI_ASSIGNMENTS.tableName(),
Tables.USERNAMES.tableName(),
SCAN_PAGE_SIZE);
deletedAccounts = new DeletedAccounts(DELETED_ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient(),
DELETED_ACCOUNTS_DYNAMO_EXTENSION.getTableName(),
NEEDS_RECONCILIATION_INDEX_NAME);
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.DELETED_ACCOUNTS.tableName(),
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
final DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
DELETED_ACCOUNTS_LOCK_DYNAMO_EXTENSION.getLegacyDynamoClient(),
DELETED_ACCOUNTS_LOCK_DYNAMO_EXTENSION.getTableName());
DYNAMO_DB_EXTENSION.getLegacyDynamoClient(),
Tables.DELETED_ACCOUNTS_LOCK.tableName());
final SecureStorageClient secureStorageClient = mock(SecureStorageClient.class);
when(secureStorageClient.deleteStoredData(any())).thenReturn(CompletableFuture.completedFuture(null));
@ -185,7 +101,7 @@ class AccountsManagerChangeNumberIntegrationTest {
clientPresenceManager = mock(ClientPresenceManager.class);
final PhoneNumberIdentifiers phoneNumberIdentifiers =
new PhoneNumberIdentifiers(PNI_DYNAMO_EXTENSION.getDynamoDbClient(), PNI_TABLE_NAME);
new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.PNI.tableName());
accountsManager = new AccountsManager(
accounts,

View File

@ -48,34 +48,23 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
import org.whispersystems.textsecuregcm.tests.util.JsonHelpers;
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
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 {
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;
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.tableName(ACCOUNTS_TABLE_NAME)
.hashKey(Accounts.KEY_ACCOUNT_UUID)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(Accounts.KEY_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
Tables.ACCOUNTS,
Tables.NUMBERS,
Tables.PNI_ASSIGNMENTS
);
private Accounts accounts;
@ -88,51 +77,17 @@ class AccountsManagerConcurrentModificationIntegrationTest {
@BeforeEach
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 =
mock(DynamicConfigurationManager.class);
when(dynamicConfigurationManager.getConfiguration()).thenReturn(new DynamicConfiguration());
accounts = new Accounts(
dynamoDbExtension.getDynamoDbClient(),
dynamoDbExtension.getDynamoDbAsyncClient(),
dynamoDbExtension.getTableName(),
NUMBERS_TABLE_NAME,
PNI_TABLE_NAME,
USERNAMES_TABLE_NAME,
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
Tables.ACCOUNTS.tableName(),
Tables.NUMBERS.tableName(),
Tables.PNI_ASSIGNMENTS.tableName(),
Tables.USERNAMES.tableName(),
SCAN_PAGE_SIZE);
{

View File

@ -45,23 +45,14 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.util.AttributeValues;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
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.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
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_2 = "NLUom-CHwtemcdvOTTXdmXmzRIV7F05leS8lwkVK_vc";
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);
@RegisterExtension
static DynamoDbExtension ACCOUNTS_DYNAMO_EXTENSION = DynamoDbExtension.builder()
.tableName(ACCOUNTS_TABLE_NAME)
.hashKey(Accounts.KEY_ACCOUNT_UUID)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(Accounts.KEY_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.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();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
Tables.ACCOUNTS,
Tables.NUMBERS,
Tables.USERNAMES,
Tables.PNI,
Tables.PNI_ASSIGNMENTS);
@RegisterExtension
static RedisClusterExtension CACHE_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
@ -96,48 +75,6 @@ class AccountsManagerUsernameIntegrationTest {
@BeforeEach
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);
}
@ -150,12 +87,12 @@ class AccountsManagerUsernameIntegrationTest {
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
accounts = Mockito.spy(new Accounts(
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient(),
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbAsyncClient(),
ACCOUNTS_DYNAMO_EXTENSION.getTableName(),
NUMBERS_TABLE_NAME,
PNI_ASSIGNMENT_TABLE_NAME,
USERNAMES_TABLE_NAME,
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
Tables.ACCOUNTS.tableName(),
Tables.NUMBERS.tableName(),
Tables.PNI_ASSIGNMENTS.tableName(),
Tables.USERNAMES.tableName(),
SCAN_PAGE_SIZE));
final DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.class);
@ -167,7 +104,7 @@ class AccountsManagerUsernameIntegrationTest {
}).when(deletedAccountsManager).lockAndTake(any(), any());
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);
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()));
}
i++;
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
.tableName(USERNAMES_TABLE_NAME)
DYNAMO_DB_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
.tableName(Tables.USERNAMES.tableName())
.item(item)
.build());
}
@ -222,8 +159,8 @@ class AccountsManagerUsernameIntegrationTest {
new ArrayList<>());
ArrayList<byte[]> usernameHashes = new ArrayList<>(Arrays.asList(USERNAME_HASH_1, USERNAME_HASH_2));
for (byte[] hash : usernameHashes) {
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
.tableName(USERNAMES_TABLE_NAME)
DYNAMO_DB_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
.tableName(Tables.USERNAMES.tableName())
.item(Map.of(
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(UUID.randomUUID()),
Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(hash)))
@ -287,8 +224,8 @@ class AccountsManagerUsernameIntegrationTest {
long past = Instant.now().minus(Duration.ofMinutes(1)).getEpochSecond();
// force expiration
ACCOUNTS_DYNAMO_EXTENSION.getDynamoDbClient().updateItem(UpdateItemRequest.builder()
.tableName(USERNAMES_TABLE_NAME)
DYNAMO_DB_EXTENSION.getDynamoDbClient().updateItem(UpdateItemRequest.builder()
.tableName(Tables.USERNAMES.tableName())
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
.updateExpression("SET #ttl = :ttl")
.expressionAttributeNames(Map.of("#ttl", Accounts.ATTR_TTL))

View File

@ -38,6 +38,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
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.DevicesHelper;
import org.whispersystems.textsecuregcm.util.AttributeValues;
@ -45,18 +46,13 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.TestClock;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
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.ConditionalCheckFailedException;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
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.PutItemRequest;
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.TransactWriteItemsRequest;
import software.amazon.awssdk.services.dynamodb.model.TransactionCanceledException;
@ -65,10 +61,6 @@ import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
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_2 = "NLUom-CHwtemcdvOTTXdmXmzRIV7F05leS8lwkVK_vc";
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;
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.tableName(ACCOUNTS_TABLE_NAME)
.hashKey(Accounts.KEY_ACCOUNT_UUID)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(Accounts.KEY_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
Tables.ACCOUNTS,
Tables.NUMBERS,
Tables.PNI_ASSIGNMENTS,
Tables.USERNAMES);
private final TestClock clock = TestClock.pinned(Instant.EPOCH);
private DynamicConfigurationManager<DynamicConfiguration> mockDynamicConfigManager;
@ -94,50 +82,6 @@ class AccountsTest {
@BeforeEach
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);
mockDynamicConfigManager = m;
@ -147,12 +91,12 @@ class AccountsTest {
this.accounts = new Accounts(
clock,
dynamoDbExtension.getDynamoDbClient(),
dynamoDbExtension.getDynamoDbAsyncClient(),
dynamoDbExtension.getTableName(),
NUMBER_CONSTRAINT_TABLE_NAME,
PNI_CONSTRAINT_TABLE_NAME,
USERNAME_CONSTRAINT_TABLE_NAME,
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
Tables.ACCOUNTS.tableName(),
Tables.NUMBERS.tableName(),
Tables.PNI_ASSIGNMENTS.tableName(),
Tables.USERNAMES.tableName(),
SCAN_PAGE_SIZE);
}
@ -247,7 +191,7 @@ class AccountsTest {
final TransactWriteItem phoneNumberConstraintPut = TransactWriteItem.builder()
.put(
Put.builder()
.tableName(NUMBER_CONSTRAINT_TABLE_NAME)
.tableName(Tables.NUMBERS.tableName())
.item(Map.of(
Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(account.getNumber()),
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(account.getUuid())))
@ -264,7 +208,7 @@ class AccountsTest {
final TransactWriteItem accountPut = TransactWriteItem.builder()
.put(Put.builder()
.tableName(ACCOUNTS_TABLE_NAME)
.tableName(Tables.ACCOUNTS.tableName())
.conditionExpression("attribute_not_exists(#number) OR #number = :number")
.expressionAttributeNames(Map.of("#number", Accounts.ATTR_ACCOUNT_E164))
.expressionAttributeValues(Map.of(":number", AttributeValues.fromString(account.getNumber())))
@ -277,7 +221,7 @@ class AccountsTest {
.build())
.build();
dynamoDbExtension.getDynamoDbClient().transactWriteItems(TransactWriteItemsRequest.builder()
DYNAMO_DB_EXTENSION.getDynamoDbClient().transactWriteItems(TransactWriteItemsRequest.builder()
.transactItems(phoneNumberConstraintPut, accountPut)
.build());
}
@ -389,8 +333,8 @@ class AccountsTest {
final DynamoDbAsyncClient dynamoDbAsyncClient = mock(DynamoDbAsyncClient.class);
accounts = new Accounts(mock(DynamoDbClient.class),
dynamoDbAsyncClient, dynamoDbExtension.getTableName(),
NUMBER_CONSTRAINT_TABLE_NAME, PNI_CONSTRAINT_TABLE_NAME, USERNAME_CONSTRAINT_TABLE_NAME, SCAN_PAGE_SIZE);
dynamoDbAsyncClient, Tables.ACCOUNTS.tableName(),
Tables.NUMBERS.tableName(), Tables.PNI_ASSIGNMENTS.tableName(), Tables.USERNAMES.tableName(), SCAN_PAGE_SIZE);
Exception e = TransactionConflictException.builder().build();
e = wrapException ? new CompletionException(e) : e;
@ -612,8 +556,8 @@ class AccountsTest {
final UUID existingPhoneNumberIdentifier = UUID.randomUUID();
// Artificially inject a conflicting PNI entry
dynamoDbExtension.getDynamoDbClient().putItem(PutItemRequest.builder()
.tableName(PNI_CONSTRAINT_TABLE_NAME)
DYNAMO_DB_EXTENSION.getDynamoDbClient().putItem(PutItemRequest.builder()
.tableName(Tables.PNI_ASSIGNMENTS.tableName())
.item(Map.of(
Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(existingPhoneNumberIdentifier),
Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(existingAccountIdentifier)))
@ -649,9 +593,9 @@ class AccountsTest {
accounts.confirmUsernameHash(account, USERNAME_HASH_2);
assertThat(accounts.getByUsernameHash(USERNAME_HASH_1)).isEmpty();
assertThat(dynamoDbExtension.getDynamoDbClient()
assertThat(DYNAMO_DB_EXTENSION.getDynamoDbClient()
.getItem(GetItemRequest.builder()
.tableName(USERNAME_CONSTRAINT_TABLE_NAME)
.tableName(Tables.USERNAMES.tableName())
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
.build())
.item()).isEmpty();
@ -775,9 +719,9 @@ class AccountsTest {
assertArrayEquals(account1.getUsernameHash().orElseThrow(), USERNAME_HASH_1);
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()
.tableName(USERNAME_CONSTRAINT_TABLE_NAME)
.tableName(Tables.USERNAMES.tableName())
.key(Map.of(Accounts.ATTR_USERNAME_HASH, AttributeValues.fromByteArray(USERNAME_HASH_1)))
.build())
.item();
@ -892,9 +836,9 @@ class AccountsTest {
}
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()
.tableName(NUMBER_CONSTRAINT_TABLE_NAME)
.tableName(Tables.NUMBERS.tableName())
.key(Map.of(Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(number)))
.build());
@ -903,9 +847,9 @@ class AccountsTest {
}
private void assertPhoneNumberConstraintDoesNotExist(final String number) {
final GetItemResponse numberConstraintResponse = dynamoDbExtension.getDynamoDbClient().getItem(
final GetItemResponse numberConstraintResponse = DYNAMO_DB_EXTENSION.getDynamoDbClient().getItem(
GetItemRequest.builder()
.tableName(NUMBER_CONSTRAINT_TABLE_NAME)
.tableName(Tables.NUMBERS.tableName())
.key(Map.of(Accounts.ATTR_ACCOUNT_E164, AttributeValues.fromString(number)))
.build());
@ -913,9 +857,9 @@ class AccountsTest {
}
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()
.tableName(PNI_CONSTRAINT_TABLE_NAME)
.tableName(Tables.PNI_ASSIGNMENTS.tableName())
.key(Map.of(Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(phoneNumberIdentifier)))
.build());
@ -924,9 +868,9 @@ class AccountsTest {
}
private void assertPhoneNumberIdentifierConstraintDoesNotExist(final UUID phoneNumberIdentifier) {
final GetItemResponse pniConstraintResponse = dynamoDbExtension.getDynamoDbClient().getItem(
final GetItemResponse pniConstraintResponse = DYNAMO_DB_EXTENSION.getDynamoDbClient().getItem(
GetItemRequest.builder()
.tableName(PNI_CONSTRAINT_TABLE_NAME)
.tableName(Tables.PNI_ASSIGNMENTS.tableName())
.key(Map.of(Accounts.ATTR_PNI_UUID, AttributeValues.fromUUID(phoneNumberIdentifier)))
.build());
@ -934,10 +878,10 @@ class AccountsTest {
}
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()
.tableName(dynamoDbExtension.getTableName())
.tableName(Tables.ACCOUNTS.tableName())
.key(Map.of(Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)))
.consistentRead(true)
.build());

View File

@ -19,75 +19,27 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.function.Executable;
import 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;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class DeletedAccountsManagerTest {
private static final String NEEDS_RECONCILIATION_INDEX_NAME = "needs_reconciliation_test";
@RegisterExtension
static final DynamoDbExtension DELETED_ACCOUNTS_DYNAMODB_EXTENSION = DynamoDbExtension.builder()
.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();
@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();
static final DynamoDbExtension DYNAMO_DB_EXTENSION =
new DynamoDbExtension(Tables.DELETED_ACCOUNTS, Tables.DELETED_ACCOUNTS_LOCK);
private DeletedAccounts deletedAccounts;
private DeletedAccountsManager deletedAccountsManager;
@BeforeEach
void setUp() {
deletedAccounts = new DeletedAccounts(DELETED_ACCOUNTS_DYNAMODB_EXTENSION.getDynamoDbClient(),
DELETED_ACCOUNTS_DYNAMODB_EXTENSION.getTableName(),
NEEDS_RECONCILIATION_INDEX_NAME);
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.DELETED_ACCOUNTS.tableName(),
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
DELETED_ACCOUNTS_LOCK_DYNAMODB_EXTENSION.getLegacyDynamoClient(),
DELETED_ACCOUNTS_LOCK_DYNAMODB_EXTENSION.getTableName());
DYNAMO_DB_EXTENSION.getLegacyDynamoClient(),
Tables.DELETED_ACCOUNTS_LOCK.tableName());
}
@Test

View File

@ -16,62 +16,22 @@ import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Indexes;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.util.Pair;
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 {
private static final String NEEDS_RECONCILIATION_INDEX_NAME = "needs_reconciliation_test";
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.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();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.DELETED_ACCOUNTS);
private DeletedAccounts deletedAccounts;
@BeforeEach
void setUp() {
deletedAccounts = new DeletedAccounts(dynamoDbExtension.getDynamoDbClient(),
dynamoDbExtension.getTableName(),
NEEDS_RECONCILIATION_INDEX_NAME);
deletedAccounts = new DeletedAccounts(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.DELETED_ACCOUNTS.tableName(),
Indexes.DELETED_ACCOUNTS_NEEDS_RECONCILIATION.indexName());
}
@Test

View File

@ -1,3 +1,8 @@
/*
* Copyright 2021-2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.storage;
import com.almworks.sqlite4java.SQLite;
@ -31,7 +36,23 @@ import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
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()
.readCapacityUnits(20L)
@ -42,42 +63,14 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
private DynamoDBProxyServer server;
private int port;
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;
private final long readCapacityUnits;
private final long writeCapacityUnits;
private final List<TableSchema> schemas;
private DynamoDbClient dynamoDB2;
private DynamoDbAsyncClient dynamoAsyncDB2;
private AmazonDynamoDB legacyDynamoClient;
private DynamoDbExtension(String tableName, String hashKey, String rangeKey,
List<AttributeDefinition> attributeDefinitions, List<GlobalSecondaryIndex> globalSecondaryIndexes,
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();
public DynamoDbExtension(TableSchema... schemas) {
this.schemas = List.of(schemas);
}
private static void loadLibrary() {
@ -114,32 +107,33 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
initializeClient();
createTable();
createTables();
}
private void createTable() {
private void createTables() {
schemas.stream().forEach(this::createTable);
}
private void createTable(TableSchema schema) {
KeySchemaElement[] keySchemaElements;
if (rangeKeyName == null) {
if (schema.rangeKeyName() == null) {
keySchemaElements = new KeySchemaElement[] {
KeySchemaElement.builder().attributeName(hashKeyName).keyType(KeyType.HASH).build(),
KeySchemaElement.builder().attributeName(schema.hashKeyName()).keyType(KeyType.HASH).build(),
};
} else {
keySchemaElements = new KeySchemaElement[] {
KeySchemaElement.builder().attributeName(hashKeyName).keyType(KeyType.HASH).build(),
KeySchemaElement.builder().attributeName(rangeKeyName).keyType(KeyType.RANGE).build(),
KeySchemaElement.builder().attributeName(schema.hashKeyName()).keyType(KeyType.HASH).build(),
KeySchemaElement.builder().attributeName(schema.rangeKeyName()).keyType(KeyType.RANGE).build(),
};
}
final CreateTableRequest createTableRequest = CreateTableRequest.builder()
.tableName(tableName)
.tableName(schema.tableName())
.keySchema(keySchemaElements)
.attributeDefinitions(attributeDefinitions.isEmpty() ? null : attributeDefinitions)
.globalSecondaryIndexes(globalSecondaryIndexes.isEmpty() ? null : globalSecondaryIndexes)
.localSecondaryIndexes(localSecondaryIndexes.isEmpty() ? null : localSecondaryIndexes)
.provisionedThroughput(ProvisionedThroughput.builder()
.readCapacityUnits(readCapacityUnits)
.writeCapacityUnits(writeCapacityUnits)
.build())
.attributeDefinitions(schema.attributeDefinitions().isEmpty() ? null : schema.attributeDefinitions())
.globalSecondaryIndexes(schema.globalSecondaryIndexes().isEmpty() ? null : schema.globalSecondaryIndexes())
.localSecondaryIndexes(schema.localSecondaryIndexes().isEmpty() ? null : schema.localSecondaryIndexes())
.provisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT)
.build();
getDynamoDbClient().createTable(createTableRequest);
@ -178,60 +172,6 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
.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() {
return dynamoDB2;
}
@ -243,8 +183,4 @@ public class DynamoDbExtension implements BeforeEachCallback, AfterEachCallback
public AmazonDynamoDB getLegacyDynamoClient() {
return legacyDynamoClient;
}
public String getTableName() {
return tableName;
}
}

View File

@ -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;
}
}
}

View File

@ -20,24 +20,15 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequest;
import org.whispersystems.textsecuregcm.subscriptions.SubscriptionProcessor;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class IssuedReceiptsManagerTest {
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();
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.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();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.ISSUED_RECEIPTS);
ReceiptCredentialRequest receiptCredentialRequest;
IssuedReceiptsManager issuedReceiptsManager;
@ -48,9 +39,9 @@ class IssuedReceiptsManagerTest {
byte[] generator = new byte[16];
SECURE_RANDOM.nextBytes(generator);
issuedReceiptsManager = new IssuedReceiptsManager(
ISSUED_RECEIPTS_TABLE_NAME,
Tables.ISSUED_RECEIPTS.tableName(),
Duration.ofDays(90),
dynamoDbExtension.getDynamoDbAsyncClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
generator);
}

View File

@ -15,38 +15,22 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
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.ScalarAttributeType;
class KeysTest {
private static final String TABLE_NAME = "Signal_Keys_Test";
private Keys keys;
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.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();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.KEYS);
private static final UUID ACCOUNT_UUID = UUID.randomUUID();
private static final long DEVICE_ID = 1L;
@BeforeEach
void setup() {
keys = new Keys(dynamoDbExtension.getDynamoDbClient(), TABLE_NAME);
keys = new Keys(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.KEYS.tableName());
}
@Test

View File

@ -35,7 +35,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.entities.MessageProtos;
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.Schedulers;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
@ -44,7 +44,7 @@ import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
class MessagePersisterIntegrationTest {
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.MESSAGES);
@RegisterExtension
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
@ -74,8 +74,8 @@ class MessagePersisterIntegrationTest {
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
messageDeletionExecutorService = Executors.newSingleThreadExecutor();
final MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(),
dynamoDbExtension.getDynamoDbAsyncClient(), MessagesDynamoDbExtension.TABLE_NAME, Duration.ofDays(14),
final MessagesDynamoDb messagesDynamoDb = new MessagesDynamoDb(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.MESSAGES.tableName(), Duration.ofDays(14),
messageDeletionExecutorService);
final AccountsManager accountsManager = mock(AccountsManager.class);
@ -164,10 +164,10 @@ class MessagePersisterIntegrationTest {
messagePersister.stop();
DynamoDbClient dynamoDB = dynamoDbExtension.getDynamoDbClient();
DynamoDbClient dynamoDB = DYNAMO_DB_EXTENSION.getDynamoDbClient();
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 -> {
try {
return MessagesDynamoDb.convertItemToEnvelope(item);

View File

@ -26,8 +26,8 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.reactivestreams.Publisher;
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.MessagesDynamoDbExtension;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
@ -77,13 +77,13 @@ class MessagesDynamoDbTest {
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.MESSAGES);
@BeforeEach
void setup() {
messageDeletionExecutorService = Executors.newSingleThreadExecutor();
messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(),
dynamoDbExtension.getDynamoDbAsyncClient(), MessagesDynamoDbExtension.TABLE_NAME, Duration.ofDays(14),
messagesDynamoDb = new MessagesDynamoDb(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.MESSAGES.tableName(), Duration.ofDays(14),
messageDeletionExecutorService);
}
@ -170,7 +170,7 @@ class MessagesDynamoDbTest {
.thenRequest(halfOfMessageLoadLimit)
.expectNextCount(halfOfMessageLoadLimit)
// the first 100 should be fetched and buffered, but further requests should fail
.then(() -> dynamoDbExtension.stopServer())
.then(() -> DYNAMO_DB_EXTENSION.stopServer())
.thenRequest(halfOfMessageLoadLimit)
.expectNextCount(halfOfMessageLoadLimit)
// weve consumed all the buffered messages, so a single request will fail

View File

@ -14,48 +14,19 @@ import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
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;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class PhoneNumberIdentifiersTest {
private static final String PNI_TABLE_NAME = "pni_test";
@RegisterExtension
static DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
.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();
static DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.PNI);
private PhoneNumberIdentifiers phoneNumberIdentifiers;
@BeforeEach
void setUp() {
phoneNumberIdentifiers = new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(), PNI_TABLE_NAME);
phoneNumberIdentifiers = new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.PNI.tableName());
}
@Test

View File

@ -12,9 +12,8 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
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.ScalarAttributeType;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -27,30 +26,16 @@ import java.util.stream.Stream;
public abstract class ProfilesTest {
private static final String PROFILES_TABLE_NAME = "profiles_test";
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.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();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.PROFILES);
private Profiles profiles;
@BeforeEach
void setUp() {
profiles = new Profiles(dynamoDbExtension.getDynamoDbClient(),
dynamoDbExtension.getDynamoDbAsyncClient(),
PROFILES_TABLE_NAME);
profiles = new Profiles(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
Tables.PROFILES.tableName());
}
@Test

View File

@ -18,8 +18,7 @@ import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class PushChallengeDynamoDbTest {
@ -28,22 +27,16 @@ class PushChallengeDynamoDbTest {
private static final long CURRENT_TIME_MILLIS = 1_000_000_000;
private static final Random RANDOM = new Random();
private static final String TABLE_NAME = "push_challenge_test";
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.tableName(TABLE_NAME)
.hashKey(PushChallengeDynamoDb.KEY_ACCOUNT_UUID)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(PushChallengeDynamoDb.KEY_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.PUSH_CHALLENGES);
@BeforeEach
void setUp() {
this.pushChallengeDynamoDb = new PushChallengeDynamoDb(dynamoDbExtension.getDynamoDbClient(), TABLE_NAME, Clock.fixed(
Instant.ofEpochMilli(CURRENT_TIME_MILLIS), ZoneId.systemDefault()));
this.pushChallengeDynamoDb = new PushChallengeDynamoDb(
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
Tables.PUSH_CHALLENGES.tableName(),
Clock.fixed(Instant.ofEpochMilli(CURRENT_TIME_MILLIS), ZoneId.systemDefault()));
}
@Test

View File

@ -21,17 +21,14 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.util.AttributeValues;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.util.MockUtils;
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.GetItemRequest;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
public class RegistrationRecoveryTest {
private static final String TABLE_NAME = "registration_recovery_passwords";
private static final MutableClock CLOCK = MockUtils.mutableClock(0);
private static final Duration EXPIRATION = Duration.ofSeconds(1000);
@ -43,14 +40,8 @@ public class RegistrationRecoveryTest {
private static final SaltedTokenHash ANOTHER_HASH = SaltedTokenHash.generateFor("pass2");
@RegisterExtension
private static final DynamoDbExtension DB_EXTENSION = DynamoDbExtension.builder()
.tableName(TABLE_NAME)
.hashKey(RegistrationRecoveryPasswords.KEY_E164)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(RegistrationRecoveryPasswords.KEY_E164)
.attributeType(ScalarAttributeType.S)
.build())
.build();
private static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
Tables.REGISTRATION_RECOVERY_PASSWORDS);
private RegistrationRecoveryPasswords store;
@ -60,10 +51,10 @@ public class RegistrationRecoveryTest {
public void before() throws Exception {
CLOCK.setTimeMillis(Clock.systemUTC().millis());
store = new RegistrationRecoveryPasswords(
DB_EXTENSION.getTableName(),
Tables.REGISTRATION_RECOVERY_PASSWORDS.tableName(),
EXPIRATION,
DB_EXTENSION.getDynamoDbClient(),
DB_EXTENSION.getDynamoDbAsyncClient(),
DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
CLOCK
);
manager = new RegistrationRecoveryPasswordsManager(store);
@ -147,8 +138,8 @@ public class RegistrationRecoveryTest {
}
private static long fetchTimestamp(final String number) throws ExecutionException, InterruptedException {
return DB_EXTENSION.getDynamoDbAsyncClient().getItem(GetItemRequest.builder()
.tableName(DB_EXTENSION.getTableName())
return DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient().getItem(GetItemRequest.builder()
.tableName(Tables.REGISTRATION_RECOVERY_PASSWORDS.tableName())
.key(Map.of(RegistrationRecoveryPasswords.KEY_E164, AttributeValues.fromString(number)))
.build())
.thenApply(getItemResponse -> {

View File

@ -9,8 +9,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import java.util.List;
import java.util.Set;
@ -18,23 +17,14 @@ import static org.assertj.core.api.Assertions.assertThat;
class RemoteConfigsTest {
private static final String REMOTE_CONFIGS_TABLE_NAME = "remote_configs_test";
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.tableName(REMOTE_CONFIGS_TABLE_NAME)
.hashKey(RemoteConfigs.KEY_NAME)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(RemoteConfigs.KEY_NAME)
.attributeType(ScalarAttributeType.S)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.REMOTE_CONFIGS);
private RemoteConfigs remoteConfigs;
@BeforeEach
void setUp() {
remoteConfigs = new RemoteConfigs(dynamoDbExtension.getDynamoDbClient(), REMOTE_CONFIGS_TABLE_NAME);
remoteConfigs = new RemoteConfigs(DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.REMOTE_CONFIGS.tableName());
}
@Test

View File

@ -14,30 +14,23 @@ import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.util.UUIDUtil;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
class ReportMessageDynamoDbTest {
private ReportMessageDynamoDb reportMessageDynamoDb;
private static final String TABLE_NAME = "report_message_test";
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.tableName(TABLE_NAME)
.hashKey(ReportMessageDynamoDb.KEY_HASH)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(ReportMessageDynamoDb.KEY_HASH)
.attributeType(ScalarAttributeType.B)
.build())
.build();
static DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.REPORT_MESSAGES);
@BeforeEach
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

View File

@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
@ -39,14 +40,17 @@ class SerializedExpireableJsonDynamoStoreTest {
}
@RegisterExtension
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
.tableName(TABLE_NAME)
.hashKey(SerializedExpireableJsonDynamoStore.KEY_KEY)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(SerializedExpireableJsonDynamoStore.KEY_KEY)
.attributeType(ScalarAttributeType.S)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
new DynamoDbExtension.RawSchema(
TABLE_NAME,
SerializedExpireableJsonDynamoStore.KEY_KEY,
null,
List.of(AttributeDefinition.builder()
.attributeName(SerializedExpireableJsonDynamoStore.KEY_KEY)
.attributeType(ScalarAttributeType.S)
.build()),
List.of(),
List.of()));
private SerializedExpireableJsonDynamoStore<T> store;

View File

@ -28,49 +28,16 @@ import org.whispersystems.textsecuregcm.storage.SubscriptionManager.GetResult;
import org.whispersystems.textsecuregcm.storage.SubscriptionManager.Record;
import org.whispersystems.textsecuregcm.subscriptions.ProcessorCustomer;
import org.whispersystems.textsecuregcm.subscriptions.SubscriptionProcessor;
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;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class SubscriptionManagerTest {
private static final long NOW_EPOCH_SECONDS = 1_500_000_000L;
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();
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder().
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();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.SUBSCRIPTIONS);
byte[] user;
byte[] password;
@ -85,7 +52,7 @@ class SubscriptionManagerTest {
customer = Base64.getEncoder().encodeToString(getRandomBytes(16));
created = Instant.ofEpochSecond(NOW_EPOCH_SECONDS);
subscriptionManager = new SubscriptionManager(
SUBSCRIPTIONS_TABLE_NAME, dynamoDbExtension.getDynamoDbAsyncClient());
Tables.SUBSCRIPTIONS.tableName(), DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient());
}
@Test

View File

@ -19,15 +19,12 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
class VerificationCodeStoreTest {
private VerificationCodeStore verificationCodeStore;
private static final String TABLE_NAME = "verification_code_test";
private static final String PHONE_NUMBER = "+14151112222";
private static final long VALID_TIMESTAMP = Instant.now().toEpochMilli();
@ -35,18 +32,12 @@ class VerificationCodeStoreTest {
Duration.ofHours(1)).toEpochMilli();
@RegisterExtension
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
.tableName(TABLE_NAME)
.hashKey(VerificationCodeStore.KEY_E164)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(VerificationCodeStore.KEY_E164)
.attributeType(ScalarAttributeType.S)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.VERIFICATION_CODES);
@BeforeEach
void setUp() {
verificationCodeStore = new VerificationCodeStore(DYNAMO_DB_EXTENSION.getDynamoDbClient(), TABLE_NAME);
verificationCodeStore = new VerificationCodeStore(
DYNAMO_DB_EXTENSION.getDynamoDbClient(), Tables.VERIFICATION_CODES.tableName());
}
@Test

View File

@ -21,32 +21,23 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.whispersystems.textsecuregcm.registration.VerificationSession;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
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.ScalarAttributeType;
class VerificationSessionsTest {
private static final String TABLE_NAME = "verification_sessions_test";
private static final Clock clock = Clock.systemUTC();
@RegisterExtension
static final DynamoDbExtension DYNAMO_DB_EXTENSION = DynamoDbExtension.builder()
.tableName(TABLE_NAME)
.hashKey(VerificationSessions.KEY_KEY)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(VerificationSessions.KEY_KEY)
.attributeType(ScalarAttributeType.S)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.VERIFICATION_SESSIONS);
private VerificationSessions verificationSessions;
@BeforeEach
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

View File

@ -21,27 +21,18 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.receipts.ReceiptSerial;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.TestClock;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
class RedeemedReceiptsManagerTest {
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();
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
.tableName(REDEEMED_RECEIPTS_TABLE_NAME)
.hashKey(RedeemedReceiptsManager.KEY_SERIAL)
.attributeDefinition(AttributeDefinition.builder()
.attributeName(RedeemedReceiptsManager.KEY_SERIAL)
.attributeType(ScalarAttributeType.B)
.build())
.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.REDEEMED_RECEIPTS);
Clock clock = TestClock.pinned(Instant.ofEpochSecond(NOW_EPOCH_SECONDS));
ReceiptSerial receiptSerial;
@ -53,7 +44,10 @@ class RedeemedReceiptsManagerTest {
SECURE_RANDOM.nextBytes(receiptSerialBytes);
receiptSerial = new ReceiptSerial(receiptSerialBytes);
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

View File

@ -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();
}
}

View File

@ -51,11 +51,11 @@ import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.storage.MessagesCache;
import org.whispersystems.textsecuregcm.storage.MessagesDynamoDb;
import org.whispersystems.textsecuregcm.storage.MessagesManager;
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
import org.whispersystems.textsecuregcm.tests.util.MessagesDynamoDbExtension;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.websocket.WebSocketClient;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
@ -65,7 +65,7 @@ import reactor.core.scheduler.Schedulers;
class WebSocketConnectionIntegrationTest {
@RegisterExtension
static DynamoDbExtension dynamoDbExtension = MessagesDynamoDbExtension.build();
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(Tables.MESSAGES);
@RegisterExtension
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
@ -90,8 +90,8 @@ class WebSocketConnectionIntegrationTest {
messageDeliveryScheduler = Schedulers.newBoundedElastic(10, 10_000, "messageDelivery");
messagesCache = new MessagesCache(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
REDIS_CLUSTER_EXTENSION.getRedisCluster(), sharedExecutorService, messageDeliveryScheduler, sharedExecutorService, Clock.systemUTC());
messagesDynamoDb = new MessagesDynamoDb(dynamoDbExtension.getDynamoDbClient(),
dynamoDbExtension.getDynamoDbAsyncClient(), MessagesDynamoDbExtension.TABLE_NAME, Duration.ofDays(7),
messagesDynamoDb = new MessagesDynamoDb(DYNAMO_DB_EXTENSION.getDynamoDbClient(),
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.MESSAGES.tableName(), Duration.ofDays(7),
sharedExecutorService);
reportMessageManager = mock(ReportMessageManager.class);
account = mock(Account.class);