From 1dd7d33e23a5b3c10dad13dd73ed178a70bb9d67 Mon Sep 17 00:00:00 2001 From: Jon Chambers <63609320+jon-signal@users.noreply.github.com> Date: Wed, 13 Jul 2022 13:55:20 -0400 Subject: [PATCH] Simplify `Device` entity --- .../textsecuregcm/storage/Account.java | 30 +--- .../textsecuregcm/storage/Device.java | 35 ---- .../storage/PushFeedbackProcessor.java | 4 +- ...blementRefreshRequirementProviderTest.java | 4 +- .../auth/BaseAccountAuthenticatorTest.java | 28 +-- ...rChangeRefreshRequirementProviderTest.java | 32 ++-- .../controllers/MessageControllerTest.java | 48 +++--- ...ntsManagerChangeNumberIntegrationTest.java | 3 - ...ConcurrentModificationIntegrationTest.java | 11 +- .../storage/AccountsManagerTest.java | 72 ++++---- .../textsecuregcm/storage/AccountsTest.java | 75 +++----- .../storage/ChangeNumberManagerTest.java | 28 ++- .../textsecuregcm/storage/DeviceTest.java | 19 +- .../controllers/AccountControllerTest.java | 9 +- .../controllers/DeviceControllerTest.java | 6 +- .../tests/controllers/KeysControllerTest.java | 11 +- .../tests/storage/AccountTest.java | 163 +++++++++--------- .../storage/PushFeedbackProcessorTest.java | 10 +- .../tests/util/AccountsHelper.java | 15 ++ .../tests/util/DevicesHelper.java | 10 +- .../tests/util/MessageValidationTest.java | 4 +- .../websocket/WebSocketConnectionTest.java | 18 +- 22 files changed, 281 insertions(+), 354 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java index 7357204d0..18d02560b 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java @@ -7,15 +7,12 @@ package org.whispersystems.textsecuregcm.storage; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; import java.time.Clock; import java.time.Instant; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.function.Predicate; import javax.annotation.Nullable; @@ -46,7 +43,7 @@ public class Account { private String username; @JsonProperty - private Set devices = new HashSet<>(); + private List devices = new ArrayList<>(); @JsonProperty private String identityKey; @@ -84,17 +81,6 @@ public class Account { @JsonIgnore private boolean canonicallyDiscoverable; - public Account() {} - - @VisibleForTesting - public Account(String number, UUID uuid, final UUID phoneNumberIdentifier, Set devices, byte[] unidentifiedAccessKey) { - this.number = number; - this.uuid = uuid; - this.phoneNumberIdentifier = phoneNumberIdentifier; - this.devices = devices; - this.unidentifiedAccessKey = unidentifiedAccessKey; - } - public UUID getUuid() { // this is the one method that may be called on a stale account return uuid; @@ -150,17 +136,17 @@ public class Account { public void addDevice(Device device) { requireNotStale(); - this.devices.remove(device); + removeDevice(device.getId()); this.devices.add(device); } public void removeDevice(long deviceId) { requireNotStale(); - this.devices.remove(new Device(deviceId, null, null, null, null, null, null, false, 0, null, 0, 0, "NA", 0, null)); + this.devices.removeIf(device -> device.getId() == deviceId); } - public Set getDevices() { + public List getDevices() { requireNotStale(); return devices; @@ -175,13 +161,7 @@ public class Account { public Optional getDevice(long deviceId) { requireNotStale(); - for (Device device : devices) { - if (device.getId() == deviceId) { - return Optional.of(device); - } - } - - return Optional.empty(); + return devices.stream().filter(device -> device.getId() == deviceId).findFirst(); } public boolean isGroupsV2Supported() { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java index 1b43fee6e..4a0d9b3fd 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Device.java @@ -67,31 +67,6 @@ public class Device { @JsonProperty private DeviceCapabilities capabilities; - public Device() {} - - public Device(long id, String name, String authToken, String salt, - String gcmId, String apnId, - String voipApnId, boolean fetchesMessages, - int registrationId, SignedPreKey signedPreKey, - long lastSeen, long created, String userAgent, - long uninstalledFeedback, DeviceCapabilities capabilities) { - this.id = id; - this.name = name; - this.authToken = authToken; - this.salt = salt; - this.gcmId = gcmId; - this.apnId = apnId; - this.voipApnId = voipApnId; - this.fetchesMessages = fetchesMessages; - this.registrationId = registrationId; - this.signedPreKey = signedPreKey; - this.lastSeen = lastSeen; - this.created = created; - this.userAgent = userAgent; - this.uninstalledFeedback = uninstalledFeedback; - this.capabilities = capabilities; - } - public String getApnId() { return apnId; } @@ -251,16 +226,6 @@ public class Device { return groupsV2Supported; } - @Override - public boolean equals(Object other) { - return (other instanceof Device that) && this.id == that.id; - } - - @Override - public int hashCode() { - return (int)this.id; - } - public static class DeviceCapabilities { @JsonProperty private boolean gv2; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java index e31a11c5e..0837032c8 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/PushFeedbackProcessor.java @@ -12,7 +12,6 @@ import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.whispersystems.textsecuregcm.util.Constants; @@ -41,8 +40,7 @@ public class PushFeedbackProcessor extends AccountDatabaseCrawlerListener { for (Account account : chunkAccounts) { boolean update = false; - final Set devices = account.getDevices(); - for (Device device : devices) { + for (Device device : account.getDevices()) { if (deviceNeedsUpdate(device)) { if (deviceExpired(device)) { if (device.isEnabled()) { diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/auth/AuthEnablementRefreshRequirementProviderTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/auth/AuthEnablementRefreshRequirementProviderTest.java index c9f0e947e..68c6597b7 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/auth/AuthEnablementRefreshRequirementProviderTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/auth/AuthEnablementRefreshRequirementProviderTest.java @@ -32,9 +32,9 @@ import io.dropwizard.testing.junit5.ResourceExtension; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.Principal; +import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -141,7 +141,7 @@ class AuthEnablementRefreshRequirementProviderTest { final Account account = mock(Account.class); - final Set devices = new HashSet<>(); + final List devices = new ArrayList<>(); when(account.getDevices()).thenReturn(devices); LongStream.range(1, 5) diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/auth/BaseAccountAuthenticatorTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/auth/BaseAccountAuthenticatorTest.java index f3cb36ed4..a3973f27f 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/auth/BaseAccountAuthenticatorTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/auth/BaseAccountAuthenticatorTest.java @@ -17,14 +17,13 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.dropwizard.auth.basic.BasicCredentials; import java.time.Clock; import java.time.Instant; +import java.util.List; import java.util.Optional; -import java.util.Random; -import java.util.Set; import java.util.UUID; import java.util.stream.Stream; -import io.dropwizard.auth.basic.BasicCredentials; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -35,12 +34,10 @@ import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; -import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import org.whispersystems.textsecuregcm.util.Pair; class BaseAccountAuthenticatorTest { - private final Random random = new Random(867_5309L); private final long today = 1590451200000L; private final long yesterday = today - 86_400_000L; private final long oldTime = yesterday - 86_400_000L; @@ -59,19 +56,22 @@ class BaseAccountAuthenticatorTest { clock = mock(Clock.class); baseAccountAuthenticator = new BaseAccountAuthenticator(accountsManager, clock); - acct1 = new Account("+14088675309", AuthHelper.getRandomUUID(random), UUID.randomUUID(), - Set.of(new Device(1, null, null, null, - null, null, null, false, 0, null, yesterday, 0, null, 0, null)), null); - acct2 = new Account("+14098675309", AuthHelper.getRandomUUID(random), UUID.randomUUID(), - Set.of(new Device(1, null, null, null, - null, null, null, false, 0, null, yesterday, 0, null, 0, null)), null); - oldAccount = new Account("+14108675309", AuthHelper.getRandomUUID(random), UUID.randomUUID(), - Set.of(new Device(1, null, null, null, - null, null, null, false, 0, null, oldTime, 0, null, 0, null)), null); + // We use static UUIDs here because the UUID affects the "date last seen" offset + acct1 = AccountsHelper.generateTestAccount("+14088675309", UUID.fromString("c139cb3e-f70c-4460-b221-815e8bdf778f"), UUID.randomUUID(), List.of(generateTestDevice(yesterday)), null); + acct2 = AccountsHelper.generateTestAccount("+14088675310", UUID.fromString("30018a41-2764-4bc7-a935-775dfef84ad1"), UUID.randomUUID(), List.of(generateTestDevice(yesterday)), null); + oldAccount = AccountsHelper.generateTestAccount("+14088675311", UUID.fromString("adfce52b-9299-4c25-9c51-412fb420c6a6"), UUID.randomUUID(), List.of(generateTestDevice(oldTime)), null); AccountsHelper.setupMockUpdate(accountsManager); } + private static Device generateTestDevice(final long lastSeen) { + final Device device = new Device(); + device.setId(Device.MASTER_ID); + device.setLastSeen(lastSeen); + + return device; + } + @Test void testUpdateLastSeenMiddleOfDay() { when(clock.instant()).thenReturn(Instant.ofEpochMilli(currentTime)); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/auth/PhoneNumberChangeRefreshRequirementProviderTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/auth/PhoneNumberChangeRefreshRequirementProviderTest.java index 57c0d8dab..5e04da700 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/auth/PhoneNumberChangeRefreshRequirementProviderTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/auth/PhoneNumberChangeRefreshRequirementProviderTest.java @@ -5,6 +5,20 @@ package org.whispersystems.textsecuregcm.auth; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nullable; +import javax.ws.rs.core.SecurityContext; import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.monitoring.RequestEvent; import org.junit.jupiter.api.BeforeEach; @@ -13,22 +27,6 @@ import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.util.Pair; -import javax.annotation.Nullable; -import javax.ws.rs.core.SecurityContext; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - class PhoneNumberChangeRefreshRequirementProviderTest { private PhoneNumberChangeRefreshRequirementProvider provider; @@ -50,7 +48,7 @@ class PhoneNumberChangeRefreshRequirementProviderTest { when(account.getUuid()).thenReturn(ACCOUNT_UUID); when(account.getNumber()).thenReturn(NUMBER); - when(account.getDevices()).thenReturn(Set.of(device)); + when(account.getDevices()).thenReturn(List.of(device)); when(device.getId()).thenReturn(Device.MASTER_ID); request = mock(ContainerRequest.class); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/MessageControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/MessageControllerTest.java index 17fa2d66c..0df7b654a 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/MessageControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/MessageControllerTest.java @@ -35,7 +35,6 @@ import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Base64; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -80,6 +79,7 @@ import org.whispersystems.textsecuregcm.storage.DeletedAccountsManager; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.MessagesManager; import org.whispersystems.textsecuregcm.storage.ReportMessageManager; +import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import org.whispersystems.textsecuregcm.util.SystemMapper; @@ -126,31 +126,19 @@ class MessageControllerTest { @BeforeEach void setup() { - Set singleDeviceList = new HashSet<>() {{ - add(new Device(1, null, "foo", "bar", - "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), - System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, true, false, - false, false, false, false, false, false))); - }}; + final List singleDeviceList = List.of( + generateTestDevice(1, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis()) + ); - Set multiDeviceList = new HashSet<>() {{ - add(new Device(1, null, "foo", "bar", - "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), - System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false, - false, false, false, false, false, false))); - add(new Device(2, null, "foo", "bar", - "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), - System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, false, false, true, false, false, - false, false, false, false, false, false))); - add(new Device(3, null, "foo", "bar", - "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), - System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false, false, false, false, false, false, - false, false, false, false, false, false))); - }}; + final List multiDeviceList = List.of( + generateTestDevice(1, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis()), + generateTestDevice(2, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis()), + generateTestDevice(3, 444, null, System.currentTimeMillis(), System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31)) + ); - Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, SINGLE_DEVICE_PNI, singleDeviceList, "1234".getBytes()); - Account multiDeviceAccount = new Account(MULTI_DEVICE_RECIPIENT, MULTI_DEVICE_UUID, UUID.randomUUID(), multiDeviceList, "1234".getBytes()); - internationalAccount = new Account(INTERNATIONAL_RECIPIENT, INTERNATIONAL_UUID, UUID.randomUUID(), singleDeviceList, "1234".getBytes()); + Account singleDeviceAccount = AccountsHelper.generateTestAccount(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, SINGLE_DEVICE_PNI, singleDeviceList, "1234".getBytes()); + Account multiDeviceAccount = AccountsHelper.generateTestAccount(MULTI_DEVICE_RECIPIENT, MULTI_DEVICE_UUID, UUID.randomUUID(), multiDeviceList, "1234".getBytes()); + internationalAccount = AccountsHelper.generateTestAccount(INTERNATIONAL_RECIPIENT, INTERNATIONAL_UUID, UUID.randomUUID(), singleDeviceList, "1234".getBytes()); when(accountsManager.getByAccountIdentifier(eq(SINGLE_DEVICE_UUID))).thenReturn(Optional.of(singleDeviceAccount)); when(accountsManager.getByPhoneNumberIdentifier(SINGLE_DEVICE_PNI)).thenReturn(Optional.of(singleDeviceAccount)); @@ -160,6 +148,18 @@ class MessageControllerTest { when(rateLimiters.getMessagesLimiter()).thenReturn(rateLimiter); } + private static Device generateTestDevice(final long id, final int registrationId, final SignedPreKey signedPreKey, final long createdAt, final long lastSeen) { + final Device device = new Device(); + device.setId(id); + device.setRegistrationId(registrationId); + device.setSignedPreKey(signedPreKey); + device.setCreated(createdAt); + device.setLastSeen(lastSeen); + device.setGcmId("isgcm"); + + return device; + } + @AfterEach void teardown() { reset( diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java index 5169e6df8..155b1394e 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerChangeNumberIntegrationTest.java @@ -28,7 +28,6 @@ import org.whispersystems.textsecuregcm.redis.RedisClusterExtension; import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; -import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities; import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; @@ -256,8 +255,6 @@ class AccountsManagerChangeNumberIntegrationTest { final Account existingAccount = accountsManager.create(secondNumber, "password", null, new AccountAttributes(), new ArrayList<>()); final UUID existingAccountUuid = existingAccount.getUuid(); - accountsManager.update(existingAccount, a -> a.addDevice(new Device(Device.MASTER_ID, "test", "token", "salt", null, null, null, true, 1, null, 0, 0, null, 0, new DeviceCapabilities()))); - accountsManager.changeNumber(account, secondNumber); assertTrue(accountsManager.getByE164(originalNumber).isEmpty()); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java index 1ac52288b..b580e8e0a 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerConcurrentModificationIntegrationTest.java @@ -46,6 +46,7 @@ import org.whispersystems.textsecuregcm.push.ClientPresenceManager; import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; +import org.whispersystems.textsecuregcm.tests.util.DevicesHelper; import org.whispersystems.textsecuregcm.tests.util.JsonHelpers; import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper; import org.whispersystems.textsecuregcm.util.Pair; @@ -183,15 +184,7 @@ class AccountsManagerConcurrentModificationIntegrationTest { "testSignature-" + random.nextInt()); a.removeDevice(1); - a.addDevice(new Device(1, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), - "testSalt-" + random.nextInt(), - "testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), - random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), - "testUserAgent-" + random.nextInt(), 0, - new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), - random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), - random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), - random.nextBoolean()))); + a.addDevice(DevicesHelper.createDevice(1)); }); uuid = account.getUuid(); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java index b11e86ffe..e805b3399 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java @@ -30,7 +30,6 @@ import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import java.time.Clock; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -53,6 +52,7 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient; import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient; import org.whispersystems.textsecuregcm.sqs.DirectoryQueue; import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities; +import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper; class AccountsManagerTest { @@ -231,7 +231,7 @@ class AccountsManagerTest { void testGetAccountByNumberNotInCache() { UUID uuid = UUID.randomUUID(); UUID pni = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]); when(commands.get(eq("AccountMap::+14152222222"))).thenReturn(null); when(accounts.getByE164(eq("+14152222222"))).thenReturn(Optional.of(account)); @@ -255,7 +255,7 @@ class AccountsManagerTest { void testGetAccountByUuidNotInCache() { UUID uuid = UUID.randomUUID(); UUID pni = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]); when(commands.get(eq("Account3::" + uuid))).thenReturn(null); when(accounts.getByAccountIdentifier(eq(uuid))).thenReturn(Optional.of(account)); @@ -280,7 +280,7 @@ class AccountsManagerTest { UUID uuid = UUID.randomUUID(); UUID pni = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]); when(commands.get(eq("AccountMap::" + pni))).thenReturn(null); when(accounts.getByPhoneNumberIdentifier(pni)).thenReturn(Optional.of(account)); @@ -305,7 +305,7 @@ class AccountsManagerTest { UUID uuid = UUID.randomUUID(); String username = "test"; - Account account = new Account("+14152222222", uuid, UUID.randomUUID(), new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]); account.setUsername(username); when(commands.get(eq("AccountMap::" + username))).thenReturn(null); @@ -331,7 +331,7 @@ class AccountsManagerTest { void testGetAccountByNumberBrokenCache() { UUID uuid = UUID.randomUUID(); UUID pni = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]); when(commands.get(eq("AccountMap::+14152222222"))).thenThrow(new RedisException("Connection lost!")); when(accounts.getByE164(eq("+14152222222"))).thenReturn(Optional.of(account)); @@ -355,7 +355,7 @@ class AccountsManagerTest { void testGetAccountByUuidBrokenCache() { UUID uuid = UUID.randomUUID(); UUID pni = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]); when(commands.get(eq("Account3::" + uuid))).thenThrow(new RedisException("Connection lost!")); when(accounts.getByAccountIdentifier(eq(uuid))).thenReturn(Optional.of(account)); @@ -380,7 +380,7 @@ class AccountsManagerTest { UUID uuid = UUID.randomUUID(); UUID pni = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]); when(commands.get(eq("AccountMap::" + pni))).thenThrow(new RedisException("OH NO")); when(accounts.getByPhoneNumberIdentifier(pni)).thenReturn(Optional.of(account)); @@ -405,7 +405,7 @@ class AccountsManagerTest { UUID uuid = UUID.randomUUID(); String username = "test"; - Account account = new Account("+14152222222", uuid, UUID.randomUUID(), new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]); account.setUsername(username); when(commands.get(eq("AccountMap::" + username))).thenThrow(new RedisException("OH NO")); @@ -431,18 +431,18 @@ class AccountsManagerTest { void testUpdate_optimisticLockingFailure() { UUID uuid = UUID.randomUUID(); UUID pni = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]); when(commands.get(eq("Account3::" + uuid))).thenReturn(null); when(accounts.getByAccountIdentifier(uuid)).thenReturn( - Optional.of(new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]))); + Optional.of(AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]))); doThrow(ContestedOptimisticLockException.class) .doAnswer(ACCOUNT_UPDATE_ANSWER) .when(accounts).update(any()); when(accounts.getByAccountIdentifier(uuid)).thenReturn( - Optional.of(new Account("+14152222222", uuid, pni, new HashSet<>(), new byte[16]))); + Optional.of(AccountsHelper.generateTestAccount("+14152222222", uuid, pni, new ArrayList<>(), new byte[16]))); doThrow(ContestedOptimisticLockException.class) .doAnswer(ACCOUNT_UPDATE_ANSWER) .when(accounts).update(any()); @@ -460,7 +460,7 @@ class AccountsManagerTest { @Test void testUpdate_dynamoOptimisticLockingFailureDuringCreate() { UUID uuid = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, UUID.randomUUID(), new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]); when(commands.get(eq("Account3::" + uuid))).thenReturn(null); when(accounts.getByAccountIdentifier(uuid)).thenReturn(Optional.empty()) @@ -477,10 +477,10 @@ class AccountsManagerTest { @Test void testUpdateDevice() { final UUID uuid = UUID.randomUUID(); - Account account = new Account("+14152222222", uuid, UUID.randomUUID(), new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount("+14152222222", uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]); when(accounts.getByAccountIdentifier(uuid)).thenReturn( - Optional.of(new Account("+14152222222", uuid, UUID.randomUUID(), new HashSet<>(), new byte[16]))); + Optional.of(AccountsHelper.generateTestAccount("+14152222222", uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]))); assertTrue(account.getDevices().isEmpty()); @@ -596,12 +596,10 @@ class AccountsManagerTest { @MethodSource void testUpdateDirectoryQueue(final boolean visibleBeforeUpdate, final boolean visibleAfterUpdate, final boolean expectRefresh) { - final Account account = new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); + final Account account = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); // this sets up the appropriate result for Account#shouldBeVisibleInDirectory - final Device device = new Device(Device.MASTER_ID, "device", "token", "salt", null, null, null, true, 1, - new SignedPreKey(1, "key", "sig"), 0, 0, - "OWT", 0, new DeviceCapabilities()); + final Device device = generateTestDevice(0); account.addDevice(device); account.setDiscoverableByPhoneNumber(visibleBeforeUpdate); @@ -623,10 +621,8 @@ class AccountsManagerTest { @ParameterizedTest @MethodSource void testUpdateDeviceLastSeen(final boolean expectUpdate, final long initialLastSeen, final long updatedLastSeen) { - final Account account = new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); - final Device device = new Device(Device.MASTER_ID, "device", "token", "salt", null, null, null, true, 1, - new SignedPreKey(1, "key", "sig"), initialLastSeen, 0, - "OWT", 0, new DeviceCapabilities()); + final Account account = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); + final Device device = generateTestDevice(initialLastSeen); account.addDevice(device); accountsManager.updateDeviceLastSeen(account, device, updatedLastSeen); @@ -654,7 +650,7 @@ class AccountsManagerTest { final UUID uuid = UUID.randomUUID(); final UUID originalPni = UUID.randomUUID(); - Account account = new Account(originalNumber, uuid, originalPni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount(originalNumber, uuid, originalPni, new ArrayList<>(), new byte[16]); account = accountsManager.changeNumber(account, targetNumber); assertEquals(targetNumber, account.getNumber()); @@ -670,7 +666,7 @@ class AccountsManagerTest { void testChangePhoneNumberSameNumber() throws InterruptedException { final String number = "+14152222222"; - Account account = new Account(number, UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); account = accountsManager.changeNumber(account, number); assertEquals(number, account.getNumber()); @@ -691,10 +687,10 @@ class AccountsManagerTest { final UUID originalPni = UUID.randomUUID(); final UUID targetPni = UUID.randomUUID(); - final Account existingAccount = new Account(targetNumber, existingAccountUuid, targetPni, new HashSet<>(), new byte[16]); + final Account existingAccount = AccountsHelper.generateTestAccount(targetNumber, existingAccountUuid, targetPni, new ArrayList<>(), new byte[16]); when(accounts.getByE164(targetNumber)).thenReturn(Optional.of(existingAccount)); - Account account = new Account(originalNumber, uuid, originalPni, new HashSet<>(), new byte[16]); + Account account = AccountsHelper.generateTestAccount(originalNumber, uuid, originalPni, new ArrayList<>(), new byte[16]); account = accountsManager.changeNumber(account, targetNumber); assertEquals(targetNumber, account.getNumber()); @@ -713,14 +709,14 @@ class AccountsManagerTest { final String targetNumber = "+14153333333"; final UUID uuid = UUID.randomUUID(); - final Account account = new Account(originalNumber, uuid, UUID.randomUUID(), new HashSet<>(), new byte[16]); + final Account account = AccountsHelper.generateTestAccount(originalNumber, uuid, UUID.randomUUID(), new ArrayList<>(), new byte[16]); assertThrows(AssertionError.class, () -> accountsManager.update(account, a -> a.setNumber(targetNumber, UUID.randomUUID()))); } @Test void testSetUsername() throws UsernameNotAvailableException { - final Account account = new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); + final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); final String username = "test"; assertDoesNotThrow(() -> accountsManager.setUsername(account, username)); @@ -729,7 +725,7 @@ class AccountsManagerTest { @Test void testSetUsernameSameUsername() throws UsernameNotAvailableException { - final Account account = new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); + final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); final String username = "test"; account.setUsername(username); @@ -739,7 +735,7 @@ class AccountsManagerTest { @Test void testSetUsernameNotAvailable() throws UsernameNotAvailableException { - final Account account = new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); + final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); final String username = "test"; doThrow(new UsernameNotAvailableException()).when(accounts).setUsername(account, username); @@ -755,7 +751,7 @@ class AccountsManagerTest { final String username = "reserved"; when(reservedUsernames.isReserved(eq(username), any())).thenReturn(true); - final Account account = new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); + final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); assertThrows(UsernameNotAvailableException.class, () -> accountsManager.setUsername(account, username)); assertTrue(account.getUsername().isEmpty()); @@ -763,8 +759,18 @@ class AccountsManagerTest { @Test void testSetUsernameViaUpdate() { - final Account account = new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>(), new byte[16]); + final Account account = AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); assertThrows(AssertionError.class, () -> accountsManager.update(account, a -> a.setUsername("test"))); } + + private static Device generateTestDevice(final long lastSeen) { + final Device device = new Device(); + device.setId(Device.MASTER_ID); + device.setFetchesMessages(true); + device.setSignedPreKey(new SignedPreKey(1, "key", "sig")); + device.setLastSeen(lastSeen); + + return device; + } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java index 5bbc77541..6ac00be8a 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java @@ -21,12 +21,10 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Random; -import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -41,7 +39,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; -import org.whispersystems.textsecuregcm.entities.SignedPreKey; +import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; +import org.whispersystems.textsecuregcm.tests.util.DevicesHelper; import org.whispersystems.textsecuregcm.util.AttributeValues; import org.whispersystems.textsecuregcm.util.SystemMapper; import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; @@ -153,7 +152,7 @@ class AccountsTest { @Test void testStore() { Device device = generateDevice(1); - Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(device)); + Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), List.of(device)); boolean freshUser = accounts.create(account); @@ -173,11 +172,8 @@ class AccountsTest { @Test void testStoreMulti() { - Set devices = new HashSet<>(); - devices.add(generateDevice(1)); - devices.add(generateDevice(2)); - - Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), devices); + final List devices = List.of(generateDevice(1), generateDevice(2)); + final Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), devices); accounts.create(account); @@ -189,17 +185,13 @@ class AccountsTest { @Test void testRetrieve() { - Set devicesFirst = new HashSet<>(); - devicesFirst.add(generateDevice(1)); - devicesFirst.add(generateDevice(2)); + final List devicesFirst = List.of(generateDevice(1), generateDevice(2)); UUID uuidFirst = UUID.randomUUID(); UUID pniFirst = UUID.randomUUID(); Account accountFirst = generateAccount("+14151112222", uuidFirst, pniFirst, devicesFirst); - Set devicesSecond = new HashSet<>(); - devicesSecond.add(generateDevice(1)); - devicesSecond.add(generateDevice(2)); + final List devicesSecond = List.of(generateDevice(1), generateDevice(2)); UUID uuidSecond = UUID.randomUUID(); UUID pniSecond = UUID.randomUUID(); @@ -238,12 +230,8 @@ class AccountsTest { @Test void testRetrieveNoPni() throws JsonProcessingException { - final Set devices = new HashSet<>(); - devices.add(generateDevice(1)); - devices.add(generateDevice(2)); - + final List devices = List.of(generateDevice(1), generateDevice(2)); final UUID uuid = UUID.randomUUID(); - final Account account = generateAccount("+14151112222", uuid, null, devices); // Accounts#create enforces that newly-created accounts have a PNI, so we need to make a bit of an end-run around it @@ -303,7 +291,7 @@ class AccountsTest { Device device = generateDevice(1); UUID firstUuid = UUID.randomUUID(); UUID firstPni = UUID.randomUUID(); - Account account = generateAccount("+14151112222", firstUuid, firstPni, Collections.singleton(device)); + Account account = generateAccount("+14151112222", firstUuid, firstPni, List.of(device)); accounts.create(account); @@ -317,7 +305,7 @@ class AccountsTest { UUID secondUuid = UUID.randomUUID(); device = generateDevice(1); - account = generateAccount("+14151112222", secondUuid, UUID.randomUUID(), Collections.singleton(device)); + account = generateAccount("+14151112222", secondUuid, UUID.randomUUID(), List.of(device)); final boolean freshUser = accounts.create(account); assertThat(freshUser).isFalse(); @@ -327,7 +315,7 @@ class AccountsTest { assertPhoneNumberIdentifierConstraintExists(firstPni, firstUuid); device = generateDevice(1); - Account invalidAccount = generateAccount("+14151113333", firstUuid, UUID.randomUUID(), Collections.singleton(device)); + Account invalidAccount = generateAccount("+14151113333", firstUuid, UUID.randomUUID(), List.of(device)); assertThatThrownBy(() -> accounts.create(invalidAccount)); } @@ -335,7 +323,7 @@ class AccountsTest { @Test void testUpdate() { Device device = generateDevice (1 ); - Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(device)); + Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), List.of(device)); accounts.create(account); @@ -360,7 +348,7 @@ class AccountsTest { verifyStoredState("+14151112222", account.getUuid(), account.getPhoneNumberIdentifier(), account, true); device = generateDevice(1); - Account unknownAccount = generateAccount("+14151113333", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(device)); + Account unknownAccount = generateAccount("+14151113333", UUID.randomUUID(), UUID.randomUUID(), List.of(device)); assertThatThrownBy(() -> accounts.update(unknownAccount)).isInstanceOfAny(ConditionalCheckFailedException.class); @@ -454,10 +442,10 @@ class AccountsTest { void testDelete() { final Device deletedDevice = generateDevice(1); final Account deletedAccount = generateAccount("+14151112222", UUID.randomUUID(), - UUID.randomUUID(), Collections.singleton(deletedDevice)); + UUID.randomUUID(), List.of(deletedDevice)); final Device retainedDevice = generateDevice(1); final Account retainedAccount = generateAccount("+14151112345", UUID.randomUUID(), - UUID.randomUUID(), Collections.singleton(retainedDevice)); + UUID.randomUUID(), List.of(retainedDevice)); accounts.create(deletedAccount); accounts.create(retainedAccount); @@ -482,7 +470,7 @@ class AccountsTest { { final Account recreatedAccount = generateAccount(deletedAccount.getNumber(), UUID.randomUUID(), - UUID.randomUUID(), Collections.singleton(generateDevice(1))); + UUID.randomUUID(), List.of(generateDevice(1))); final boolean freshUser = accounts.create(recreatedAccount); @@ -499,7 +487,7 @@ class AccountsTest { @Test void testMissing() { Device device = generateDevice (1 ); - Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(device)); + Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), List.of(device)); accounts.create(account); @@ -567,7 +555,7 @@ class AccountsTest { @Test void testCanonicallyDiscoverableSet() { Device device = generateDevice(1); - Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(device)); + Account account = generateAccount("+14151112222", UUID.randomUUID(), UUID.randomUUID(), List.of(device)); account.setDiscoverableByPhoneNumber(false); accounts.create(account); verifyStoredState("+14151112222", account.getUuid(), account.getPhoneNumberIdentifier(), account, false); @@ -588,7 +576,7 @@ class AccountsTest { final UUID targetPni = UUID.randomUUID(); final Device device = generateDevice(1); - final Account account = generateAccount(originalNumber, UUID.randomUUID(), originalPni, Collections.singleton(device)); + final Account account = generateAccount(originalNumber, UUID.randomUUID(), originalPni, List.of(device)); accounts.create(account); @@ -634,10 +622,10 @@ class AccountsTest { final UUID targetPni = UUID.randomUUID(); final Device existingDevice = generateDevice(1); - final Account existingAccount = generateAccount(targetNumber, UUID.randomUUID(), targetPni, Collections.singleton(existingDevice)); + final Account existingAccount = generateAccount(targetNumber, UUID.randomUUID(), targetPni, List.of(existingDevice)); final Device device = generateDevice(1); - final Account account = generateAccount(originalNumber, UUID.randomUUID(), originalPni, Collections.singleton(device)); + final Account account = generateAccount(originalNumber, UUID.randomUUID(), originalPni, List.of(device)); accounts.create(account); accounts.create(existingAccount); @@ -656,7 +644,7 @@ class AccountsTest { final String targetNumber = "+14151113333"; final Device device = generateDevice(1); - final Account account = generateAccount(originalNumber, UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(device)); + final Account account = generateAccount(originalNumber, UUID.randomUUID(), UUID.randomUUID(), List.of(device)); accounts.create(account); @@ -952,31 +940,20 @@ class AccountsTest { } private Device generateDevice(long id) { - Random random = new Random(System.currentTimeMillis()); - SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), - "testSignature-" + random.nextInt()); - return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), - "testSalt-" + random.nextInt(), - "testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), - random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), - "testUserAgent-" + random.nextInt(), 0, - new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), - random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), - random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), - random.nextBoolean(), random.nextBoolean())); + return DevicesHelper.createDevice(id); } private Account generateAccount(String number, UUID uuid, final UUID pni) { Device device = generateDevice(1); - return generateAccount(number, uuid, pni, Collections.singleton(device)); + return generateAccount(number, uuid, pni, List.of(device)); } - private Account generateAccount(String number, UUID uuid, final UUID pni, Set devices) { + private Account generateAccount(String number, UUID uuid, final UUID pni, List devices) { byte[] unidentifiedAccessKey = new byte[16]; Random random = new Random(System.currentTimeMillis()); Arrays.fill(unidentifiedAccessKey, (byte)random.nextInt(255)); - return new Account(number, uuid, pni, devices, unidentifiedAccessKey); + return AccountsHelper.generateTestAccount(number, uuid, pni, devices, unidentifiedAccessKey); } private void assertPhoneNumberConstraintExists(final String number, final UUID uuid) { diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java index 7c8a25c34..5e1923e7e 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/ChangeNumberManagerTest.java @@ -4,6 +4,18 @@ */ package org.whispersystems.textsecuregcm.storage; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; import org.apache.commons.codec.binary.Base64; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -13,20 +25,6 @@ import org.whispersystems.textsecuregcm.entities.IncomingMessage; import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.push.MessageSender; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - public class ChangeNumberManagerTest { private static AccountsManager accountsManager = mock(AccountsManager.class); private static MessageSender messageSender = mock(MessageSender.class); @@ -40,7 +38,7 @@ public class ChangeNumberManagerTest { final String number = invocation.getArgument(1, String.class); final UUID uuid = account.getUuid(); - final Set devices = account.getDevices(); + final List devices = account.getDevices(); final Account updatedAccount = mock(Account.class); when(updatedAccount.getUuid()).thenReturn(uuid); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java index 600cfdc58..4b7645dfa 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/DeviceTest.java @@ -21,9 +21,17 @@ class DeviceTest { @MethodSource void testIsEnabled(final boolean master, final boolean fetchesMessages, final String apnId, final String gcmId, final SignedPreKey signedPreKey, final Duration timeSinceLastSeen, final boolean expectEnabled) { + final long lastSeen = System.currentTimeMillis() - timeSinceLastSeen.toMillis(); - final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt", gcmId, apnId, null, fetchesMessages, - 1, signedPreKey, lastSeen, lastSeen, "user-agent", 0, null); + + final Device device = new Device(); + device.setId(master ? Device.MASTER_ID : Device.MASTER_ID + 1); + device.setFetchesMessages(fetchesMessages); + device.setApnId(apnId); + device.setGcmId(gcmId); + device.setSignedPreKey(signedPreKey); + device.setCreated(lastSeen); + device.setLastSeen(lastSeen); assertEquals(expectEnabled, device.isEnabled()); } @@ -73,8 +81,11 @@ class DeviceTest { final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability, gv2_3Capability, false, false, false, false, false, false, false, false, false); - final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt", - null, apnId, null, false, 1, null, 0, 0, "user-agent", 0, capabilities); + + final Device device = new Device(); + device.setId(master ? Device.MASTER_ID : Device.MASTER_ID + 1); + device.setApnId(apnId); + device.setCapabilities(capabilities); assertEquals(expectGv2Supported, device.isGroupsV2Supported()); } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java index e0c15322d..be103dac2 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AccountControllerTest.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.security.SecureRandom; import java.time.Duration; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -259,7 +258,7 @@ class AccountControllerTest { final String number = invocation.getArgument(1, String.class); final UUID uuid = account.getUuid(); - final Set devices = account.getDevices(); + final List devices = account.getDevices(); final Account updatedAccount = mock(Account.class); when(updatedAccount.getUuid()).thenReturn(uuid); @@ -1557,7 +1556,7 @@ class AccountControllerTest { Device device3 = mock(Device.class); when(device3.getId()).thenReturn(3L); when(device3.isEnabled()).thenReturn(true); - when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(Set.of(AuthHelper.VALID_DEVICE, device2, device3)); + when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(List.of(AuthHelper.VALID_DEVICE, device2, device3)); when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of( new StoredVerificationCode(code, System.currentTimeMillis(), "push", null))); @@ -1591,7 +1590,7 @@ class AccountControllerTest { when(device3.getId()).thenReturn(3L); when(device3.isEnabled()).thenReturn(true); when(device3.getRegistrationId()).thenReturn(3); - when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(Set.of(AuthHelper.VALID_DEVICE, device2, device3)); + when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(List.of(AuthHelper.VALID_DEVICE, device2, device3)); when(AuthHelper.VALID_ACCOUNT.getDevice(2L)).thenReturn(Optional.of(device2)); when(AuthHelper.VALID_ACCOUNT.getDevice(3L)).thenReturn(Optional.of(device3)); when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of( @@ -1633,7 +1632,7 @@ class AccountControllerTest { when(device3.getId()).thenReturn(3L); when(device3.isEnabled()).thenReturn(true); when(device3.getRegistrationId()).thenReturn(3); - when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(Set.of(AuthHelper.VALID_DEVICE, device2, device3)); + when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(List.of(AuthHelper.VALID_DEVICE, device2, device3)); when(AuthHelper.VALID_ACCOUNT.getDevice(2L)).thenReturn(Optional.of(device2)); when(AuthHelper.VALID_ACCOUNT.getDevice(3L)).thenReturn(Optional.of(device3)); when(pendingAccountsManager.getCodeForNumber(number)).thenReturn(Optional.of( diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java index 854d84e0c..865d2ff6a 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java @@ -21,9 +21,9 @@ import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.ResourceExtension; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.stream.Stream; import javax.ws.rs.Path; import javax.ws.rs.client.Entity; @@ -162,7 +162,7 @@ class DeviceControllerTest { final Device existingDevice = mock(Device.class); when(existingDevice.getId()).thenReturn(Device.MASTER_ID); - when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(Set.of(existingDevice)); + when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(List.of(existingDevice)); VerificationCode deviceCode = resources.getJerseyTest() .target("/v1/devices/provisioning/code") @@ -194,7 +194,7 @@ class DeviceControllerTest { final Device existingDevice = mock(Device.class); when(existingDevice.getId()).thenReturn(Device.MASTER_ID); - when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(Set.of(existingDevice)); + when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(List.of(existingDevice)); VerificationCode deviceCode = resources.getJerseyTest() .target("/v1/devices/provisioning/code") diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java index cfbf8c1e1..4d8e7300c 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java @@ -25,11 +25,9 @@ import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.ResourceExtension; import java.time.Duration; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; @@ -115,12 +113,7 @@ class KeysControllerTest { final Device sampleDevice3 = mock(Device.class); final Device sampleDevice4 = mock(Device.class); - Set allDevices = new HashSet<>() {{ - add(sampleDevice); - add(sampleDevice2); - add(sampleDevice3); - add(sampleDevice4); - }}; + final List allDevices = List.of(sampleDevice, sampleDevice2, sampleDevice3, sampleDevice4); AccountsHelper.setupMockUpdate(accounts); @@ -351,7 +344,7 @@ class KeysControllerTest { @Test void testNoDevices() { - when(existsAccount.getDevices()).thenReturn(Collections.emptySet()); + when(existsAccount.getDevices()).thenReturn(Collections.emptyList()); Response result = resources.getJerseyTest() .target(String.format("/v2/keys/%s/*", EXISTS_UUID)) diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java index 33a5d0711..164a594e7 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/AccountTest.java @@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.tests.storage; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -19,8 +20,7 @@ import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Instant; import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; @@ -29,6 +29,7 @@ import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.AccountBadge; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities; +import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; class AccountTest { @@ -164,14 +165,17 @@ class AccountTest { new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false)); when(pniIncapableExpiredDevice.isEnabled()).thenReturn(false); + when(storiesCapableDevice.getId()).thenReturn(1L); when(storiesCapableDevice.getCapabilities()).thenReturn( new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, true, false)); when(storiesCapableDevice.isEnabled()).thenReturn(true); + when(storiesCapableDevice.getId()).thenReturn(2L); when(storiesIncapableDevice.getCapabilities()).thenReturn( new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false)); when(storiesIncapableDevice.isEnabled()).thenReturn(true); + when(storiesCapableDevice.getId()).thenReturn(3L); when(storiesIncapableExpiredDevice.getCapabilities()).thenReturn( new DeviceCapabilities(true, true, true, true, true, true, true, false, false, false, false, false)); when(storiesIncapableExpiredDevice.isEnabled()).thenReturn(false); @@ -204,33 +208,19 @@ class AccountTest { when(disabledMasterDevice.getId()).thenReturn(1L); when(disabledLinkedDevice.getId()).thenReturn(2L); - assertTrue( new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), Set.of(enabledMasterDevice), new byte[0]).isEnabled()); - assertTrue( new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), - Set.of(enabledMasterDevice, enabledLinkedDevice), new byte[0]).isEnabled()); - assertTrue( new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), - Set.of(enabledMasterDevice, disabledLinkedDevice), new byte[0]).isEnabled()); - assertFalse(new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), Set.of(disabledMasterDevice), new byte[0]).isEnabled()); - assertFalse(new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), - Set.of(disabledMasterDevice, enabledLinkedDevice), new byte[0]).isEnabled()); - assertFalse(new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), - Set.of(disabledMasterDevice, disabledLinkedDevice), new byte[0]).isEnabled()); + assertTrue(AccountsHelper.generateTestAccount("+14151234567", List.of(enabledMasterDevice)).isEnabled()); + assertTrue(AccountsHelper.generateTestAccount("+14151234567", List.of(enabledMasterDevice, enabledLinkedDevice)).isEnabled()); + assertTrue(AccountsHelper.generateTestAccount("+14151234567", List.of(enabledMasterDevice, disabledLinkedDevice)).isEnabled()); + assertFalse(AccountsHelper.generateTestAccount("+14151234567", List.of(disabledMasterDevice)).isEnabled()); + assertFalse(AccountsHelper.generateTestAccount("+14151234567", List.of(disabledMasterDevice, enabledLinkedDevice)).isEnabled()); + assertFalse(AccountsHelper.generateTestAccount("+14151234567", List.of(disabledMasterDevice, disabledLinkedDevice)).isEnabled()); } @Test void testCapabilities() { - Account uuidCapable = new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new HashSet() {{ - add(gv2CapableDevice); - }}, "1234".getBytes()); - - Account uuidIncapable = new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new HashSet() {{ - add(gv2CapableDevice); - add(gv2IncapableDevice); - }}, "1234".getBytes()); - - Account uuidCapableWithExpiredIncapable = new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new HashSet() {{ - add(gv2CapableDevice); - add(gv2IncapableExpiredDevice); - }}, "1234".getBytes()); + final Account uuidCapable = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice), "1234".getBytes()); + final Account uuidIncapable = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice, gv2IncapableDevice), "1234".getBytes()); + final Account uuidCapableWithExpiredIncapable = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice, gv2IncapableExpiredDevice), "1234".getBytes()); assertTrue(uuidCapable.isGroupsV2Supported()); assertFalse(uuidIncapable.isGroupsV2Supported()); @@ -263,23 +253,20 @@ class AccountTest { { final Account transferableMasterAccount = - new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(transferCapableMasterDevice), "1234".getBytes()); + AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(transferCapableMasterDevice), "1234".getBytes()); assertTrue(transferableMasterAccount.isTransferSupported()); } { final Account nonTransferableMasterAccount = - new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(nonTransferCapableMasterDevice), "1234".getBytes()); + AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(nonTransferCapableMasterDevice), "1234".getBytes()); assertFalse(nonTransferableMasterAccount.isTransferSupported()); } { - final Account transferableLinkedAccount = new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), new HashSet<>() {{ - add(nonTransferCapableMasterDevice); - add(transferCapableLinkedDevice); - }}, "1234".getBytes()); + final Account transferableLinkedAccount = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(nonTransferCapableMasterDevice, transferCapableLinkedDevice), "1234".getBytes()); assertFalse(transferableLinkedAccount.isTransferSupported()); } @@ -287,7 +274,7 @@ class AccountTest { @Test void testDiscoverableByPhoneNumber() { - final Account account = new Account("+14152222222", UUID.randomUUID(), UUID.randomUUID(), Collections.singleton(recentMasterDevice), + final Account account = AccountsHelper.generateTestAccount("+14152222222", UUID.randomUUID(), UUID.randomUUID(), List.of(recentMasterDevice), "1234".getBytes()); assertTrue(account.isDiscoverableByPhoneNumber(), @@ -302,111 +289,111 @@ class AccountTest { @Test void isGroupsV2Supported() { - assertTrue(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), Set.of(gv2CapableDevice), + assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), List.of(gv2CapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGroupsV2Supported()); - assertTrue(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), - Set.of(gv2CapableDevice, gv2IncapableExpiredDevice), + assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), + List.of(gv2CapableDevice, gv2IncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGroupsV2Supported()); - assertFalse(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), - Set.of(gv2CapableDevice, gv2IncapableDevice), + assertFalse(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), + List.of(gv2CapableDevice, gv2IncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGroupsV2Supported()); } @Test void isGv1MigrationSupported() { - assertTrue(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), Set.of(gv1MigrationCapableDevice), + assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), List.of(gv1MigrationCapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGv1MigrationSupported()); assertFalse( - new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), - Set.of(gv1MigrationCapableDevice, gv1MigrationIncapableDevice), + AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), + List.of(gv1MigrationCapableDevice, gv1MigrationIncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGv1MigrationSupported()); - assertTrue(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(gv1MigrationCapableDevice, gv1MigrationIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)) + assertTrue(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(gv1MigrationCapableDevice, gv1MigrationIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)) .isGv1MigrationSupported()); } @Test void isSenderKeySupported() { - assertThat(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), Set.of(senderKeyCapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), List.of(senderKeyCapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isSenderKeySupported()).isTrue(); - assertThat(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), - Set.of(senderKeyCapableDevice, senderKeyIncapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), + List.of(senderKeyCapableDevice, senderKeyIncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isSenderKeySupported()).isFalse(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(senderKeyCapableDevice, senderKeyIncapableExpiredDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(senderKeyCapableDevice, senderKeyIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)).isSenderKeySupported()).isTrue(); } @Test void isAnnouncementGroupSupported() { - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(announcementGroupCapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(announcementGroupCapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isAnnouncementGroupSupported()).isTrue(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(announcementGroupCapableDevice, announcementGroupIncapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(announcementGroupCapableDevice, announcementGroupIncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isAnnouncementGroupSupported()).isFalse(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(announcementGroupCapableDevice, announcementGroupIncapableExpiredDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(announcementGroupCapableDevice, announcementGroupIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)).isAnnouncementGroupSupported()).isTrue(); } @Test void isChangeNumberSupported() { - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(changeNumberCapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(changeNumberCapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isTrue(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(changeNumberCapableDevice, changeNumberIncapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(changeNumberCapableDevice, changeNumberIncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isFalse(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(changeNumberCapableDevice, changeNumberIncapableExpiredDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(changeNumberCapableDevice, changeNumberIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)).isChangeNumberSupported()).isTrue(); } @Test void isPniSupported() { - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(pniCapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(pniCapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isPniSupported()).isTrue(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(pniCapableDevice, pniIncapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(pniCapableDevice, pniIncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isPniSupported()).isFalse(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(pniCapableDevice, pniIncapableExpiredDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(pniCapableDevice, pniIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)).isPniSupported()).isTrue(); } @Test void isStoriesSupported() { - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(storiesCapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(storiesCapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isStoriesSupported()).isTrue(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(storiesCapableDevice, storiesIncapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(storiesCapableDevice, storiesIncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isStoriesSupported()).isTrue(); // TODO stories capability // "1234".getBytes(StandardCharsets.UTF_8)).isStoriesSupported()).isFalse(); - assertThat(new Account("+18005551234", UUID.randomUUID(), - UUID.randomUUID(), Set.of(storiesCapableDevice, storiesIncapableExpiredDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), + UUID.randomUUID(), List.of(storiesCapableDevice, storiesIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)).isStoriesSupported()).isTrue(); } @Test void isGiftBadgesSupported() { - assertThat(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), - Set.of(giftBadgesCapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), + List.of(giftBadgesCapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGiftBadgesSupported()).isTrue(); - assertThat(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), - Set.of(giftBadgesCapableDevice, giftBadgesIncapableDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), + List.of(giftBadgesCapableDevice, giftBadgesIncapableDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGiftBadgesSupported()).isFalse(); - assertThat(new Account("+18005551234", UUID.randomUUID(), UUID.randomUUID(), - Set.of(giftBadgesCapableDevice, giftBadgesIncapableExpiredDevice), + assertThat(AccountsHelper.generateTestAccount("+18005551234", UUID.randomUUID(), UUID.randomUUID(), + List.of(giftBadgesCapableDevice, giftBadgesIncapableExpiredDevice), "1234".getBytes(StandardCharsets.UTF_8)).isGiftBadgesSupported()).isTrue(); } @Test void stale() { - final Account account = new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), Collections.emptySet(), + final Account account = AccountsHelper.generateTestAccount("+14151234567", UUID.randomUUID(), UUID.randomUUID(), Collections.emptyList(), new byte[0]); assertDoesNotThrow(account::getNumber); @@ -420,10 +407,9 @@ class AccountTest { @Test void getNextDeviceId() { - final Set devices = new HashSet<>(); - devices.add(createDevice(Device.MASTER_ID)); + final List devices = List.of(createDevice(Device.MASTER_ID)); - final Account account = new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), devices, new byte[0]); + final Account account = AccountsHelper.generateTestAccount("+14151234567", UUID.randomUUID(), UUID.randomUUID(), devices, new byte[0]); assertThat(account.getNextDeviceId()).isEqualTo(2L); @@ -442,9 +428,22 @@ class AccountTest { assertThat(account.getNextDeviceId()).isEqualTo(2L); } + @Test + void replaceDevice() { + final Device firstDevice = createDevice(Device.MASTER_ID); + final Device secondDevice = createDevice(Device.MASTER_ID); + final Account account = AccountsHelper.generateTestAccount("+14151234567", UUID.randomUUID(), UUID.randomUUID(), List.of(firstDevice), new byte[0]); + + assertEquals(List.of(firstDevice), account.getDevices()); + + account.addDevice(secondDevice); + + assertEquals(List.of(secondDevice), account.getDevices()); + } + @Test void addAndRemoveBadges() { - final Account account = new Account("+14151234567", UUID.randomUUID(), UUID.randomUUID(), Set.of(createDevice(Device.MASTER_ID)), new byte[0]); + final Account account = AccountsHelper.generateTestAccount("+14151234567", UUID.randomUUID(), UUID.randomUUID(), List.of(createDevice(Device.MASTER_ID)), new byte[0]); final Clock clock = mock(Clock.class); when(clock.instant()).thenReturn(Instant.ofEpochSecond(40)); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java index e57382c0f..131fc30e8 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/PushFeedbackProcessorTest.java @@ -78,11 +78,11 @@ class PushFeedbackProcessorTest { when(stillActiveDevice.getLastSeen()).thenReturn(Util.todayInMillis()); when(stillActiveDevice.isEnabled()).thenReturn(true); - when(uninstalledAccount.getDevices()).thenReturn(Set.of(uninstalledDevice)); - when(mixedAccount.getDevices()).thenReturn(Set.of(installedDevice, uninstalledDeviceTwo)); - when(freshAccount.getDevices()).thenReturn(Set.of(recentUninstalledDevice)); - when(cleanAccount.getDevices()).thenReturn(Set.of(installedDeviceTwo)); - when(stillActiveAccount.getDevices()).thenReturn(Set.of(stillActiveDevice)); + when(uninstalledAccount.getDevices()).thenReturn(List.of(uninstalledDevice)); + when(mixedAccount.getDevices()).thenReturn(List.of(installedDevice, uninstalledDeviceTwo)); + when(freshAccount.getDevices()).thenReturn(List.of(recentUninstalledDevice)); + when(cleanAccount.getDevices()).thenReturn(List.of(installedDeviceTwo)); + when(stillActiveAccount.getDevices()).thenReturn(List.of(stillActiveDevice)); when(mixedAccount.getUuid()).thenReturn(UUID.randomUUID()); when(freshAccount.getUuid()).thenReturn(UUID.randomUUID()); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AccountsHelper.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AccountsHelper.java index 33160a018..e3f99cbd7 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AccountsHelper.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/AccountsHelper.java @@ -14,6 +14,7 @@ import static org.mockito.Mockito.when; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; @@ -26,6 +27,20 @@ import org.whispersystems.textsecuregcm.util.SystemMapper; public class AccountsHelper { + public static Account generateTestAccount(String number, List devices) { + return generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), devices, null); + } + + public static Account generateTestAccount(String number, UUID uuid, final UUID phoneNumberIdentifier, List devices, byte[] unidentifiedAccessKey) { + final Account account = new Account(); + account.setNumber(number, phoneNumberIdentifier); + account.setUuid(uuid); + devices.forEach(account::addDevice); + account.setUnidentifiedAccessKey(unidentifiedAccessKey); + + return account; + } + public static void setupMockUpdate(final AccountsManager mockAccountsManager) { setupMockUpdate(mockAccountsManager, true); } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/DevicesHelper.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/DevicesHelper.java index 4c2f4aa85..0c40a87a5 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/DevicesHelper.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/DevicesHelper.java @@ -15,8 +15,14 @@ public class DevicesHelper { private static final Random RANDOM = new Random(); public static Device createDevice(final long deviceId) { - final Device device = new Device(deviceId, null, null, null, null, null, null, false, 0, null, 0, 0, "OWT", 0, - null); + return createDevice(deviceId, 0); + } + + public static Device createDevice(final long deviceId, final long lastSeen) { + final Device device = new Device(); + device.setId(deviceId); + device.setLastSeen(lastSeen); + device.setUserAgent("OWT"); setEnabled(device, true); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/MessageValidationTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/MessageValidationTest.java index 9235021a0..b90af7409 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/MessageValidationTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/MessageValidationTest.java @@ -11,8 +11,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; @@ -120,7 +122,7 @@ class MessageValidationTest { if (deviceIdAndEnabled.length % 2 != 0) { throw new IllegalArgumentException("invalid number of arguments specified; must be even"); } - final Set devices = new HashSet<>(deviceIdAndEnabled.length / 2); + final List devices = new ArrayList<>(deviceIdAndEnabled.length / 2); for (int i = 0; i < deviceIdAndEnabled.length; i+=2) { if (!(deviceIdAndEnabled[i] instanceof Long)) { throw new IllegalArgumentException("device id is not instance of long at index " + i); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnectionTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnectionTest.java index 2f80ff784..d1a37ea3d 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnectionTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnectionTest.java @@ -30,11 +30,9 @@ import java.io.IOException; import java.time.Duration; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -166,9 +164,7 @@ class WebSocketConnectionTest { final Device sender1device = mock(Device.class); - Set sender1devices = new HashSet<>() {{ - add(sender1device); - }}; + List sender1devices = List.of(sender1device); Account sender1 = mock(Account.class); when(sender1.getDevices()).thenReturn(sender1devices); @@ -323,9 +319,7 @@ class WebSocketConnectionTest { final Device sender1device = mock(Device.class); - Set sender1devices = new HashSet() {{ - add(sender1device); - }}; + List sender1devices = List.of(sender1device); Account sender1 = mock(Account.class); when(sender1.getDevices()).thenReturn(sender1devices); @@ -705,9 +699,7 @@ class WebSocketConnectionTest { final Device sender1device = mock(Device.class); - Set sender1devices = new HashSet<>() {{ - add(sender1device); - }}; + List sender1devices = List.of(sender1device); Account sender1 = mock(Account.class); when(sender1.getDevices()).thenReturn(sender1devices); @@ -780,9 +772,7 @@ class WebSocketConnectionTest { final Device sender1device = mock(Device.class); - Set sender1devices = new HashSet<>() {{ - add(sender1device); - }}; + List sender1devices = List.of(sender1device); Account sender1 = mock(Account.class); when(sender1.getDevices()).thenReturn(sender1devices);