mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-26 09:08:05 +01:00
Migrate profiles from a relational database to DynamoDB
This commit is contained in:
@@ -5,10 +5,10 @@
|
||||
|
||||
package org.whispersystems.textsecuregcm.tests.storage;
|
||||
|
||||
import static junit.framework.TestCase.assertSame;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -22,59 +22,85 @@ import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||
import java.util.Base64;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.junit.Test;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicProfileMigrationConfiguration;
|
||||
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Profiles;
|
||||
import org.whispersystems.textsecuregcm.storage.ProfilesDynamoDb;
|
||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||
|
||||
public class ProfilesManagerTest {
|
||||
|
||||
private Profiles profiles;
|
||||
private RedisAdvancedClusterCommands<String, String> commands;
|
||||
|
||||
private ProfilesManager profilesManager;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
//noinspection unchecked
|
||||
commands = mock(RedisAdvancedClusterCommands.class);
|
||||
final FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
||||
|
||||
profiles = mock(Profiles.class);
|
||||
|
||||
@SuppressWarnings("unchecked") final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager =
|
||||
mock(DynamicConfigurationManager.class);
|
||||
|
||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||
final DynamicProfileMigrationConfiguration profileMigrationConfiguration =
|
||||
mock(DynamicProfileMigrationConfiguration.class);
|
||||
|
||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||
when(dynamicConfiguration.getProfileMigrationConfiguration()).thenReturn(profileMigrationConfiguration);
|
||||
|
||||
profilesManager = new ProfilesManager(profiles,
|
||||
mock(ProfilesDynamoDb.class),
|
||||
cacheCluster,
|
||||
dynamicConfigurationManager,
|
||||
mock(Executor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfileInCache() {
|
||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
||||
Profiles profiles = mock(Profiles.class);
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
|
||||
when(commands.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn("{\"version\": \"someversion\", \"name\": \"somename\", \"avatar\": \"someavatar\", \"commitment\":\"" + Base64.getEncoder().encodeToString("somecommitment".getBytes()) + "\"}");
|
||||
when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn("{\"version\": \"someversion\", \"name\": \"somename\", \"avatar\": \"someavatar\", \"commitment\":\"" + Base64.getEncoder().encodeToString("somecommitment".getBytes()) + "\"}");
|
||||
|
||||
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster);
|
||||
Optional<VersionedProfile> profile = profilesManager.get(uuid, "someversion");
|
||||
Optional<VersionedProfile> profile = profilesManager.get(uuid, "someversion");
|
||||
|
||||
assertTrue(profile.isPresent());
|
||||
assertEquals(profile.get().getName(), "somename");
|
||||
assertEquals(profile.get().getAvatar(), "someavatar");
|
||||
assertThat(profile.get().getCommitment()).isEqualTo("somecommitment".getBytes());
|
||||
|
||||
verify(commands, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
|
||||
verify(commands, times(1)).hget(eq("profiles::" + uuid), eq("someversion"));
|
||||
verifyNoMoreInteractions(commands);
|
||||
verifyNoMoreInteractions(profiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfileNotInCache() {
|
||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
||||
Profiles profiles = mock(Profiles.class);
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
when(commands.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn(null);
|
||||
when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(null);
|
||||
when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile));
|
||||
|
||||
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster);
|
||||
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
|
||||
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
|
||||
|
||||
assertTrue(retrieved.isPresent());
|
||||
assertSame(retrieved.get(), profile);
|
||||
|
||||
verify(commands, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
|
||||
verify(commands, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString());
|
||||
verify(commands, times(1)).hget(eq("profiles::" + uuid), eq("someversion"));
|
||||
verify(commands, times(1)).hset(eq("profiles::" + uuid), eq("someversion"), anyString());
|
||||
verifyNoMoreInteractions(commands);
|
||||
|
||||
verify(profiles, times(1)).get(eq(uuid), eq("someversion"));
|
||||
@@ -83,25 +109,20 @@ public class ProfilesManagerTest {
|
||||
|
||||
@Test
|
||||
public void testGetProfileBrokenCache() {
|
||||
RedisAdvancedClusterCommands<String, String> commands = mock(RedisAdvancedClusterCommands.class);
|
||||
FaultTolerantRedisCluster cacheCluster = RedisClusterHelper.buildMockRedisCluster(commands);
|
||||
Profiles profiles = mock(Profiles.class);
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", null, null,
|
||||
null, "somecommitment".getBytes());
|
||||
|
||||
when(commands.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenThrow(new RedisException("Connection lost"));
|
||||
when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenThrow(new RedisException("Connection lost"));
|
||||
when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile));
|
||||
|
||||
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheCluster);
|
||||
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
|
||||
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
|
||||
|
||||
assertTrue(retrieved.isPresent());
|
||||
assertSame(retrieved.get(), profile);
|
||||
|
||||
verify(commands, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
|
||||
verify(commands, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString());
|
||||
verify(commands, times(1)).hget(eq("profiles::" + uuid), eq("someversion"));
|
||||
verify(commands, times(1)).hset(eq("profiles::" + uuid), eq("someversion"), anyString());
|
||||
verifyNoMoreInteractions(commands);
|
||||
|
||||
verify(profiles, times(1)).get(eq(uuid), eq("someversion"));
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.tests.storage;
|
||||
|
||||
import com.opentable.db.postgres.embedded.LiquibasePreparer;
|
||||
import com.opentable.db.postgres.junit.EmbeddedPostgresRules;
|
||||
import com.opentable.db.postgres.junit.PreparedDbRule;
|
||||
import org.jdbi.v3.core.Jdbi;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.FaultTolerantDatabase;
|
||||
import org.whispersystems.textsecuregcm.storage.Profiles;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
public class ProfilesTest {
|
||||
|
||||
@Rule
|
||||
public PreparedDbRule db = EmbeddedPostgresRules.preparedDatabase(LiquibasePreparer.forClasspathLocation("accountsdb.xml"));
|
||||
|
||||
private Profiles profiles;
|
||||
|
||||
@Before
|
||||
public void setupProfilesDao() {
|
||||
FaultTolerantDatabase faultTolerantDatabase = new FaultTolerantDatabase("profilesTest",
|
||||
Jdbi.create(db.getTestDatabase()),
|
||||
new CircuitBreakerConfiguration());
|
||||
|
||||
this.profiles = new Profiles(faultTolerantDatabase);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetGet() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("123", "foo", "avatarLocation", "emoji", "the very model of a modern major general",
|
||||
null, "acommitment".getBytes());
|
||||
profiles.set(uuid, profile);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profile.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profile.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profile.getCommitment());
|
||||
assertThat(retrieved.get().getAbout()).isEqualTo(profile.getAbout());
|
||||
assertThat(retrieved.get().getAboutEmoji()).isEqualTo(profile.getAboutEmoji());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetGetNullOptionalFields() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("123", "foo", null, null, null, null,
|
||||
"acommitment".getBytes());
|
||||
profiles.set(uuid, profile);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profile.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profile.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profile.getCommitment());
|
||||
assertThat(retrieved.get().getAbout()).isEqualTo(profile.getAbout());
|
||||
assertThat(retrieved.get().getAboutEmoji()).isEqualTo(profile.getAboutEmoji());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetReplace() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("123", "foo", "avatarLocation", null, null,
|
||||
null, "acommitment".getBytes());
|
||||
profiles.set(uuid, profile);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profile.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profile.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profile.getCommitment());
|
||||
assertThat(retrieved.get().getAbout()).isNull();
|
||||
assertThat(retrieved.get().getAboutEmoji()).isNull();
|
||||
|
||||
VersionedProfile updated = new VersionedProfile("123", "bar", "baz", "emoji", "bio", null,
|
||||
"boof".getBytes());
|
||||
profiles.set(uuid, updated);
|
||||
|
||||
retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(updated.getName());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profile.getCommitment());
|
||||
assertThat(retrieved.get().getAbout()).isEqualTo(updated.getAbout());
|
||||
assertThat(retrieved.get().getAboutEmoji()).isEqualTo(updated.getAboutEmoji());
|
||||
|
||||
// Commitment should be unchanged after an overwrite
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(updated.getAvatar());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleVersions() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profileOne = new VersionedProfile("123", "foo", "avatarLocation", null, null,
|
||||
null, "acommitmnet".getBytes());
|
||||
VersionedProfile profileTwo = new VersionedProfile("345", "bar", "baz", "emoji", "i keep typing emoju for some reason",
|
||||
null, "boof".getBytes());
|
||||
|
||||
profiles.set(uuid, profileOne);
|
||||
profiles.set(uuid, profileTwo);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profileOne.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profileOne.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profileOne.getCommitment());
|
||||
assertThat(retrieved.get().getAbout()).isEqualTo(profileOne.getAbout());
|
||||
assertThat(retrieved.get().getAboutEmoji()).isEqualTo(profileOne.getAboutEmoji());
|
||||
|
||||
retrieved = profiles.get(uuid, "345");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profileTwo.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profileTwo.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profileTwo.getCommitment());
|
||||
assertThat(retrieved.get().getAbout()).isEqualTo(profileTwo.getAbout());
|
||||
assertThat(retrieved.get().getAboutEmoji()).isEqualTo(profileTwo.getAboutEmoji());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissing() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("123", "foo", "avatarLocation", null, null,
|
||||
null, "aDigest".getBytes());
|
||||
profiles.set(uuid, profile);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "888");
|
||||
assertThat(retrieved.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDelete() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profileOne = new VersionedProfile("123", "foo", "avatarLocation", null, null,
|
||||
null, "aDigest".getBytes());
|
||||
VersionedProfile profileTwo = new VersionedProfile("345", "bar", "baz", null, null, null, "boof".getBytes());
|
||||
|
||||
profiles.set(uuid, profileOne);
|
||||
profiles.set(uuid, profileTwo);
|
||||
|
||||
profiles.deleteAll(uuid);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isFalse();
|
||||
|
||||
retrieved = profiles.get(uuid, "345");
|
||||
|
||||
assertThat(retrieved.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user