More Accounts cleanup
* Remove `AccountStore` * Clean up `AccountsDynamoDb#delete` * Rename `AccountsDynamoDb` → `Accounts` * Remove unused configuration * Move Accounts scan page size to static configuration * Remove disabled tests and related methods
This commit is contained in:
parent
75661fa800
commit
6a71d369e2
|
@ -109,11 +109,6 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private AccountDatabaseCrawlerConfiguration accountDatabaseCrawler;
|
private AccountDatabaseCrawlerConfiguration accountDatabaseCrawler;
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Valid
|
|
||||||
@JsonProperty
|
|
||||||
private AccountDatabaseCrawlerConfiguration dynamoDbMigrationCrawler;
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -149,21 +144,6 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private AccountsDynamoDbConfiguration accountsDynamoDb;
|
private AccountsDynamoDbConfiguration accountsDynamoDb;
|
||||||
|
|
||||||
@Valid
|
|
||||||
@NotNull
|
|
||||||
@JsonProperty
|
|
||||||
private DynamoDbConfiguration migrationDeletedAccountsDynamoDb;
|
|
||||||
|
|
||||||
@Valid
|
|
||||||
@NotNull
|
|
||||||
@JsonProperty
|
|
||||||
private DynamoDbConfiguration migrationMismatchedAccountsDynamoDb;
|
|
||||||
|
|
||||||
@Valid
|
|
||||||
@NotNull
|
|
||||||
@JsonProperty
|
|
||||||
private DynamoDbConfiguration migrationRetryAccountsDynamoDb;
|
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull
|
@NotNull
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -376,10 +356,6 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
return accountDatabaseCrawler;
|
return accountDatabaseCrawler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountDatabaseCrawlerConfiguration getDynamoDbMigrationCrawlerConfiguration() {
|
|
||||||
return dynamoDbMigrationCrawler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MessageCacheConfiguration getMessageCacheConfiguration() {
|
public MessageCacheConfiguration getMessageCacheConfiguration() {
|
||||||
return messageCache;
|
return messageCache;
|
||||||
}
|
}
|
||||||
|
@ -408,18 +384,6 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
return accountsDynamoDb;
|
return accountsDynamoDb;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamoDbConfiguration getMigrationDeletedAccountsDynamoDbConfiguration() {
|
|
||||||
return migrationDeletedAccountsDynamoDb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbConfiguration getMigrationMismatchedAccountsDynamoDbConfiguration() {
|
|
||||||
return migrationMismatchedAccountsDynamoDb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamoDbConfiguration getMigrationRetryAccountsDynamoDbConfiguration() {
|
|
||||||
return migrationRetryAccountsDynamoDb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeletedAccountsDynamoDbConfiguration getDeletedAccountsDynamoDbConfiguration() {
|
public DeletedAccountsDynamoDbConfiguration getDeletedAccountsDynamoDbConfiguration() {
|
||||||
return deletedAccountsDynamoDb;
|
return deletedAccountsDynamoDb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ import org.whispersystems.textsecuregcm.storage.AccountCleaner;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
|
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawler;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
|
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerCache;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
|
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerListener;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ContactDiscoveryWriter;
|
import org.whispersystems.textsecuregcm.storage.ContactDiscoveryWriter;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
||||||
|
@ -337,10 +337,10 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
config.getDeletedAccountsDynamoDbConfiguration().getTableName(),
|
config.getDeletedAccountsDynamoDbConfiguration().getTableName(),
|
||||||
config.getDeletedAccountsDynamoDbConfiguration().getNeedsReconciliationIndexName());
|
config.getDeletedAccountsDynamoDbConfiguration().getNeedsReconciliationIndexName());
|
||||||
|
|
||||||
AccountsDynamoDb accountsDynamoDb = new AccountsDynamoDb(accountsDynamoDbClient,
|
Accounts accounts = new Accounts(accountsDynamoDbClient,
|
||||||
config.getAccountsDynamoDbConfiguration().getTableName(),
|
config.getAccountsDynamoDbConfiguration().getTableName(),
|
||||||
config.getAccountsDynamoDbConfiguration().getPhoneNumberTableName()
|
config.getAccountsDynamoDbConfiguration().getPhoneNumberTableName(),
|
||||||
);
|
config.getAccountsDynamoDbConfiguration().getScanPageSize());
|
||||||
Usernames usernames = new Usernames(accountDatabase);
|
Usernames usernames = new Usernames(accountDatabase);
|
||||||
ReservedUsernames reservedUsernames = new ReservedUsernames(accountDatabase);
|
ReservedUsernames reservedUsernames = new ReservedUsernames(accountDatabase);
|
||||||
Profiles profiles = new Profiles(accountDatabase);
|
Profiles profiles = new Profiles(accountDatabase);
|
||||||
|
@ -424,10 +424,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, pushLatencyManager, reportMessageManager);
|
MessagesManager messagesManager = new MessagesManager(messagesDynamoDb, messagesCache, pushLatencyManager, reportMessageManager);
|
||||||
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
|
DeletedAccountsManager deletedAccountsManager = new DeletedAccountsManager(deletedAccounts,
|
||||||
deletedAccountsLockDynamoDbClient, config.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
deletedAccountsLockDynamoDbClient, config.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
||||||
AccountsManager accountsManager = new AccountsManager(accountsDynamoDb, cacheCluster,
|
AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster,
|
||||||
deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager,
|
deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager,
|
||||||
profilesManager, pendingAccountsManager, secureStorageClient, secureBackupClient,
|
pendingAccountsManager, secureStorageClient, secureBackupClient);
|
||||||
dynamicConfigurationManager);
|
|
||||||
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
RemoteConfigsManager remoteConfigsManager = new RemoteConfigsManager(remoteConfigs);
|
||||||
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(accountsManager, messagesManager);
|
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(accountsManager, messagesManager);
|
||||||
DispatchManager dispatchManager = new DispatchManager(pubSubClientFactory, Optional.of(deadLetterHandler));
|
DispatchManager dispatchManager = new DispatchManager(pubSubClientFactory, Optional.of(deadLetterHandler));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.whispersystems.textsecuregcm.configuration;
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
public class AccountsDynamoDbConfiguration extends DynamoDbConfiguration {
|
public class AccountsDynamoDbConfiguration extends DynamoDbConfiguration {
|
||||||
|
@ -7,7 +8,16 @@ public class AccountsDynamoDbConfiguration extends DynamoDbConfiguration {
|
||||||
@NotNull
|
@NotNull
|
||||||
private String phoneNumberTableName;
|
private String phoneNumberTableName;
|
||||||
|
|
||||||
|
private int scanPageSize = 100;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
public String getPhoneNumberTableName() {
|
public String getPhoneNumberTableName() {
|
||||||
return phoneNumberTableName;
|
return phoneNumberTableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public int getScanPageSize() {
|
||||||
|
return scanPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package org.whispersystems.textsecuregcm.configuration.dynamic;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
public class DynamicAccountsDynamoDbMigrationConfiguration {
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
int dynamoCrawlerScanPageSize = 10;
|
|
||||||
|
|
||||||
// TODO move out of "migration" configuration
|
|
||||||
public int getDynamoCrawlerScanPageSize() {
|
|
||||||
return dynamoCrawlerScanPageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -44,9 +44,6 @@ public class DynamicConfiguration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private DynamicSignupCaptchaConfiguration signupCaptcha = new DynamicSignupCaptchaConfiguration();
|
private DynamicSignupCaptchaConfiguration signupCaptcha = new DynamicSignupCaptchaConfiguration();
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private DynamicAccountsDynamoDbMigrationConfiguration accountsDynamoDbMigration = new DynamicAccountsDynamoDbMigrationConfiguration();
|
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@Valid
|
@Valid
|
||||||
private DynamicRateLimitChallengeConfiguration rateLimitChallenge = new DynamicRateLimitChallengeConfiguration();
|
private DynamicRateLimitChallengeConfiguration rateLimitChallenge = new DynamicRateLimitChallengeConfiguration();
|
||||||
|
@ -94,10 +91,6 @@ public class DynamicConfiguration {
|
||||||
return signupCaptcha;
|
return signupCaptcha;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicAccountsDynamoDbMigrationConfiguration getAccountsDynamoDbMigrationConfiguration() {
|
|
||||||
return accountsDynamoDbMigration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DynamicRateLimitChallengeConfiguration getRateLimitChallengeConfiguration() {
|
public DynamicRateLimitChallengeConfiguration getRateLimitChallengeConfiguration() {
|
||||||
return rateLimitChallenge;
|
return rateLimitChallenge;
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class AccountDatabaseCrawler implements Managed, Runnable {
|
||||||
final long endTimeMs = System.currentTimeMillis();
|
final long endTimeMs = System.currentTimeMillis();
|
||||||
final long sleepIntervalMs = chunkIntervalMs - (endTimeMs - startTimeMs);
|
final long sleepIntervalMs = chunkIntervalMs - (endTimeMs - startTimeMs);
|
||||||
if (sleepIntervalMs > 0) {
|
if (sleepIntervalMs > 0) {
|
||||||
logger.info("Sleeping {}ms", sleepIntervalMs);
|
logger.debug("Sleeping {}ms", sleepIntervalMs);
|
||||||
sleepWhileRunning(sleepIntervalMs);
|
sleepWhileRunning(sleepIntervalMs);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -135,7 +135,7 @@ public class AccountDatabaseCrawler implements Managed, Runnable {
|
||||||
cacheLastUuid(Optional.empty());
|
cacheLastUuid(Optional.empty());
|
||||||
cache.setAccelerated(false);
|
cache.setAccelerated(false);
|
||||||
} else {
|
} else {
|
||||||
logger.info("Processing chunk");
|
logger.debug("Processing chunk");
|
||||||
try {
|
try {
|
||||||
for (AccountDatabaseCrawlerListener listener : listeners) {
|
for (AccountDatabaseCrawlerListener listener : listeners) {
|
||||||
listener.timeAndProcessCrawlChunk(fromUuid, chunkAccounts.getAccounts());
|
listener.timeAndProcessCrawlChunk(fromUuid, chunkAccounts.getAccounts());
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public interface AccountStore {
|
|
||||||
|
|
||||||
boolean create(Account account);
|
|
||||||
|
|
||||||
void update(Account account) throws ContestedOptimisticLockException;
|
|
||||||
|
|
||||||
Optional<Account> get(String number);
|
|
||||||
|
|
||||||
Optional<Account> get(UUID uuid);
|
|
||||||
|
|
||||||
void delete(final UUID uuid);
|
|
||||||
}
|
|
|
@ -38,7 +38,7 @@ import software.amazon.awssdk.services.dynamodb.model.TransactionConflictExcepti
|
||||||
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;
|
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;
|
||||||
|
|
||||||
public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountStore {
|
public class Accounts extends AbstractDynamoDbStore {
|
||||||
|
|
||||||
// uuid, primary key
|
// uuid, primary key
|
||||||
static final String KEY_ACCOUNT_UUID = "U";
|
static final String KEY_ACCOUNT_UUID = "U";
|
||||||
|
@ -56,25 +56,28 @@ public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountSt
|
||||||
private final String phoneNumbersTableName;
|
private final String phoneNumbersTableName;
|
||||||
private final String accountsTableName;
|
private final String accountsTableName;
|
||||||
|
|
||||||
private static final Timer CREATE_TIMER = Metrics.timer(name(AccountsDynamoDb.class, "create"));
|
private final int scanPageSize;
|
||||||
private static final Timer UPDATE_TIMER = Metrics.timer(name(AccountsDynamoDb.class, "update"));
|
|
||||||
private static final Timer GET_BY_NUMBER_TIMER = Metrics.timer(name(AccountsDynamoDb.class, "getByNumber"));
|
private static final Timer CREATE_TIMER = Metrics.timer(name(Accounts.class, "create"));
|
||||||
private static final Timer GET_BY_UUID_TIMER = Metrics.timer(name(AccountsDynamoDb.class, "getByUuid"));
|
private static final Timer UPDATE_TIMER = Metrics.timer(name(Accounts.class, "update"));
|
||||||
private static final Timer GET_ALL_FROM_START_TIMER = Metrics.timer(name(AccountsDynamoDb.class, "getAllFrom"));
|
private static final Timer GET_BY_NUMBER_TIMER = Metrics.timer(name(Accounts.class, "getByNumber"));
|
||||||
private static final Timer GET_ALL_FROM_OFFSET_TIMER = Metrics.timer(name(AccountsDynamoDb.class, "getAllFromOffset"));
|
private static final Timer GET_BY_UUID_TIMER = Metrics.timer(name(Accounts.class, "getByUuid"));
|
||||||
private static final Timer DELETE_TIMER = Metrics.timer(name(AccountsDynamoDb.class, "delete"));
|
private static final Timer GET_ALL_FROM_START_TIMER = Metrics.timer(name(Accounts.class, "getAllFrom"));
|
||||||
|
private static final Timer GET_ALL_FROM_OFFSET_TIMER = Metrics.timer(name(Accounts.class, "getAllFromOffset"));
|
||||||
|
private static final Timer DELETE_TIMER = Metrics.timer(name(Accounts.class, "delete"));
|
||||||
|
|
||||||
|
|
||||||
public AccountsDynamoDb(DynamoDbClient client, String accountsTableName, String phoneNumbersTableName) {
|
public Accounts(DynamoDbClient client, String accountsTableName, String phoneNumbersTableName,
|
||||||
|
final int scanPageSize) {
|
||||||
|
|
||||||
super(client);
|
super(client);
|
||||||
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.phoneNumbersTableName = phoneNumbersTableName;
|
this.phoneNumbersTableName = phoneNumbersTableName;
|
||||||
this.accountsTableName = accountsTableName;
|
this.accountsTableName = accountsTableName;
|
||||||
|
this.scanPageSize = scanPageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean create(Account account) {
|
public boolean create(Account account) {
|
||||||
return CREATE_TIMER.record(() -> {
|
return CREATE_TIMER.record(() -> {
|
||||||
|
|
||||||
|
@ -115,7 +118,7 @@ public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountSt
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("TransactionConflict".equals(accountCancellationReason.code())) {
|
if ("TransactionConflict".equals(accountCancellationReason.code())) {
|
||||||
// this should only happen during concurrent update()s for an account migration
|
// this should only happen if two clients manage to make concurrent create() calls
|
||||||
throw new ContestedOptimisticLockException();
|
throw new ContestedOptimisticLockException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +167,6 @@ public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountSt
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(Account account) throws ContestedOptimisticLockException {
|
public void update(Account account) throws ContestedOptimisticLockException {
|
||||||
UPDATE_TIMER.record(() -> {
|
UPDATE_TIMER.record(() -> {
|
||||||
UpdateItemRequest updateItemRequest;
|
UpdateItemRequest updateItemRequest;
|
||||||
|
@ -207,8 +209,6 @@ public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountSt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Account> get(String number) {
|
public Optional<Account> get(String number) {
|
||||||
return GET_BY_NUMBER_TIMER.record(() -> {
|
return GET_BY_NUMBER_TIMER.record(() -> {
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountSt
|
||||||
return Optional.ofNullable(response.item())
|
return Optional.ofNullable(response.item())
|
||||||
.map(item -> item.get(KEY_ACCOUNT_UUID))
|
.map(item -> item.get(KEY_ACCOUNT_UUID))
|
||||||
.map(uuid -> accountByUuid(uuid))
|
.map(uuid -> accountByUuid(uuid))
|
||||||
.map(AccountsDynamoDb::fromItem);
|
.map(Accounts::fromItem);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,29 +233,52 @@ public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountSt
|
||||||
return r.item().isEmpty() ? null : r.item();
|
return r.item().isEmpty() ? null : r.item();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Account> get(UUID uuid) {
|
public Optional<Account> get(UUID uuid) {
|
||||||
return GET_BY_UUID_TIMER.record(() ->
|
return GET_BY_UUID_TIMER.record(() ->
|
||||||
Optional.ofNullable(accountByUuid(AttributeValues.fromUUID(uuid)))
|
Optional.ofNullable(accountByUuid(AttributeValues.fromUUID(uuid)))
|
||||||
.map(AccountsDynamoDb::fromItem));
|
.map(Accounts::fromItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete(UUID uuid) {
|
public void delete(UUID uuid) {
|
||||||
DELETE_TIMER.record(() -> delete(uuid, true));
|
DELETE_TIMER.record(() -> {
|
||||||
|
|
||||||
|
Optional<Account> maybeAccount = get(uuid);
|
||||||
|
|
||||||
|
maybeAccount.ifPresent(account -> {
|
||||||
|
|
||||||
|
TransactWriteItem phoneNumberDelete = TransactWriteItem.builder()
|
||||||
|
.delete(Delete.builder()
|
||||||
|
.tableName(phoneNumbersTableName)
|
||||||
|
.key(Map.of(ATTR_ACCOUNT_E164, AttributeValues.fromString(account.getNumber())))
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TransactWriteItem accountDelete = TransactWriteItem.builder()
|
||||||
|
.delete(Delete.builder()
|
||||||
|
.tableName(accountsTableName)
|
||||||
|
.key(Map.of(KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)))
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TransactWriteItemsRequest request = TransactWriteItemsRequest.builder()
|
||||||
|
.transactItems(phoneNumberDelete, accountDelete).build();
|
||||||
|
|
||||||
|
client.transactWriteItems(request);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountCrawlChunk getAllFrom(final UUID from, final int maxCount, final int pageSize) {
|
public AccountCrawlChunk getAllFrom(final UUID from, final int maxCount) {
|
||||||
final ScanRequest.Builder scanRequestBuilder = ScanRequest.builder()
|
final ScanRequest.Builder scanRequestBuilder = ScanRequest.builder()
|
||||||
.limit(pageSize)
|
.limit(scanPageSize)
|
||||||
.exclusiveStartKey(Map.of(KEY_ACCOUNT_UUID, AttributeValues.fromUUID(from)));
|
.exclusiveStartKey(Map.of(KEY_ACCOUNT_UUID, AttributeValues.fromUUID(from)));
|
||||||
|
|
||||||
return scanForChunk(scanRequestBuilder, maxCount, GET_ALL_FROM_OFFSET_TIMER);
|
return scanForChunk(scanRequestBuilder, maxCount, GET_ALL_FROM_OFFSET_TIMER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountCrawlChunk getAllFromStart(final int maxCount, final int pageSize) {
|
public AccountCrawlChunk getAllFromStart(final int maxCount) {
|
||||||
final ScanRequest.Builder scanRequestBuilder = ScanRequest.builder()
|
final ScanRequest.Builder scanRequestBuilder = ScanRequest.builder()
|
||||||
.limit(pageSize);
|
.limit(scanPageSize);
|
||||||
|
|
||||||
return scanForChunk(scanRequestBuilder, maxCount, GET_ALL_FROM_START_TIMER);
|
return scanForChunk(scanRequestBuilder, maxCount, GET_ALL_FROM_START_TIMER);
|
||||||
}
|
}
|
||||||
|
@ -266,39 +289,12 @@ public class AccountsDynamoDb extends AbstractDynamoDbStore implements AccountSt
|
||||||
|
|
||||||
final List<Account> accounts = timer.record(() -> scan(scanRequestBuilder.build(), maxCount)
|
final List<Account> accounts = timer.record(() -> scan(scanRequestBuilder.build(), maxCount)
|
||||||
.stream()
|
.stream()
|
||||||
.map(AccountsDynamoDb::fromItem)
|
.map(Accounts::fromItem)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|
||||||
return new AccountCrawlChunk(accounts, accounts.size() > 0 ? accounts.get(accounts.size() - 1).getUuid() : null);
|
return new AccountCrawlChunk(accounts, accounts.size() > 0 ? accounts.get(accounts.size() - 1).getUuid() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void delete(UUID uuid, boolean saveInDeletedAccountsTable) {
|
|
||||||
|
|
||||||
Optional<Account> maybeAccount = get(uuid);
|
|
||||||
|
|
||||||
maybeAccount.ifPresent(account -> {
|
|
||||||
|
|
||||||
TransactWriteItem phoneNumberDelete = TransactWriteItem.builder()
|
|
||||||
.delete(Delete.builder()
|
|
||||||
.tableName(phoneNumbersTableName)
|
|
||||||
.key(Map.of(ATTR_ACCOUNT_E164, AttributeValues.fromString(account.getNumber())))
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
TransactWriteItem accountDelete = TransactWriteItem.builder()
|
|
||||||
.delete(Delete.builder()
|
|
||||||
.tableName(accountsTableName)
|
|
||||||
.key(Map.of(KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)))
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
TransactWriteItemsRequest request = TransactWriteItemsRequest.builder()
|
|
||||||
.transactItems(phoneNumberDelete, accountDelete).build();
|
|
||||||
|
|
||||||
client.transactWriteItems(request);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String extractCancellationReasonCodes(final TransactionCanceledException exception) {
|
private static String extractCancellationReasonCodes(final TransactionCanceledException exception) {
|
||||||
return exception.cancellationReasons().stream()
|
return exception.cancellationReasons().stream()
|
||||||
.map(CancellationReason::code)
|
.map(CancellationReason::code)
|
|
@ -18,15 +18,12 @@ import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||||
import io.micrometer.core.instrument.Metrics;
|
import io.micrometer.core.instrument.Metrics;
|
||||||
import io.micrometer.core.instrument.Tags;
|
import io.micrometer.core.instrument.Tags;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
|
@ -65,7 +62,7 @@ public class AccountsManager {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(AccountsManager.class);
|
private final Logger logger = LoggerFactory.getLogger(AccountsManager.class);
|
||||||
|
|
||||||
private final AccountsDynamoDb accountsDynamoDb;
|
private final Accounts accounts;
|
||||||
private final FaultTolerantRedisCluster cacheCluster;
|
private final FaultTolerantRedisCluster cacheCluster;
|
||||||
private final DeletedAccountsManager deletedAccountsManager;
|
private final DeletedAccountsManager deletedAccountsManager;
|
||||||
private final DirectoryQueue directoryQueue;
|
private final DirectoryQueue directoryQueue;
|
||||||
|
@ -78,8 +75,6 @@ public class AccountsManager {
|
||||||
private final SecureBackupClient secureBackupClient;
|
private final SecureBackupClient secureBackupClient;
|
||||||
private final ObjectMapper mapper;
|
private final ObjectMapper mapper;
|
||||||
|
|
||||||
private final DynamicConfigurationManager dynamicConfigurationManager;
|
|
||||||
|
|
||||||
public enum DeletionReason {
|
public enum DeletionReason {
|
||||||
ADMIN_DELETED("admin"),
|
ADMIN_DELETED("admin"),
|
||||||
EXPIRED ("expired"),
|
EXPIRED ("expired"),
|
||||||
|
@ -92,7 +87,7 @@ public class AccountsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountsManager(AccountsDynamoDb accountsDynamoDb, FaultTolerantRedisCluster cacheCluster,
|
public AccountsManager(Accounts accounts, FaultTolerantRedisCluster cacheCluster,
|
||||||
final DeletedAccountsManager deletedAccountsManager,
|
final DeletedAccountsManager deletedAccountsManager,
|
||||||
final DirectoryQueue directoryQueue,
|
final DirectoryQueue directoryQueue,
|
||||||
final KeysDynamoDb keysDynamoDb, final MessagesManager messagesManager,
|
final KeysDynamoDb keysDynamoDb, final MessagesManager messagesManager,
|
||||||
|
@ -100,22 +95,20 @@ public class AccountsManager {
|
||||||
final ProfilesManager profilesManager,
|
final ProfilesManager profilesManager,
|
||||||
final StoredVerificationCodeManager pendingAccounts,
|
final StoredVerificationCodeManager pendingAccounts,
|
||||||
final SecureStorageClient secureStorageClient,
|
final SecureStorageClient secureStorageClient,
|
||||||
final SecureBackupClient secureBackupClient,
|
final SecureBackupClient secureBackupClient) {
|
||||||
final DynamicConfigurationManager dynamicConfigurationManager) {
|
this.accounts = accounts;
|
||||||
this.accountsDynamoDb = accountsDynamoDb;
|
this.cacheCluster = cacheCluster;
|
||||||
this.cacheCluster = cacheCluster;
|
|
||||||
this.deletedAccountsManager = deletedAccountsManager;
|
this.deletedAccountsManager = deletedAccountsManager;
|
||||||
this.directoryQueue = directoryQueue;
|
this.directoryQueue = directoryQueue;
|
||||||
this.keysDynamoDb = keysDynamoDb;
|
this.keysDynamoDb = keysDynamoDb;
|
||||||
this.messagesManager = messagesManager;
|
this.messagesManager = messagesManager;
|
||||||
this.usernamesManager = usernamesManager;
|
this.usernamesManager = usernamesManager;
|
||||||
this.profilesManager = profilesManager;
|
this.profilesManager = profilesManager;
|
||||||
this.pendingAccounts = pendingAccounts;
|
this.pendingAccounts = pendingAccounts;
|
||||||
this.secureStorageClient = secureStorageClient;
|
this.secureStorageClient = secureStorageClient;
|
||||||
this.secureBackupClient = secureBackupClient;
|
this.secureBackupClient = secureBackupClient;
|
||||||
this.mapper = SystemMapper.getMapper();
|
this.mapper = SystemMapper.getMapper();
|
||||||
|
|
||||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account create(final String number,
|
public Account create(final String number,
|
||||||
|
@ -336,15 +329,11 @@ public class AccountsManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountCrawlChunk getAllFromDynamo(int length) {
|
public AccountCrawlChunk getAllFromDynamo(int length) {
|
||||||
final int maxPageSize = dynamicConfigurationManager.getConfiguration().getAccountsDynamoDbMigrationConfiguration()
|
return accounts.getAllFromStart(length);
|
||||||
.getDynamoCrawlerScanPageSize();
|
|
||||||
return accountsDynamoDb.getAllFromStart(length, maxPageSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountCrawlChunk getAllFromDynamo(UUID uuid, int length) {
|
public AccountCrawlChunk getAllFromDynamo(UUID uuid, int length) {
|
||||||
final int maxPageSize = dynamicConfigurationManager.getConfiguration().getAccountsDynamoDbMigrationConfiguration()
|
return accounts.getAllFrom(uuid, length);
|
||||||
.getDynamoCrawlerScanPageSize();
|
|
||||||
return accountsDynamoDb.getAllFrom(uuid, length, maxPageSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(final Account account, final DeletionReason deletionReason) throws InterruptedException {
|
public void delete(final Account account, final DeletionReason deletionReason) throws InterruptedException {
|
||||||
|
@ -445,38 +434,23 @@ public class AccountsManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Account> dynamoGet(String number) {
|
private Optional<Account> dynamoGet(String number) {
|
||||||
return accountsDynamoDb.get(number);
|
return accounts.get(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Account> dynamoGet(UUID uuid) {
|
private Optional<Account> dynamoGet(UUID uuid) {
|
||||||
return accountsDynamoDb.get(uuid);
|
return accounts.get(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean dynamoCreate(Account account) {
|
private boolean dynamoCreate(Account account) {
|
||||||
return accountsDynamoDb.create(account);
|
return accounts.create(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dynamoUpdate(Account account) {
|
private void dynamoUpdate(Account account) {
|
||||||
accountsDynamoDb.update(account);
|
accounts.update(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dynamoDelete(final Account account) {
|
private void dynamoDelete(final Account account) {
|
||||||
accountsDynamoDb.delete(account.getUuid());
|
accounts.delete(account.getUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO delete
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
|
||||||
@Deprecated
|
|
||||||
public Optional<String> compareAccounts(final Optional<Account> maybePrimaryAccount,
|
|
||||||
final Optional<Account> maybeSecondaryAccount) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getAbbreviatedCallChain(final StackTraceElement[] stackTrace) {
|
|
||||||
return Arrays.stream(stackTrace)
|
|
||||||
.filter(stackTraceElement -> stackTraceElement.getClassName().contains("org.whispersystems"))
|
|
||||||
.filter(stackTraceElement -> !(stackTraceElement.getClassName().endsWith("AccountsManager") && stackTraceElement.getMethodName().contains("compare")))
|
|
||||||
.map(stackTraceElement -> StringUtils.substringAfterLast(stackTraceElement.getClassName(), ".") + ":" + stackTraceElement.getMethodName())
|
|
||||||
.collect(Collectors.joining(" -> "));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager.DeletionReason;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager.DeletionReason;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
||||||
|
@ -150,9 +150,10 @@ public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfigura
|
||||||
VerificationCodeStore pendingAccounts = new VerificationCodeStore(pendingAccountsDynamoDbClient,
|
VerificationCodeStore pendingAccounts = new VerificationCodeStore(pendingAccountsDynamoDbClient,
|
||||||
configuration.getPendingAccountsDynamoDbConfiguration().getTableName());
|
configuration.getPendingAccountsDynamoDbConfiguration().getTableName());
|
||||||
|
|
||||||
AccountsDynamoDb accountsDynamoDb = new AccountsDynamoDb(accountsDynamoDbClient,
|
Accounts accounts = new Accounts(accountsDynamoDbClient,
|
||||||
configuration.getAccountsDynamoDbConfiguration().getTableName(),
|
configuration.getAccountsDynamoDbConfiguration().getTableName(),
|
||||||
configuration.getAccountsDynamoDbConfiguration().getPhoneNumberTableName());
|
configuration.getAccountsDynamoDbConfiguration().getPhoneNumberTableName(),
|
||||||
|
configuration.getAccountsDynamoDbConfiguration().getScanPageSize());
|
||||||
Usernames usernames = new Usernames(accountDatabase);
|
Usernames usernames = new Usernames(accountDatabase);
|
||||||
Profiles profiles = new Profiles(accountDatabase);
|
Profiles profiles = new Profiles(accountDatabase);
|
||||||
ReservedUsernames reservedUsernames = new ReservedUsernames(accountDatabase);
|
ReservedUsernames reservedUsernames = new ReservedUsernames(accountDatabase);
|
||||||
|
@ -188,10 +189,9 @@ public class DeleteUserCommand extends EnvironmentCommand<WhisperServerConfigura
|
||||||
deletedAccountsLockDynamoDbClient,
|
deletedAccountsLockDynamoDbClient,
|
||||||
configuration.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
configuration.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
||||||
StoredVerificationCodeManager pendingAccountsManager = new StoredVerificationCodeManager(pendingAccounts);
|
StoredVerificationCodeManager pendingAccountsManager = new StoredVerificationCodeManager(pendingAccounts);
|
||||||
AccountsManager accountsManager = new AccountsManager(accountsDynamoDb, cacheCluster,
|
AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster,
|
||||||
deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager,
|
deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager,
|
||||||
profilesManager, pendingAccountsManager, secureStorageClient, secureBackupClient,
|
pendingAccountsManager, secureStorageClient, secureBackupClient);
|
||||||
dynamicConfigurationManager);
|
|
||||||
|
|
||||||
for (String user : users) {
|
for (String user : users) {
|
||||||
Optional<Account> account = accountsManager.get(user);
|
Optional<Account> account = accountsManager.get(user);
|
||||||
|
|
|
@ -32,7 +32,7 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
import org.whispersystems.textsecuregcm.storage.DeletedAccounts;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager;
|
import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager;
|
||||||
|
@ -153,10 +153,10 @@ public class SetUserDiscoverabilityCommand extends EnvironmentCommand<WhisperSer
|
||||||
VerificationCodeStore pendingAccounts = new VerificationCodeStore(pendingAccountsDynamoDbClient,
|
VerificationCodeStore pendingAccounts = new VerificationCodeStore(pendingAccountsDynamoDbClient,
|
||||||
configuration.getPendingAccountsDynamoDbConfiguration().getTableName());
|
configuration.getPendingAccountsDynamoDbConfiguration().getTableName());
|
||||||
|
|
||||||
AccountsDynamoDb accountsDynamoDb = new AccountsDynamoDb(accountsDynamoDbClient,
|
Accounts accounts = new Accounts(accountsDynamoDbClient,
|
||||||
configuration.getAccountsDynamoDbConfiguration().getTableName(),
|
configuration.getAccountsDynamoDbConfiguration().getTableName(),
|
||||||
configuration.getAccountsDynamoDbConfiguration().getPhoneNumberTableName()
|
configuration.getAccountsDynamoDbConfiguration().getPhoneNumberTableName(),
|
||||||
);
|
configuration.getAccountsDynamoDbConfiguration().getScanPageSize());
|
||||||
Usernames usernames = new Usernames(accountDatabase);
|
Usernames usernames = new Usernames(accountDatabase);
|
||||||
Profiles profiles = new Profiles(accountDatabase);
|
Profiles profiles = new Profiles(accountDatabase);
|
||||||
ReservedUsernames reservedUsernames = new ReservedUsernames(accountDatabase);
|
ReservedUsernames reservedUsernames = new ReservedUsernames(accountDatabase);
|
||||||
|
@ -192,11 +192,9 @@ public class SetUserDiscoverabilityCommand extends EnvironmentCommand<WhisperSer
|
||||||
deletedAccountsLockDynamoDbClient,
|
deletedAccountsLockDynamoDbClient,
|
||||||
configuration.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
configuration.getDeletedAccountsLockDynamoDbConfiguration().getTableName());
|
||||||
StoredVerificationCodeManager pendingAccountsManager = new StoredVerificationCodeManager(pendingAccounts);
|
StoredVerificationCodeManager pendingAccountsManager = new StoredVerificationCodeManager(pendingAccounts);
|
||||||
AccountsManager accountsManager = new AccountsManager(accountsDynamoDb, cacheCluster,
|
AccountsManager accountsManager = new AccountsManager(accounts, cacheCluster,
|
||||||
deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager,
|
deletedAccountsManager, directoryQueue, keysDynamoDb, messagesManager, usernamesManager, profilesManager,
|
||||||
profilesManager,
|
pendingAccountsManager, secureStorageClient, secureBackupClient);
|
||||||
pendingAccountsManager, secureStorageClient, secureBackupClient,
|
|
||||||
dynamicConfigurationManager);
|
|
||||||
|
|
||||||
Optional<Account> maybeAccount;
|
Optional<Account> maybeAccount;
|
||||||
|
|
||||||
|
|
|
@ -313,29 +313,6 @@ class DynamicConfigurationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testParseAccountsDynamoDbMigrationConfiguration() throws JsonProcessingException {
|
|
||||||
{
|
|
||||||
final String emptyConfigYaml = "test: true";
|
|
||||||
final DynamicConfiguration emptyConfig =
|
|
||||||
DynamicConfigurationManager.parseConfiguration(emptyConfigYaml).orElseThrow();
|
|
||||||
|
|
||||||
assertEquals(10, emptyConfig.getAccountsDynamoDbMigrationConfiguration().getDynamoCrawlerScanPageSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
final String accountsDynamoDbMigrationConfig =
|
|
||||||
"accountsDynamoDbMigration:\n"
|
|
||||||
+ " dynamoCrawlerScanPageSize: 5000";
|
|
||||||
|
|
||||||
final DynamicAccountsDynamoDbMigrationConfiguration config =
|
|
||||||
DynamicConfigurationManager.parseConfiguration(accountsDynamoDbMigrationConfig).orElseThrow()
|
|
||||||
.getAccountsDynamoDbMigrationConfiguration();
|
|
||||||
|
|
||||||
assertEquals(5000, config.getDynamoCrawlerScanPageSize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testParseLimits() throws JsonProcessingException {
|
void testParseLimits() throws JsonProcessingException {
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,8 +33,6 @@ public class AccountDatabaseCrawlerIntegrationTest extends AbstractRedisClusterT
|
||||||
private AccountsManager accountsManager;
|
private AccountsManager accountsManager;
|
||||||
private AccountDatabaseCrawlerListener listener;
|
private AccountDatabaseCrawlerListener listener;
|
||||||
|
|
||||||
private DynamicConfigurationManager dynamicConfigurationManager;
|
|
||||||
|
|
||||||
private AccountDatabaseCrawler accountDatabaseCrawler;
|
private AccountDatabaseCrawler accountDatabaseCrawler;
|
||||||
|
|
||||||
private static final int CHUNK_SIZE = 1;
|
private static final int CHUNK_SIZE = 1;
|
||||||
|
@ -50,8 +48,6 @@ public class AccountDatabaseCrawlerIntegrationTest extends AbstractRedisClusterT
|
||||||
accountsManager = mock(AccountsManager.class);
|
accountsManager = mock(AccountsManager.class);
|
||||||
listener = mock(AccountDatabaseCrawlerListener.class);
|
listener = mock(AccountDatabaseCrawlerListener.class);
|
||||||
|
|
||||||
dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
|
||||||
|
|
||||||
when(firstAccount.getUuid()).thenReturn(FIRST_UUID);
|
when(firstAccount.getUuid()).thenReturn(FIRST_UUID);
|
||||||
when(secondAccount.getUuid()).thenReturn(SECOND_UUID);
|
when(secondAccount.getUuid()).thenReturn(SECOND_UUID);
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,7 @@ import static org.mockito.Mockito.atLeast;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.opentable.db.postgres.embedded.LiquibasePreparer;
|
|
||||||
import com.opentable.db.postgres.junit5.EmbeddedPostgresExtension;
|
|
||||||
import com.opentable.db.postgres.junit5.PreparedDbExtension;
|
|
||||||
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
@ -38,7 +34,6 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
|
@ -55,23 +50,22 @@ import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
|
||||||
|
|
||||||
class AccountsManagerConcurrentModificationIntegrationTest {
|
class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
static PreparedDbExtension db = EmbeddedPostgresExtension.preparedDatabase(LiquibasePreparer.forClasspathLocation("accountsdb.xml"));
|
|
||||||
|
|
||||||
private static final String ACCOUNTS_TABLE_NAME = "accounts_test";
|
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 int SCAN_PAGE_SIZE = 1;
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
||||||
.tableName(ACCOUNTS_TABLE_NAME)
|
.tableName(ACCOUNTS_TABLE_NAME)
|
||||||
.hashKey(AccountsDynamoDb.KEY_ACCOUNT_UUID)
|
.hashKey(Accounts.KEY_ACCOUNT_UUID)
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
.attributeDefinition(AttributeDefinition.builder()
|
||||||
.attributeName(AccountsDynamoDb.KEY_ACCOUNT_UUID)
|
.attributeName(Accounts.KEY_ACCOUNT_UUID)
|
||||||
.attributeType(ScalarAttributeType.B)
|
.attributeType(ScalarAttributeType.B)
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private AccountsDynamoDb accountsDynamoDb;
|
private Accounts accounts;
|
||||||
|
|
||||||
private AccountsManager accountsManager;
|
private AccountsManager accountsManager;
|
||||||
|
|
||||||
|
@ -86,11 +80,11 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
||||||
.tableName(NUMBERS_TABLE_NAME)
|
.tableName(NUMBERS_TABLE_NAME)
|
||||||
.keySchema(KeySchemaElement.builder()
|
.keySchema(KeySchemaElement.builder()
|
||||||
.attributeName(AccountsDynamoDb.ATTR_ACCOUNT_E164)
|
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
||||||
.keyType(KeyType.HASH)
|
.keyType(KeyType.HASH)
|
||||||
.build())
|
.build())
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
.attributeDefinitions(AttributeDefinition.builder()
|
||||||
.attributeName(AccountsDynamoDb.ATTR_ACCOUNT_E164)
|
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
||||||
.attributeType(ScalarAttributeType.S)
|
.attributeType(ScalarAttributeType.S)
|
||||||
.build())
|
.build())
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
||||||
|
@ -99,18 +93,14 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
dynamoDbExtension.getDynamoDbClient().createTable(createNumbersTableRequest);
|
dynamoDbExtension.getDynamoDbClient().createTable(createNumbersTableRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
accountsDynamoDb = new AccountsDynamoDb(
|
accounts = new Accounts(
|
||||||
dynamoDbExtension.getDynamoDbClient(),
|
dynamoDbExtension.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getTableName(),
|
dynamoDbExtension.getTableName(),
|
||||||
NUMBERS_TABLE_NAME
|
NUMBERS_TABLE_NAME,
|
||||||
);
|
SCAN_PAGE_SIZE);
|
||||||
|
|
||||||
{
|
{
|
||||||
final DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
//noinspection unchecked
|
||||||
|
|
||||||
DynamicConfiguration dynamicConfiguration = new DynamicConfiguration();
|
|
||||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
|
||||||
|
|
||||||
commands = mock(RedisAdvancedClusterCommands.class);
|
commands = mock(RedisAdvancedClusterCommands.class);
|
||||||
|
|
||||||
final DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.class);
|
final DeletedAccountsManager deletedAccountsManager = mock(DeletedAccountsManager.class);
|
||||||
|
@ -122,7 +112,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
}).when(deletedAccountsManager).lockAndTake(anyString(), any());
|
}).when(deletedAccountsManager).lockAndTake(anyString(), any());
|
||||||
|
|
||||||
accountsManager = new AccountsManager(
|
accountsManager = new AccountsManager(
|
||||||
accountsDynamoDb,
|
accounts,
|
||||||
RedisClusterHelper.buildMockRedisCluster(commands),
|
RedisClusterHelper.buildMockRedisCluster(commands),
|
||||||
deletedAccountsManager,
|
deletedAccountsManager,
|
||||||
mock(DirectoryQueue.class),
|
mock(DirectoryQueue.class),
|
||||||
|
@ -132,8 +122,8 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
mock(ProfilesManager.class),
|
mock(ProfilesManager.class),
|
||||||
mock(StoredVerificationCodeManager.class),
|
mock(StoredVerificationCodeManager.class),
|
||||||
mock(SecureStorageClient.class),
|
mock(SecureStorageClient.class),
|
||||||
mock(SecureBackupClient.class),
|
mock(SecureBackupClient.class)
|
||||||
dynamicConfigurationManager);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,12 +176,12 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
modifyAccount(uuid, account -> account.setUnidentifiedAccessKey(unidentifiedAccessKey)),
|
modifyAccount(uuid, account -> account.setUnidentifiedAccessKey(unidentifiedAccessKey)),
|
||||||
modifyAccount(uuid, account -> account.setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt())),
|
modifyAccount(uuid, account -> account.setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt())),
|
||||||
modifyAccount(uuid, account -> account.setUnrestrictedUnidentifiedAccess(unrestrictedUnidentifiedAccess)),
|
modifyAccount(uuid, account -> account.setUnrestrictedUnidentifiedAccess(unrestrictedUnidentifiedAccess)),
|
||||||
modifyDevice(uuid, Device.MASTER_ID, device-> device.setLastSeen(lastSeen)),
|
modifyDevice(uuid, Device.MASTER_ID, device -> device.setLastSeen(lastSeen)),
|
||||||
modifyDevice(uuid, Device.MASTER_ID, device-> device.setName("deviceName"))
|
modifyDevice(uuid, Device.MASTER_ID, device -> device.setName("deviceName"))
|
||||||
).join();
|
).join();
|
||||||
|
|
||||||
final Account managerAccount = accountsManager.get(uuid).get();
|
final Account managerAccount = accountsManager.get(uuid).orElseThrow();
|
||||||
final Account dynamoAccount = accountsDynamoDb.get(uuid).get();
|
final Account dynamoAccount = accounts.get(uuid).orElseThrow();
|
||||||
|
|
||||||
final Account redisAccount = getLastAccountFromRedisMock(commands);
|
final Account redisAccount = getLastAccountFromRedisMock(commands);
|
||||||
|
|
||||||
|
@ -200,10 +190,9 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
new Pair<>("dynamo", dynamoAccount),
|
new Pair<>("dynamo", dynamoAccount),
|
||||||
new Pair<>("redis", redisAccount)
|
new Pair<>("redis", redisAccount)
|
||||||
).forEach(pair ->
|
).forEach(pair ->
|
||||||
verifyAccount(pair.first(), pair.second(), profileName, avatar, discoverableByPhoneNumber,
|
verifyAccount(pair.first(), pair.second(), profileName, avatar, discoverableByPhoneNumber,
|
||||||
currentProfileVersion, identityKey, unidentifiedAccessKey, pin, registrationLock,
|
currentProfileVersion, identityKey, unidentifiedAccessKey, pin, registrationLock,
|
||||||
unrestrictedUnidentifiedAccess, lastSeen)
|
unrestrictedUnidentifiedAccess, lastSeen));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account getLastAccountFromRedisMock(RedisAdvancedClusterCommands<String, String> commands) throws IOException {
|
private Account getLastAccountFromRedisMock(RedisAdvancedClusterCommands<String, String> commands) throws IOException {
|
||||||
|
@ -220,9 +209,9 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
() -> assertEquals(profileName, account.getProfileName()),
|
() -> assertEquals(profileName, account.getProfileName()),
|
||||||
() -> assertEquals(avatar, account.getAvatar()),
|
() -> assertEquals(avatar, account.getAvatar()),
|
||||||
() -> assertEquals(discoverableByPhoneNumber, account.isDiscoverableByPhoneNumber()),
|
() -> assertEquals(discoverableByPhoneNumber, account.isDiscoverableByPhoneNumber()),
|
||||||
() -> assertEquals(currentProfileVersion, account.getCurrentProfileVersion().get()),
|
() -> assertEquals(currentProfileVersion, account.getCurrentProfileVersion().orElseThrow()),
|
||||||
() -> assertEquals(identityKey, account.getIdentityKey()),
|
() -> assertEquals(identityKey, account.getIdentityKey()),
|
||||||
() -> assertArrayEquals(unidentifiedAccessKey, account.getUnidentifiedAccessKey().get()),
|
() -> assertArrayEquals(unidentifiedAccessKey, account.getUnidentifiedAccessKey().orElseThrow()),
|
||||||
() -> assertTrue(account.getRegistrationLock().verify(clientRegistrationLock)),
|
() -> assertTrue(account.getRegistrationLock().verify(clientRegistrationLock)),
|
||||||
() -> assertEquals(unrestrictedUnidentifiedAcces, account.isUnrestrictedUnidentifiedAccess())
|
() -> assertEquals(unrestrictedUnidentifiedAcces, account.isUnrestrictedUnidentifiedAccess())
|
||||||
);
|
);
|
||||||
|
@ -231,7 +220,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
private CompletableFuture<?> modifyAccount(final UUID uuid, final Consumer<Account> accountMutation) {
|
private CompletableFuture<?> modifyAccount(final UUID uuid, final Consumer<Account> accountMutation) {
|
||||||
|
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.runAsync(() -> {
|
||||||
final Account account = accountsManager.get(uuid).get();
|
final Account account = accountsManager.get(uuid).orElseThrow();
|
||||||
accountsManager.update(account, accountMutation);
|
accountsManager.update(account, accountMutation);
|
||||||
}, mutationExecutor);
|
}, mutationExecutor);
|
||||||
}
|
}
|
||||||
|
@ -239,7 +228,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
||||||
private CompletableFuture<?> modifyDevice(final UUID uuid, final long deviceId, final Consumer<Device> deviceMutation) {
|
private CompletableFuture<?> modifyDevice(final UUID uuid, final long deviceId, final Consumer<Device> deviceMutation) {
|
||||||
|
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.runAsync(() -> {
|
||||||
final Account account = accountsManager.get(uuid).get();
|
final Account account = accountsManager.get(uuid).orElseThrow();
|
||||||
accountsManager.updateDevice(account, deviceId, deviceMutation);
|
accountsManager.updateDevice(account, deviceId, deviceMutation);
|
||||||
}, mutationExecutor);
|
}, mutationExecutor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,33 +45,35 @@ import software.amazon.awssdk.services.dynamodb.model.TransactWriteItemsRequest;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.TransactionConflictException;
|
import software.amazon.awssdk.services.dynamodb.model.TransactionConflictException;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
|
||||||
|
|
||||||
class AccountsDynamoDbTest {
|
class AccountsTest {
|
||||||
|
|
||||||
private static final String ACCOUNTS_TABLE_NAME = "accounts_test";
|
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 int SCAN_PAGE_SIZE = 1;
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
static DynamoDbExtension dynamoDbExtension = DynamoDbExtension.builder()
|
||||||
.tableName(ACCOUNTS_TABLE_NAME)
|
.tableName(ACCOUNTS_TABLE_NAME)
|
||||||
.hashKey(AccountsDynamoDb.KEY_ACCOUNT_UUID)
|
.hashKey(Accounts.KEY_ACCOUNT_UUID)
|
||||||
.attributeDefinition(AttributeDefinition.builder()
|
.attributeDefinition(AttributeDefinition.builder()
|
||||||
.attributeName(AccountsDynamoDb.KEY_ACCOUNT_UUID)
|
.attributeName(Accounts.KEY_ACCOUNT_UUID)
|
||||||
.attributeType(ScalarAttributeType.B)
|
.attributeType(ScalarAttributeType.B)
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private AccountsDynamoDb accountsDynamoDb;
|
private Accounts accounts;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setupAccountsDao() {
|
void setupAccountsDao() {
|
||||||
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
CreateTableRequest createNumbersTableRequest = CreateTableRequest.builder()
|
||||||
.tableName(NUMBERS_TABLE_NAME)
|
.tableName(NUMBERS_TABLE_NAME)
|
||||||
.keySchema(KeySchemaElement.builder()
|
.keySchema(KeySchemaElement.builder()
|
||||||
.attributeName(AccountsDynamoDb.ATTR_ACCOUNT_E164)
|
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
||||||
.keyType(KeyType.HASH)
|
.keyType(KeyType.HASH)
|
||||||
.build())
|
.build())
|
||||||
.attributeDefinitions(AttributeDefinition.builder()
|
.attributeDefinitions(AttributeDefinition.builder()
|
||||||
.attributeName(AccountsDynamoDb.ATTR_ACCOUNT_E164)
|
.attributeName(Accounts.ATTR_ACCOUNT_E164)
|
||||||
.attributeType(ScalarAttributeType.S)
|
.attributeType(ScalarAttributeType.S)
|
||||||
.build())
|
.build())
|
||||||
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
.provisionedThroughput(DynamoDbExtension.DEFAULT_PROVISIONED_THROUGHPUT)
|
||||||
|
@ -79,11 +81,11 @@ class AccountsDynamoDbTest {
|
||||||
|
|
||||||
dynamoDbExtension.getDynamoDbClient().createTable(createNumbersTableRequest);
|
dynamoDbExtension.getDynamoDbClient().createTable(createNumbersTableRequest);
|
||||||
|
|
||||||
this.accountsDynamoDb = new AccountsDynamoDb(
|
this.accounts = new Accounts(
|
||||||
dynamoDbExtension.getDynamoDbClient(),
|
dynamoDbExtension.getDynamoDbClient(),
|
||||||
dynamoDbExtension.getTableName(),
|
dynamoDbExtension.getTableName(),
|
||||||
NUMBERS_TABLE_NAME
|
NUMBERS_TABLE_NAME,
|
||||||
);
|
SCAN_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -91,12 +93,12 @@ class AccountsDynamoDbTest {
|
||||||
Device device = generateDevice (1 );
|
Device device = generateDevice (1 );
|
||||||
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
||||||
|
|
||||||
boolean freshUser = accountsDynamoDb.create(account);
|
boolean freshUser = accounts.create(account);
|
||||||
|
|
||||||
assertThat(freshUser).isTrue();
|
assertThat(freshUser).isTrue();
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
||||||
|
|
||||||
freshUser = accountsDynamoDb.create(account);
|
freshUser = accounts.create(account);
|
||||||
assertThat(freshUser).isTrue();
|
assertThat(freshUser).isTrue();
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
||||||
|
|
||||||
|
@ -110,7 +112,7 @@ class AccountsDynamoDbTest {
|
||||||
|
|
||||||
Account account = generateAccount("+14151112222", UUID.randomUUID(), devices);
|
Account account = generateAccount("+14151112222", UUID.randomUUID(), devices);
|
||||||
|
|
||||||
accountsDynamoDb.create(account);
|
accounts.create(account);
|
||||||
|
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
||||||
}
|
}
|
||||||
|
@ -131,11 +133,11 @@ class AccountsDynamoDbTest {
|
||||||
UUID uuidSecond = UUID.randomUUID();
|
UUID uuidSecond = UUID.randomUUID();
|
||||||
Account accountSecond = generateAccount("+14152221111", uuidSecond, devicesSecond);
|
Account accountSecond = generateAccount("+14152221111", uuidSecond, devicesSecond);
|
||||||
|
|
||||||
accountsDynamoDb.create(accountFirst);
|
accounts.create(accountFirst);
|
||||||
accountsDynamoDb.create(accountSecond);
|
accounts.create(accountSecond);
|
||||||
|
|
||||||
Optional<Account> retrievedFirst = accountsDynamoDb.get("+14151112222");
|
Optional<Account> retrievedFirst = accounts.get("+14151112222");
|
||||||
Optional<Account> retrievedSecond = accountsDynamoDb.get("+14152221111");
|
Optional<Account> retrievedSecond = accounts.get("+14152221111");
|
||||||
|
|
||||||
assertThat(retrievedFirst.isPresent()).isTrue();
|
assertThat(retrievedFirst.isPresent()).isTrue();
|
||||||
assertThat(retrievedSecond.isPresent()).isTrue();
|
assertThat(retrievedSecond.isPresent()).isTrue();
|
||||||
|
@ -143,8 +145,8 @@ class AccountsDynamoDbTest {
|
||||||
verifyStoredState("+14151112222", uuidFirst, retrievedFirst.get(), accountFirst);
|
verifyStoredState("+14151112222", uuidFirst, retrievedFirst.get(), accountFirst);
|
||||||
verifyStoredState("+14152221111", uuidSecond, retrievedSecond.get(), accountSecond);
|
verifyStoredState("+14152221111", uuidSecond, retrievedSecond.get(), accountSecond);
|
||||||
|
|
||||||
retrievedFirst = accountsDynamoDb.get(uuidFirst);
|
retrievedFirst = accounts.get(uuidFirst);
|
||||||
retrievedSecond = accountsDynamoDb.get(uuidSecond);
|
retrievedSecond = accounts.get(uuidSecond);
|
||||||
|
|
||||||
assertThat(retrievedFirst.isPresent()).isTrue();
|
assertThat(retrievedFirst.isPresent()).isTrue();
|
||||||
assertThat(retrievedSecond.isPresent()).isTrue();
|
assertThat(retrievedSecond.isPresent()).isTrue();
|
||||||
|
@ -159,27 +161,27 @@ class AccountsDynamoDbTest {
|
||||||
UUID firstUuid = UUID.randomUUID();
|
UUID firstUuid = UUID.randomUUID();
|
||||||
Account account = generateAccount("+14151112222", firstUuid, Collections.singleton(device));
|
Account account = generateAccount("+14151112222", firstUuid, Collections.singleton(device));
|
||||||
|
|
||||||
accountsDynamoDb.create(account);
|
accounts.create(account);
|
||||||
|
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
||||||
|
|
||||||
account.setProfileName("name");
|
account.setProfileName("name");
|
||||||
|
|
||||||
accountsDynamoDb.update(account);
|
accounts.update(account);
|
||||||
|
|
||||||
UUID secondUuid = UUID.randomUUID();
|
UUID secondUuid = UUID.randomUUID();
|
||||||
|
|
||||||
device = generateDevice(1);
|
device = generateDevice(1);
|
||||||
account = generateAccount("+14151112222", secondUuid, Collections.singleton(device));
|
account = generateAccount("+14151112222", secondUuid, Collections.singleton(device));
|
||||||
|
|
||||||
final boolean freshUser = accountsDynamoDb.create(account);
|
final boolean freshUser = accounts.create(account);
|
||||||
assertThat(freshUser).isFalse();
|
assertThat(freshUser).isFalse();
|
||||||
verifyStoredState("+14151112222", firstUuid, account, true);
|
verifyStoredState("+14151112222", firstUuid, account, true);
|
||||||
|
|
||||||
device = generateDevice(1);
|
device = generateDevice(1);
|
||||||
Account invalidAccount = generateAccount("+14151113333", firstUuid, Collections.singleton(device));
|
Account invalidAccount = generateAccount("+14151113333", firstUuid, Collections.singleton(device));
|
||||||
|
|
||||||
assertThatThrownBy(() -> accountsDynamoDb.create(invalidAccount));
|
assertThatThrownBy(() -> accounts.create(invalidAccount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -187,18 +189,18 @@ class AccountsDynamoDbTest {
|
||||||
Device device = generateDevice (1 );
|
Device device = generateDevice (1 );
|
||||||
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
||||||
|
|
||||||
accountsDynamoDb.create(account);
|
accounts.create(account);
|
||||||
|
|
||||||
device.setName("foobar");
|
device.setName("foobar");
|
||||||
|
|
||||||
accountsDynamoDb.update(account);
|
accounts.update(account);
|
||||||
|
|
||||||
Optional<Account> retrieved = accountsDynamoDb.get("+14151112222");
|
Optional<Account> retrieved = accounts.get("+14151112222");
|
||||||
|
|
||||||
assertThat(retrieved.isPresent()).isTrue();
|
assertThat(retrieved.isPresent()).isTrue();
|
||||||
verifyStoredState("+14151112222", account.getUuid(), retrieved.get(), account);
|
verifyStoredState("+14151112222", account.getUuid(), retrieved.get(), account);
|
||||||
|
|
||||||
retrieved = accountsDynamoDb.get(account.getUuid());
|
retrieved = accounts.get(account.getUuid());
|
||||||
|
|
||||||
assertThat(retrieved.isPresent()).isTrue();
|
assertThat(retrieved.isPresent()).isTrue();
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
||||||
|
@ -206,11 +208,11 @@ class AccountsDynamoDbTest {
|
||||||
device = generateDevice(1);
|
device = generateDevice(1);
|
||||||
Account unknownAccount = generateAccount("+14151113333", UUID.randomUUID(), Collections.singleton(device));
|
Account unknownAccount = generateAccount("+14151113333", UUID.randomUUID(), Collections.singleton(device));
|
||||||
|
|
||||||
assertThatThrownBy(() -> accountsDynamoDb.update(unknownAccount)).isInstanceOfAny(ConditionalCheckFailedException.class);
|
assertThatThrownBy(() -> accounts.update(unknownAccount)).isInstanceOfAny(ConditionalCheckFailedException.class);
|
||||||
|
|
||||||
account.setProfileName("name");
|
account.setProfileName("name");
|
||||||
|
|
||||||
accountsDynamoDb.update(account);
|
accounts.update(account);
|
||||||
|
|
||||||
assertThat(account.getVersion()).isEqualTo(2);
|
assertThat(account.getVersion()).isEqualTo(2);
|
||||||
|
|
||||||
|
@ -218,12 +220,12 @@ class AccountsDynamoDbTest {
|
||||||
|
|
||||||
account.setVersion(1);
|
account.setVersion(1);
|
||||||
|
|
||||||
assertThatThrownBy(() -> accountsDynamoDb.update(account)).isInstanceOfAny(ContestedOptimisticLockException.class);
|
assertThatThrownBy(() -> accounts.update(account)).isInstanceOfAny(ContestedOptimisticLockException.class);
|
||||||
|
|
||||||
account.setVersion(2);
|
account.setVersion(2);
|
||||||
account.setProfileName("name2");
|
account.setProfileName("name2");
|
||||||
|
|
||||||
accountsDynamoDb.update(account);
|
accounts.update(account);
|
||||||
|
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
||||||
}
|
}
|
||||||
|
@ -232,8 +234,8 @@ class AccountsDynamoDbTest {
|
||||||
void testUpdateWithMockTransactionConflictException() {
|
void testUpdateWithMockTransactionConflictException() {
|
||||||
|
|
||||||
final DynamoDbClient dynamoDbClient = mock(DynamoDbClient.class);
|
final DynamoDbClient dynamoDbClient = mock(DynamoDbClient.class);
|
||||||
accountsDynamoDb = new AccountsDynamoDb(dynamoDbClient,
|
accounts = new Accounts(dynamoDbClient,
|
||||||
dynamoDbExtension.getTableName(), NUMBERS_TABLE_NAME);
|
dynamoDbExtension.getTableName(), NUMBERS_TABLE_NAME, SCAN_PAGE_SIZE);
|
||||||
|
|
||||||
when(dynamoDbClient.updateItem(any(UpdateItemRequest.class)))
|
when(dynamoDbClient.updateItem(any(UpdateItemRequest.class)))
|
||||||
.thenThrow(TransactionConflictException.class);
|
.thenThrow(TransactionConflictException.class);
|
||||||
|
@ -241,7 +243,7 @@ class AccountsDynamoDbTest {
|
||||||
Device device = generateDevice(1);
|
Device device = generateDevice(1);
|
||||||
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
||||||
|
|
||||||
assertThatThrownBy(() -> accountsDynamoDb.update(account)).isInstanceOfAny(ContestedOptimisticLockException.class);
|
assertThatThrownBy(() -> accounts.update(account)).isInstanceOfAny(ContestedOptimisticLockException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -251,12 +253,12 @@ class AccountsDynamoDbTest {
|
||||||
for (int i = 1; i <= 100; i++) {
|
for (int i = 1; i <= 100; i++) {
|
||||||
Account account = generateAccount("+1" + String.format("%03d", i), UUID.randomUUID());
|
Account account = generateAccount("+1" + String.format("%03d", i), UUID.randomUUID());
|
||||||
users.add(account);
|
users.add(account);
|
||||||
accountsDynamoDb.create(account);
|
accounts.create(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
users.sort((account, t1) -> UUIDComparator.staticCompare(account.getUuid(), t1.getUuid()));
|
users.sort((account, t1) -> UUIDComparator.staticCompare(account.getUuid(), t1.getUuid()));
|
||||||
|
|
||||||
AccountCrawlChunk retrieved = accountsDynamoDb.getAllFromStart(10, 1);
|
AccountCrawlChunk retrieved = accounts.getAllFromStart(10);
|
||||||
assertThat(retrieved.getAccounts().size()).isEqualTo(10);
|
assertThat(retrieved.getAccounts().size()).isEqualTo(10);
|
||||||
|
|
||||||
for (int i = 0; i < retrieved.getAccounts().size(); i++) {
|
for (int i = 0; i < retrieved.getAccounts().size(); i++) {
|
||||||
|
@ -273,7 +275,7 @@ class AccountsDynamoDbTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 0; j < 9; j++) {
|
for (int j = 0; j < 9; j++) {
|
||||||
retrieved = accountsDynamoDb.getAllFrom(retrieved.getLastUuid().orElseThrow(), 10, 1);
|
retrieved = accounts.getAllFrom(retrieved.getLastUuid().orElseThrow(), 10);
|
||||||
assertThat(retrieved.getAccounts().size()).isEqualTo(10);
|
assertThat(retrieved.getAccounts().size()).isEqualTo(10);
|
||||||
|
|
||||||
for (int i = 0; i < retrieved.getAccounts().size(); i++) {
|
for (int i = 0; i < retrieved.getAccounts().size(); i++) {
|
||||||
|
@ -295,33 +297,36 @@ class AccountsDynamoDbTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testDelete() {
|
void testDelete() {
|
||||||
final Device deletedDevice = generateDevice (1);
|
final Device deletedDevice = generateDevice(1);
|
||||||
final Account deletedAccount = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(deletedDevice));
|
final Account deletedAccount = generateAccount("+14151112222", UUID.randomUUID(),
|
||||||
final Device retainedDevice = generateDevice (1);
|
Collections.singleton(deletedDevice));
|
||||||
final Account retainedAccount = generateAccount("+14151112345", UUID.randomUUID(), Collections.singleton(retainedDevice));
|
final Device retainedDevice = generateDevice(1);
|
||||||
|
final Account retainedAccount = generateAccount("+14151112345", UUID.randomUUID(),
|
||||||
|
Collections.singleton(retainedDevice));
|
||||||
|
|
||||||
accountsDynamoDb.create(deletedAccount);
|
accounts.create(deletedAccount);
|
||||||
accountsDynamoDb.create(retainedAccount);
|
accounts.create(retainedAccount);
|
||||||
|
|
||||||
assertThat(accountsDynamoDb.get(deletedAccount.getUuid())).isPresent();
|
assertThat(accounts.get(deletedAccount.getUuid())).isPresent();
|
||||||
assertThat(accountsDynamoDb.get(retainedAccount.getUuid())).isPresent();
|
assertThat(accounts.get(retainedAccount.getUuid())).isPresent();
|
||||||
|
|
||||||
accountsDynamoDb.delete(deletedAccount.getUuid());
|
accounts.delete(deletedAccount.getUuid());
|
||||||
|
|
||||||
assertThat(accountsDynamoDb.get(deletedAccount.getUuid())).isNotPresent();
|
assertThat(accounts.get(deletedAccount.getUuid())).isNotPresent();
|
||||||
|
|
||||||
verifyStoredState(retainedAccount.getNumber(), retainedAccount.getUuid(), accountsDynamoDb.get(retainedAccount.getUuid()).get(), retainedAccount);
|
verifyStoredState(retainedAccount.getNumber(), retainedAccount.getUuid(),
|
||||||
|
accounts.get(retainedAccount.getUuid()).get(), retainedAccount);
|
||||||
|
|
||||||
{
|
{
|
||||||
final Account recreatedAccount = generateAccount(deletedAccount.getNumber(), UUID.randomUUID(),
|
final Account recreatedAccount = generateAccount(deletedAccount.getNumber(), UUID.randomUUID(),
|
||||||
Collections.singleton(generateDevice(1)));
|
Collections.singleton(generateDevice(1)));
|
||||||
|
|
||||||
final boolean freshUser = accountsDynamoDb.create(recreatedAccount);
|
final boolean freshUser = accounts.create(recreatedAccount);
|
||||||
|
|
||||||
assertThat(freshUser).isTrue();
|
assertThat(freshUser).isTrue();
|
||||||
assertThat(accountsDynamoDb.get(recreatedAccount.getUuid())).isPresent();
|
assertThat(accounts.get(recreatedAccount.getUuid())).isPresent();
|
||||||
verifyStoredState(recreatedAccount.getNumber(), recreatedAccount.getUuid(),
|
verifyStoredState(recreatedAccount.getNumber(), recreatedAccount.getUuid(),
|
||||||
accountsDynamoDb.get(recreatedAccount.getUuid()).get(), recreatedAccount);
|
accounts.get(recreatedAccount.getUuid()).get(), recreatedAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,12 +335,12 @@ class AccountsDynamoDbTest {
|
||||||
Device device = generateDevice (1 );
|
Device device = generateDevice (1 );
|
||||||
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
Account account = generateAccount("+14151112222", UUID.randomUUID(), Collections.singleton(device));
|
||||||
|
|
||||||
accountsDynamoDb.create(account);
|
accounts.create(account);
|
||||||
|
|
||||||
Optional<Account> retrieved = accountsDynamoDb.get("+11111111");
|
Optional<Account> retrieved = accounts.get("+11111111");
|
||||||
assertThat(retrieved.isPresent()).isFalse();
|
assertThat(retrieved.isPresent()).isFalse();
|
||||||
|
|
||||||
retrieved = accountsDynamoDb.get(UUID.randomUUID());
|
retrieved = accounts.get(UUID.randomUUID());
|
||||||
assertThat(retrieved.isPresent()).isFalse();
|
assertThat(retrieved.isPresent()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +362,7 @@ class AccountsDynamoDbTest {
|
||||||
when(client.updateItem(any(UpdateItemRequest.class)))
|
when(client.updateItem(any(UpdateItemRequest.class)))
|
||||||
.thenThrow(RuntimeException.class);
|
.thenThrow(RuntimeException.class);
|
||||||
|
|
||||||
AccountsDynamoDb accounts = new AccountsDynamoDb(client, ACCOUNTS_TABLE_NAME, NUMBERS_TABLE_NAME);
|
Accounts accounts = new Accounts(client, ACCOUNTS_TABLE_NAME, NUMBERS_TABLE_NAME, SCAN_PAGE_SIZE);
|
||||||
Account account = generateAccount("+14151112222", UUID.randomUUID());
|
Account account = generateAccount("+14151112222", UUID.randomUUID());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -397,13 +402,13 @@ class AccountsDynamoDbTest {
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = generateAccount("+14151112222", uuid, Collections.singleton(device));
|
Account account = generateAccount("+14151112222", uuid, Collections.singleton(device));
|
||||||
account.setDiscoverableByPhoneNumber(false);
|
account.setDiscoverableByPhoneNumber(false);
|
||||||
accountsDynamoDb.create(account);
|
accounts.create(account);
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, false);
|
verifyStoredState("+14151112222", account.getUuid(), account, false);
|
||||||
account.setDiscoverableByPhoneNumber(true);
|
account.setDiscoverableByPhoneNumber(true);
|
||||||
accountsDynamoDb.update(account);
|
accounts.update(account);
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
verifyStoredState("+14151112222", account.getUuid(), account, true);
|
||||||
account.setDiscoverableByPhoneNumber(false);
|
account.setDiscoverableByPhoneNumber(false);
|
||||||
accountsDynamoDb.update(account);
|
accounts.update(account);
|
||||||
verifyStoredState("+14151112222", account.getUuid(), account, false);
|
verifyStoredState("+14151112222", account.getUuid(), account, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,20 +438,21 @@ class AccountsDynamoDbTest {
|
||||||
|
|
||||||
final GetItemResponse get = db.getItem(GetItemRequest.builder()
|
final GetItemResponse get = db.getItem(GetItemRequest.builder()
|
||||||
.tableName(dynamoDbExtension.getTableName())
|
.tableName(dynamoDbExtension.getTableName())
|
||||||
.key(Map.of(AccountsDynamoDb.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)))
|
.key(Map.of(Accounts.KEY_ACCOUNT_UUID, AttributeValues.fromUUID(uuid)))
|
||||||
.consistentRead(true)
|
.consistentRead(true)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
if (get.hasItem()) {
|
if (get.hasItem()) {
|
||||||
String data = new String(get.item().get(AccountsDynamoDb.ATTR_ACCOUNT_DATA).b().asByteArray(), StandardCharsets.UTF_8);
|
String data = new String(get.item().get(Accounts.ATTR_ACCOUNT_DATA).b().asByteArray(), StandardCharsets.UTF_8);
|
||||||
assertThat(data).isNotEmpty();
|
assertThat(data).isNotEmpty();
|
||||||
|
|
||||||
assertThat(AttributeValues.getInt(get.item(), AccountsDynamoDb.ATTR_VERSION, -1))
|
assertThat(AttributeValues.getInt(get.item(), Accounts.ATTR_VERSION, -1))
|
||||||
.isEqualTo(expecting.getVersion());
|
.isEqualTo(expecting.getVersion());
|
||||||
|
|
||||||
assertThat(AttributeValues.getBool(get.item(), AccountsDynamoDb.ATTR_CANONICALLY_DISCOVERABLE, !canonicallyDiscoverable)).isEqualTo(canonicallyDiscoverable);
|
assertThat(AttributeValues.getBool(get.item(), Accounts.ATTR_CANONICALLY_DISCOVERABLE,
|
||||||
|
!canonicallyDiscoverable)).isEqualTo(canonicallyDiscoverable);
|
||||||
|
|
||||||
Account result = AccountsDynamoDb.fromItem(get.item());
|
Account result = Accounts.fromItem(get.item());
|
||||||
verifyStoredState(number, uuid, result, expecting);
|
verifyStoredState(number, uuid, result, expecting);
|
||||||
} else {
|
} else {
|
||||||
throw new AssertionError("No data");
|
throw new AssertionError("No data");
|
|
@ -6,9 +6,7 @@
|
||||||
package org.whispersystems.textsecuregcm.tests.storage;
|
package org.whispersystems.textsecuregcm.tests.storage;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
@ -26,20 +24,17 @@ import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import io.lettuce.core.RedisException;
|
import io.lettuce.core.RedisException;
|
||||||
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
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.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
|
@ -48,7 +43,7 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
|
||||||
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
|
||||||
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsDynamoDb;
|
import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ContestedOptimisticLockException;
|
import org.whispersystems.textsecuregcm.storage.ContestedOptimisticLockException;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager;
|
import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager;
|
||||||
|
@ -60,12 +55,11 @@ import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
import org.whispersystems.textsecuregcm.storage.StoredVerificationCodeManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.JsonHelpers;
|
|
||||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||||
|
|
||||||
class AccountsManagerTest {
|
class AccountsManagerTest {
|
||||||
|
|
||||||
private AccountsDynamoDb accountsDynamoDb;
|
private Accounts accounts;
|
||||||
private DeletedAccountsManager deletedAccountsManager;
|
private DeletedAccountsManager deletedAccountsManager;
|
||||||
private DirectoryQueue directoryQueue;
|
private DirectoryQueue directoryQueue;
|
||||||
private DynamicConfigurationManager dynamicConfigurationManager;
|
private DynamicConfigurationManager dynamicConfigurationManager;
|
||||||
|
@ -86,7 +80,7 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() throws InterruptedException {
|
void setup() throws InterruptedException {
|
||||||
accountsDynamoDb = mock(AccountsDynamoDb.class);
|
accounts = mock(Accounts.class);
|
||||||
deletedAccountsManager = mock(DeletedAccountsManager.class);
|
deletedAccountsManager = mock(DeletedAccountsManager.class);
|
||||||
directoryQueue = mock(DirectoryQueue.class);
|
directoryQueue = mock(DirectoryQueue.class);
|
||||||
dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||||
|
@ -107,7 +101,7 @@ class AccountsManagerTest {
|
||||||
}).when(deletedAccountsManager).lockAndTake(anyString(), any());
|
}).when(deletedAccountsManager).lockAndTake(anyString(), any());
|
||||||
|
|
||||||
accountsManager = new AccountsManager(
|
accountsManager = new AccountsManager(
|
||||||
accountsDynamoDb,
|
accounts,
|
||||||
RedisClusterHelper.buildMockRedisCluster(commands),
|
RedisClusterHelper.buildMockRedisCluster(commands),
|
||||||
deletedAccountsManager,
|
deletedAccountsManager,
|
||||||
directoryQueue,
|
directoryQueue,
|
||||||
|
@ -117,8 +111,8 @@ class AccountsManagerTest {
|
||||||
profilesManager,
|
profilesManager,
|
||||||
mock(StoredVerificationCodeManager.class),
|
mock(StoredVerificationCodeManager.class),
|
||||||
mock(SecureStorageClient.class),
|
mock(SecureStorageClient.class),
|
||||||
mock(SecureBackupClient.class),
|
mock(SecureBackupClient.class)
|
||||||
dynamicConfigurationManager);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -138,7 +132,7 @@ class AccountsManagerTest {
|
||||||
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verifyNoInteractions(accountsDynamoDb);
|
verifyNoInteractions(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -157,7 +151,7 @@ class AccountsManagerTest {
|
||||||
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
verify(commands, times(1)).get(eq("Account3::" + uuid));
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verifyNoInteractions(accountsDynamoDb);
|
verifyNoInteractions(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -168,7 +162,7 @@ class AccountsManagerTest {
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null);
|
when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null);
|
||||||
when(accountsDynamoDb.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
||||||
|
|
||||||
|
@ -180,8 +174,8 @@ class AccountsManagerTest {
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).get(eq("+14152222222"));
|
verify(accounts, times(1)).get(eq("+14152222222"));
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
verifyNoMoreInteractions(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -191,7 +185,7 @@ class AccountsManagerTest {
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
||||||
when(accountsDynamoDb.get(eq(uuid))).thenReturn(Optional.of(account));
|
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
Optional<Account> retrieved = accountsManager.get(uuid);
|
Optional<Account> retrieved = accountsManager.get(uuid);
|
||||||
|
|
||||||
|
@ -203,8 +197,8 @@ class AccountsManagerTest {
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).get(eq(uuid));
|
verify(accounts, times(1)).get(eq(uuid));
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
verifyNoMoreInteractions(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -213,7 +207,7 @@ class AccountsManagerTest {
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!"));
|
when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!"));
|
||||||
when(accountsDynamoDb.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
when(accounts.get(eq("+14152222222"))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
Optional<Account> retrieved = accountsManager.get("+14152222222");
|
||||||
|
|
||||||
|
@ -225,18 +219,17 @@ class AccountsManagerTest {
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).get(eq("+14152222222"));
|
verify(accounts, times(1)).get(eq("+14152222222"));
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
verifyNoMoreInteractions(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGetAccountByUuidBrokenCache() {
|
void testGetAccountByUuidBrokenCache() {
|
||||||
final boolean dynamoEnabled = true;
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!"));
|
when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!"));
|
||||||
when(accountsDynamoDb.get(eq(uuid))).thenReturn(Optional.of(account));
|
when(accounts.get(eq(uuid))).thenReturn(Optional.of(account));
|
||||||
|
|
||||||
Optional<Account> retrieved = accountsManager.get(uuid);
|
Optional<Account> retrieved = accountsManager.get(uuid);
|
||||||
|
|
||||||
|
@ -248,76 +241,8 @@ class AccountsManagerTest {
|
||||||
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
verify(commands, times(1)).set(eq("Account3::" + uuid), anyString());
|
||||||
verifyNoMoreInteractions(commands);
|
verifyNoMoreInteractions(commands);
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).get(eq(uuid));
|
verify(accounts, times(1)).get(eq(uuid));
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
verifyNoMoreInteractions(accounts);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO delete
|
|
||||||
@Disabled("migration specific")
|
|
||||||
@Test
|
|
||||||
void testUpdate_dynamoDbMigration() throws IOException {
|
|
||||||
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
|
||||||
// database fetches should always return new instances
|
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(
|
|
||||||
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(
|
|
||||||
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
|
||||||
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accountsDynamoDb).update(any(Account.class));
|
|
||||||
|
|
||||||
Account updatedAccount = accountsManager.update(account, a -> a.setProfileName("name"));
|
|
||||||
|
|
||||||
assertThrows(AssertionError.class, account::getProfileName, "Account passed to update() should be stale");
|
|
||||||
|
|
||||||
assertNotSame(updatedAccount, account);
|
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).update(account);
|
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
|
||||||
|
|
||||||
ArgumentCaptor<Account> argumentCaptor = ArgumentCaptor.forClass(Account.class);
|
|
||||||
verify(accountsDynamoDb, times(1)).update(argumentCaptor.capture());
|
|
||||||
assertEquals(uuid, argumentCaptor.getValue().getUuid());
|
|
||||||
verify(accountsDynamoDb, times(1)).get(uuid);
|
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
|
||||||
|
|
||||||
ArgumentCaptor<String> redisSetArgumentCapture = ArgumentCaptor.forClass(String.class);
|
|
||||||
|
|
||||||
verify(commands, times(2)).set(anyString(), redisSetArgumentCapture.capture());
|
|
||||||
|
|
||||||
Account accountCached = JsonHelpers.fromJson(redisSetArgumentCapture.getAllValues().get(1), Account.class);
|
|
||||||
|
|
||||||
// uuid is @JsonIgnore, so we need to set it for compareAccounts to work
|
|
||||||
accountCached.setUuid(uuid);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(),
|
|
||||||
accountsManager.compareAccounts(Optional.of(updatedAccount), Optional.of(accountCached)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO delete
|
|
||||||
@Disabled("migration specific")
|
|
||||||
@Test
|
|
||||||
void testUpdate_dynamoMissing() {
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(Optional.empty());
|
|
||||||
doAnswer(ACCOUNT_UPDATE_ANSWER).when(accountsDynamoDb).update(any());
|
|
||||||
|
|
||||||
Account updatedAccount = accountsManager.update(account, a -> {
|
|
||||||
});
|
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).update(account);
|
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
|
||||||
|
|
||||||
verify(accountsDynamoDb, never()).update(account);
|
|
||||||
verify(accountsDynamoDb, times(1)).get(uuid);
|
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
|
||||||
|
|
||||||
assertEquals(1, updatedAccount.getVersion());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -327,52 +252,51 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
||||||
|
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(
|
when(accounts.get(uuid)).thenReturn(
|
||||||
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
||||||
doThrow(ContestedOptimisticLockException.class)
|
doThrow(ContestedOptimisticLockException.class)
|
||||||
.doAnswer(ACCOUNT_UPDATE_ANSWER)
|
.doAnswer(ACCOUNT_UPDATE_ANSWER)
|
||||||
.when(accountsDynamoDb).update(any());
|
.when(accounts).update(any());
|
||||||
|
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(
|
when(accounts.get(uuid)).thenReturn(
|
||||||
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
||||||
doThrow(ContestedOptimisticLockException.class)
|
doThrow(ContestedOptimisticLockException.class)
|
||||||
.doAnswer(ACCOUNT_UPDATE_ANSWER)
|
.doAnswer(ACCOUNT_UPDATE_ANSWER)
|
||||||
.when(accountsDynamoDb).update(any());
|
.when(accounts).update(any());
|
||||||
|
|
||||||
account = accountsManager.update(account, a -> a.setProfileName("name"));
|
account = accountsManager.update(account, a -> a.setProfileName("name"));
|
||||||
|
|
||||||
assertEquals(1, account.getVersion());
|
assertEquals(1, account.getVersion());
|
||||||
assertEquals("name", account.getProfileName());
|
assertEquals("name", account.getProfileName());
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).get(uuid);
|
verify(accounts, times(1)).get(uuid);
|
||||||
verify(accountsDynamoDb, times(2)).update(any());
|
verify(accounts, times(2)).update(any());
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
verifyNoMoreInteractions(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate_dynamoOptimisticLockingFailureDuringCreate() {
|
void testUpdate_dynamoOptimisticLockingFailureDuringCreate() {
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
when(commands.get(eq("Account3::" + uuid))).thenReturn(null);
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(Optional.empty())
|
when(accounts.get(uuid)).thenReturn(Optional.empty())
|
||||||
.thenReturn(Optional.of(account));
|
.thenReturn(Optional.of(account));
|
||||||
when(accountsDynamoDb.create(any())).thenThrow(ContestedOptimisticLockException.class);
|
when(accounts.create(any())).thenThrow(ContestedOptimisticLockException.class);
|
||||||
|
|
||||||
accountsManager.update(account, a -> {});
|
accountsManager.update(account, a -> {
|
||||||
|
});
|
||||||
|
|
||||||
verify(accountsDynamoDb, times(1)).update(account);
|
verify(accounts, times(1)).update(account);
|
||||||
verifyNoMoreInteractions(accountsDynamoDb);
|
verifyNoMoreInteractions(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdateDevice() {
|
void testUpdateDevice() {
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.empty(), Optional.empty()));
|
|
||||||
|
|
||||||
final UUID uuid = UUID.randomUUID();
|
final UUID uuid = UUID.randomUUID();
|
||||||
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
Account account = new Account("+14152222222", uuid, new HashSet<>(), new byte[16]);
|
||||||
|
|
||||||
when(accountsDynamoDb.get(uuid)).thenReturn(
|
when(accounts.get(uuid)).thenReturn(
|
||||||
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
Optional.of(new Account("+14152222222", uuid, new HashSet<>(), new byte[16])));
|
||||||
|
|
||||||
assertTrue(account.getDevices().isEmpty());
|
assertTrue(account.getDevices().isEmpty());
|
||||||
|
@ -400,90 +324,15 @@ class AccountsManagerTest {
|
||||||
verify(unknownDeviceUpdater, never()).accept(any(Device.class));
|
verify(unknownDeviceUpdater, never()).accept(any(Device.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO delete
|
|
||||||
@Disabled("migration specific")
|
|
||||||
@Test
|
|
||||||
void testCompareAccounts() throws Exception {
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.empty(), Optional.empty()));
|
|
||||||
|
|
||||||
final UUID uuidA = UUID.randomUUID();
|
|
||||||
final Account a1 = new Account("+14152222222", uuidA, new HashSet<>(), new byte[16]);
|
|
||||||
|
|
||||||
assertEquals(Optional.of("primaryMissing"), accountsManager.compareAccounts(Optional.empty(), Optional.of(a1)));
|
|
||||||
|
|
||||||
final Account a2 = new Account("+14152222222", uuidA, new HashSet<>(), new byte[16]);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device1 = new Device();
|
|
||||||
device1.setId(1L);
|
|
||||||
|
|
||||||
a1.addDevice(device1);
|
|
||||||
|
|
||||||
assertEquals(Optional.of("devices"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
Device device2 = new Device();
|
|
||||||
device2.setId(1L);
|
|
||||||
|
|
||||||
a2.addDevice(device2);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
device1.setLastSeen(1L);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
device1.setName("name");
|
|
||||||
|
|
||||||
assertEquals(Optional.of("devices"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
device1.setName(null);
|
|
||||||
|
|
||||||
device1.setSignedPreKey(new SignedPreKey(1L, "123", "456"));
|
|
||||||
device2.setSignedPreKey(new SignedPreKey(2L, "123", "456"));
|
|
||||||
|
|
||||||
assertEquals(Optional.of("masterDeviceSignedPreKey"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
device1.setSignedPreKey(null);
|
|
||||||
device2.setSignedPreKey(null);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
device1.setApnId("123");
|
|
||||||
Thread.sleep(5);
|
|
||||||
device2.setApnId("123");
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
a1.removeDevice(1L);
|
|
||||||
a2.removeDevice(1L);
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(Optional.empty(), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
a1.setVersion(1);
|
|
||||||
|
|
||||||
assertEquals(Optional.of("version"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
|
|
||||||
a2.setVersion(1);
|
|
||||||
|
|
||||||
a2.setProfileName("name");
|
|
||||||
|
|
||||||
assertEquals(Optional.of("profileName"), accountsManager.compareAccounts(Optional.of(a1), Optional.of(a2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCreateFreshAccount() throws InterruptedException {
|
void testCreateFreshAccount() throws InterruptedException {
|
||||||
when(accountsDynamoDb.create(any())).thenReturn(true);
|
when(accounts.create(any())).thenReturn(true);
|
||||||
|
|
||||||
final String e164 = "+18005550123";
|
final String e164 = "+18005550123";
|
||||||
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, null);
|
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, null);
|
||||||
accountsManager.create(e164, "password", null, attributes);
|
accountsManager.create(e164, "password", null, attributes);
|
||||||
|
|
||||||
verify(accountsDynamoDb).create(argThat(account -> e164.equals(account.getNumber())));
|
verify(accounts).create(argThat(account -> e164.equals(account.getNumber())));
|
||||||
verifyNoInteractions(keys);
|
verifyNoInteractions(keys);
|
||||||
verifyNoInteractions(messagesManager);
|
verifyNoInteractions(messagesManager);
|
||||||
verifyNoInteractions(profilesManager);
|
verifyNoInteractions(profilesManager);
|
||||||
|
@ -493,7 +342,7 @@ class AccountsManagerTest {
|
||||||
void testReregisterAccount() throws InterruptedException {
|
void testReregisterAccount() throws InterruptedException {
|
||||||
final UUID existingUuid = UUID.randomUUID();
|
final UUID existingUuid = UUID.randomUUID();
|
||||||
|
|
||||||
when(accountsDynamoDb.create(any())).thenAnswer(invocation -> {
|
when(accounts.create(any())).thenAnswer(invocation -> {
|
||||||
invocation.getArgument(0, Account.class).setUuid(existingUuid);
|
invocation.getArgument(0, Account.class).setUuid(existingUuid);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -502,7 +351,7 @@ class AccountsManagerTest {
|
||||||
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, null);
|
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, null);
|
||||||
accountsManager.create(e164, "password", null, attributes);
|
accountsManager.create(e164, "password", null, attributes);
|
||||||
|
|
||||||
verify(accountsDynamoDb).create(
|
verify(accounts).create(
|
||||||
argThat(account -> e164.equals(account.getNumber()) && existingUuid.equals(account.getUuid())));
|
argThat(account -> e164.equals(account.getNumber()) && existingUuid.equals(account.getUuid())));
|
||||||
verify(keys).delete(existingUuid);
|
verify(keys).delete(existingUuid);
|
||||||
verify(messagesManager).clear(existingUuid);
|
verify(messagesManager).clear(existingUuid);
|
||||||
|
@ -519,13 +368,13 @@ class AccountsManagerTest {
|
||||||
return null;
|
return null;
|
||||||
}).when(deletedAccountsManager).lockAndTake(anyString(), any());
|
}).when(deletedAccountsManager).lockAndTake(anyString(), any());
|
||||||
|
|
||||||
when(accountsDynamoDb.create(any())).thenReturn(true);
|
when(accounts.create(any())).thenReturn(true);
|
||||||
|
|
||||||
final String e164 = "+18005550123";
|
final String e164 = "+18005550123";
|
||||||
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, null);
|
final AccountAttributes attributes = new AccountAttributes(false, 0, null, null, true, null);
|
||||||
accountsManager.create(e164, "password", null, attributes);
|
accountsManager.create(e164, "password", null, attributes);
|
||||||
|
|
||||||
verify(accountsDynamoDb).create(
|
verify(accounts).create(
|
||||||
argThat(account -> e164.equals(account.getNumber()) && recentlyDeletedUuid.equals(account.getUuid())));
|
argThat(account -> e164.equals(account.getNumber()) && recentlyDeletedUuid.equals(account.getUuid())));
|
||||||
verifyNoInteractions(keys);
|
verifyNoInteractions(keys);
|
||||||
verifyNoInteractions(messagesManager);
|
verifyNoInteractions(messagesManager);
|
||||||
|
@ -596,7 +445,7 @@ class AccountsManagerTest {
|
||||||
accountsManager.updateDeviceLastSeen(account, device, updatedLastSeen);
|
accountsManager.updateDeviceLastSeen(account, device, updatedLastSeen);
|
||||||
|
|
||||||
assertEquals(expectUpdate ? updatedLastSeen : initialLastSeen, device.getLastSeen());
|
assertEquals(expectUpdate ? updatedLastSeen : initialLastSeen, device.getLastSeen());
|
||||||
verify(accountsDynamoDb, expectUpdate ? times(1) : never()).update(account);
|
verify(accounts, expectUpdate ? times(1) : never()).update(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
|
Loading…
Reference in New Issue