Reconcile inactive and undiscoverable accounts when using v3 endpoints

This commit is contained in:
Chris Eager
2021-07-28 11:27:23 -05:00
committed by Chris Eager
parent 331ff83cd5
commit df9c0051c9
7 changed files with 173 additions and 121 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -46,14 +46,13 @@ public class DirectoryQueueTest {
@ParameterizedTest
@MethodSource("argumentsForTestRefreshRegisteredUser")
void testRefreshRegisteredUser(final boolean accountEnabled, final boolean accountDiscoverableByPhoneNumber, final String expectedAction) {
void testRefreshRegisteredUser(final boolean shouldBeVisibleInDirectory, final String expectedAction) {
final DirectoryQueue directoryQueue = new DirectoryQueue(List.of("sqs://test"), sqsAsyncClient);
final Account account = mock(Account.class);
when(account.getNumber()).thenReturn("+18005556543");
when(account.getUuid()).thenReturn(UUID.randomUUID());
when(account.isEnabled()).thenReturn(accountEnabled);
when(account.isDiscoverableByPhoneNumber()).thenReturn(accountDiscoverableByPhoneNumber);
when(account.shouldBeVisibleInDirectory()).thenReturn(shouldBeVisibleInDirectory);
directoryQueue.refreshAccount(account);
@@ -67,10 +66,8 @@ public class DirectoryQueueTest {
@SuppressWarnings("unused")
private static Stream<Arguments> argumentsForTestRefreshRegisteredUser() {
return Stream.of(
Arguments.of(true, true, "add"),
Arguments.of(true, false, "delete"),
Arguments.of(false, true, "delete"),
Arguments.of(false, false, "delete"));
Arguments.of(true, "add"),
Arguments.of(false, "delete"));
}
@Test
@@ -80,8 +77,7 @@ public class DirectoryQueueTest {
final Account account = mock(Account.class);
when(account.getNumber()).thenReturn("+18005556543");
when(account.getUuid()).thenReturn(UUID.randomUUID());
when(account.isEnabled()).thenReturn(true);
when(account.isDiscoverableByPhoneNumber()).thenReturn(true);
when(account.shouldBeVisibleInDirectory()).thenReturn(true);
directoryQueue.refreshAccount(account);
@@ -104,8 +100,7 @@ public class DirectoryQueueTest {
final Account account = mock(Account.class);
when(account.getNumber()).thenReturn("+18005556543");
when(account.getUuid()).thenReturn(UUID.randomUUID());
when(account.isEnabled()).thenReturn(true);
when(account.isDiscoverableByPhoneNumber()).thenReturn(true);
when(account.shouldBeVisibleInDirectory()).thenReturn(true);
directoryQueue.refreshAccount(account);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -617,14 +617,19 @@ class AccountsManagerTest {
@ParameterizedTest
@MethodSource
void testUpdateDirectoryQueue(final boolean discoverableBeforeUpdate, final boolean discoverableAfterUpdate, final boolean expectRefresh) {
void testUpdateDirectoryQueue(final boolean visibleBeforeUpdate, final boolean visibleAfterUpdate,
final boolean expectRefresh) {
final Account account = new Account("+14152222222", UUID.randomUUID(), new HashSet<>(), new byte[16]);
when(directoryQueue.isDiscoverable(any()))
.thenReturn(discoverableBeforeUpdate)
.thenReturn(discoverableAfterUpdate);
// this sets up the appropriate result for Account#shouldBeVisibleInDirectory
final Device device = new Device(Device.MASTER_ID, "device", "token", "salt", null, null, null, true, 1,
new SignedPreKey(1, "key", "sig"), 0, 0,
"OWT", 0, new DeviceCapabilities());
account.addDevice(device);
account.setDiscoverableByPhoneNumber(visibleBeforeUpdate);
final Account updatedAccount = accountsManager.update(account, a -> a.setProfileName("Hello I am a unit test"));
final Account updatedAccount = accountsManager.update(account,
a -> a.setDiscoverableByPhoneNumber(visibleAfterUpdate));
verify(directoryQueue, times(expectRefresh ? 1 : 0)).refreshAccount(updatedAccount);
}

View File

@@ -1,71 +1,92 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.tests.storage;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationRequest;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationRequest.User;
import org.whispersystems.textsecuregcm.entities.DirectoryReconciliationResponse;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountDatabaseCrawlerRestartException;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciler;
import org.whispersystems.textsecuregcm.storage.DirectoryReconciliationClient;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
class DirectoryReconcilerTest {
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
public class DirectoryReconcilerTest {
private static final UUID VALID_UUID = UUID.randomUUID();
private static final String VALID_NUMBERRR = "+14152222222";
private static final UUID INACTIVE_UUID = UUID.randomUUID();
private static final String INACTIVE_NUMBERRR = "+14151111111";
private static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
private static final UUID VALID_UUID = UUID.randomUUID();
private static final String VALID_NUMBER = "+14152222222";
private static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
private static final String UNDISCOVERABLE_NUMBER = "+14153333333";
private final Account activeAccount = mock(Account.class);
private final Account inactiveAccount = mock(Account.class);
private final Account undiscoverableAccount = mock(Account.class);
private final DirectoryReconciliationClient reconciliationClient = mock(DirectoryReconciliationClient.class);
private final DirectoryReconciler directoryReconciler = new DirectoryReconciler("test", reconciliationClient);
private final Account visibleAccount = mock(Account.class);
private final Account undiscoverableAccount = mock(Account.class);
private final DirectoryReconciliationClient reconciliationClient = mock(DirectoryReconciliationClient.class);
private final DirectoryReconciler directoryReconciler = new DirectoryReconciler("test", reconciliationClient);
private final DirectoryReconciliationResponse successResponse = new DirectoryReconciliationResponse(DirectoryReconciliationResponse.Status.OK);
@Before
public void setup() {
when(activeAccount.getUuid()).thenReturn(VALID_UUID);
when(activeAccount.isEnabled()).thenReturn(true);
when(activeAccount.getNumber()).thenReturn(VALID_NUMBERRR);
when(activeAccount.isDiscoverableByPhoneNumber()).thenReturn(true);
when(inactiveAccount.getUuid()).thenReturn(INACTIVE_UUID);
when(inactiveAccount.getNumber()).thenReturn(INACTIVE_NUMBERRR);
when(inactiveAccount.isEnabled()).thenReturn(false);
when(inactiveAccount.isDiscoverableByPhoneNumber()).thenReturn(true);
@BeforeEach
void setup() {
when(visibleAccount.getUuid()).thenReturn(VALID_UUID);
when(visibleAccount.getNumber()).thenReturn(VALID_NUMBER);
when(visibleAccount.shouldBeVisibleInDirectory()).thenReturn(true);
when(undiscoverableAccount.getUuid()).thenReturn(UNDISCOVERABLE_UUID);
when(undiscoverableAccount.getNumber()).thenReturn(UNDISCOVERABLE_NUMBER);
when(undiscoverableAccount.isEnabled()).thenReturn(true);
when(undiscoverableAccount.isDiscoverableByPhoneNumber()).thenReturn(false);
when(undiscoverableAccount.shouldBeVisibleInDirectory()).thenReturn(false);
}
@Test
public void testCrawlChunkValid() throws AccountDatabaseCrawlerRestartException {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testCrawlChunkValid(final boolean useV3Endpoints) throws AccountDatabaseCrawlerRestartException {
directoryReconciler.setUseV3Endpoints(useV3Endpoints);
when(reconciliationClient.sendChunk(any())).thenReturn(successResponse);
directoryReconciler.timeAndProcessCrawlChunk(Optional.of(VALID_UUID), Arrays.asList(activeAccount, inactiveAccount, undiscoverableAccount));
when(reconciliationClient.sendChunkV3(any())).thenReturn(successResponse);
when(reconciliationClient.delete(any())).thenReturn(successResponse);
ArgumentCaptor<DirectoryReconciliationRequest> request = ArgumentCaptor.forClass(DirectoryReconciliationRequest.class);
verify(reconciliationClient, times(1)).sendChunk(request.capture());
directoryReconciler.timeAndProcessCrawlChunk(Optional.of(VALID_UUID),
Arrays.asList(visibleAccount, undiscoverableAccount));
assertThat(request.getValue().getFromUuid()).isEqualTo(VALID_UUID);
assertThat(request.getValue().getToUuid()).isEqualTo(UNDISCOVERABLE_UUID);
assertThat(request.getValue().getUsers()).isEqualTo(Arrays.asList(new DirectoryReconciliationRequest.User(VALID_UUID, VALID_NUMBERRR)));
ArgumentCaptor<DirectoryReconciliationRequest> chunkRequest = ArgumentCaptor.forClass(
DirectoryReconciliationRequest.class);
if (useV3Endpoints) {
verify(reconciliationClient, times(1)).sendChunkV3(chunkRequest.capture());
} else {
verify(reconciliationClient, times(1)).sendChunk(chunkRequest.capture());
}
assertThat(chunkRequest.getValue().getFromUuid()).isEqualTo(VALID_UUID);
assertThat(chunkRequest.getValue().getToUuid()).isEqualTo(UNDISCOVERABLE_UUID);
assertThat(chunkRequest.getValue().getUsers()).isEqualTo(List.of(new User(VALID_UUID, VALID_NUMBER)));
if (useV3Endpoints) {
ArgumentCaptor<DirectoryReconciliationRequest> deletesRequest = ArgumentCaptor.forClass(DirectoryReconciliationRequest.class);
verify(reconciliationClient, times(1)).delete(deletesRequest.capture());
assertThat(deletesRequest.getValue().getUsers()).isEqualTo(
List.of(new User(UNDISCOVERABLE_UUID, UNDISCOVERABLE_NUMBER)));
}
verifyNoMoreInteractions(reconciliationClient);
}
}