From 50ccfee2014a71c32e56ae2b73947112d8ee2e92 Mon Sep 17 00:00:00 2001 From: Ehren Kret Date: Wed, 29 Apr 2020 10:51:10 -0700 Subject: [PATCH] Allow remote config to send non-boolean values This version of remote config allows non-boolean values to be returned to clients but unfortunately limits the configuration to only one value or another. There is no way to configure more than two values for the same key with this setup. --- .../controllers/RemoteConfigController.java | 14 +-- .../entities/UserRemoteConfig.java | 12 +- .../textsecuregcm/storage/RemoteConfig.java | 26 ++++- .../textsecuregcm/storage/RemoteConfigs.java | 29 +++-- .../storage/RemoteConfigsManager.java | 5 +- .../mappers/RemoteConfigRowMapper.java | 6 +- service/src/main/resources/accountsdb.xml | 7 ++ .../RemoteConfigControllerTest.java | 104 ++++++++++++------ .../storage/RemoteConfigsManagerTest.java | 25 +++-- .../tests/storage/RemoteConfigsTest.java | 88 +++++++++------ 10 files changed, 202 insertions(+), 114 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RemoteConfigController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RemoteConfigController.java index 97ef203d1..564fef311 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RemoteConfigController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RemoteConfigController.java @@ -2,6 +2,7 @@ package org.whispersystems.textsecuregcm.controllers; import com.codahale.metrics.annotation.Timed; import com.google.common.annotations.VisibleForTesting; +import io.dropwizard.auth.Auth; import org.whispersystems.textsecuregcm.entities.UserRemoteConfig; import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList; import org.whispersystems.textsecuregcm.storage.Account; @@ -29,8 +30,6 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import io.dropwizard.auth.Auth; - @Path("/v1/config") public class RemoteConfigController { @@ -50,12 +49,10 @@ public class RemoteConfigController { try { MessageDigest digest = MessageDigest.getInstance("SHA1"); - return new UserRemoteConfigList(remoteConfigsManager.getAll().stream().map(config -> new UserRemoteConfig(config.getName(), - isInBucket(digest, account.getUuid(), - config.getName().getBytes(), - config.getPercentage(), - config.getUuids()))) - .collect(Collectors.toList())); + return new UserRemoteConfigList(remoteConfigsManager.getAll().stream().map(config -> { + boolean inBucket = isInBucket(digest, account.getUuid(), config.getName().getBytes(), config.getPercentage(), config.getUuids()); + return new UserRemoteConfig(config.getName(), inBucket, inBucket ? config.getValue() : config.getDefaultValue()); + }).collect(Collectors.toList())); } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } @@ -104,5 +101,4 @@ public class RemoteConfigController { private boolean isAuthorized(String configToken) { return configToken != null && configAuthTokens.stream().anyMatch(authorized -> MessageDigest.isEqual(authorized.getBytes(), configToken.getBytes())); } - } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserRemoteConfig.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserRemoteConfig.java index f8e01fa60..c3670d82d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserRemoteConfig.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/UserRemoteConfig.java @@ -5,16 +5,20 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class UserRemoteConfig { @JsonProperty - private String name; + private String name; @JsonProperty private boolean enabled; + @JsonProperty + private String value; + public UserRemoteConfig() {} - public UserRemoteConfig(String name, boolean enabled) { + public UserRemoteConfig(String name, boolean enabled, String value) { this.name = name; this.enabled = enabled; + this.value = value; } public String getName() { @@ -24,4 +28,8 @@ public class UserRemoteConfig { public boolean isEnabled() { return enabled; } + + public String getValue() { + return value; + } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfig.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfig.java index facedbcec..9f4d5daaf 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfig.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfig.java @@ -7,8 +7,6 @@ import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Set; import java.util.UUID; @@ -28,12 +26,20 @@ public class RemoteConfig { @NotNull private Set uuids = new HashSet<>(); + @JsonProperty + private String defaultValue; + + @JsonProperty + private String value; + public RemoteConfig() {} - public RemoteConfig(String name, int percentage, Set uuids) { - this.name = name; - this.percentage = percentage; - this.uuids = uuids; + public RemoteConfig(String name, int percentage, Set uuids, String defaultValue, String value) { + this.name = name; + this.percentage = percentage; + this.uuids = uuids; + this.defaultValue = defaultValue; + this.value = value; } public int getPercentage() { @@ -47,4 +53,12 @@ public class RemoteConfig { public Set getUuids() { return uuids; } + + public String getDefaultValue() { + return defaultValue; + } + + public String getValue() { + return value; + } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigs.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigs.java index fe692b5ad..637622fa9 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigs.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigs.java @@ -3,27 +3,22 @@ package org.whispersystems.textsecuregcm.storage; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.Timer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.jdbi.v3.core.transaction.TransactionIsolationLevel; -import org.whispersystems.textsecuregcm.storage.mappers.AccountRowMapper; import org.whispersystems.textsecuregcm.storage.mappers.RemoteConfigRowMapper; import org.whispersystems.textsecuregcm.util.Constants; -import org.whispersystems.textsecuregcm.util.SystemMapper; -import java.util.LinkedList; import java.util.List; -import java.util.Optional; import java.util.UUID; import static com.codahale.metrics.MetricRegistry.name; public class RemoteConfigs { - public static final String ID = "id"; - public static final String NAME = "name"; - public static final String PERCENTAGE = "percentage"; - public static final String UUIDS = "uuids"; + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String PERCENTAGE = "percentage"; + public static final String UUIDS = "uuids"; + public static final String DEFAULT_VALUE = "default_value"; + public static final String VALUE = "value"; private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME); private final Timer setTimer = metricRegistry.timer(name(Accounts.class, "set" )); @@ -41,11 +36,13 @@ public class RemoteConfigs { public void set(RemoteConfig remoteConfig) { database.use(jdbi -> jdbi.useHandle(handle -> { try (Timer.Context ignored = setTimer.time()) { - handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ") VALUES (:name, :percentage, :uuids) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS) - .bind("name", remoteConfig.getName()) - .bind("percentage", remoteConfig.getPercentage()) - .bind("uuids", remoteConfig.getUuids().toArray(new UUID[0])) - .execute(); + handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ", " + DEFAULT_VALUE + ", " + VALUE + ") VALUES (:name, :percentage, :uuids, :default_value, :value) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS + ", " + DEFAULT_VALUE + " = EXCLUDED." + DEFAULT_VALUE + ", " + VALUE + " = EXCLUDED." + VALUE) + .bind("name", remoteConfig.getName()) + .bind("percentage", remoteConfig.getPercentage()) + .bind("uuids", remoteConfig.getUuids().toArray(new UUID[0])) + .bind("default_value", remoteConfig.getDefaultValue()) + .bind("value", remoteConfig.getValue()) + .execute(); } })); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigsManager.java index 4a775302c..d955274b4 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/RemoteConfigsManager.java @@ -1,6 +1,7 @@ package org.whispersystems.textsecuregcm.storage; import com.google.common.annotations.VisibleForTesting; +import io.dropwizard.lifecycle.Managed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.util.Util; @@ -10,8 +11,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import io.dropwizard.lifecycle.Managed; - public class RemoteConfigsManager implements Managed { private final Logger logger = LoggerFactory.getLogger(RemoteConfigsManager.class); @@ -19,7 +18,7 @@ public class RemoteConfigsManager implements Managed { private final RemoteConfigs remoteConfigs; private final long sleepInterval; - private AtomicReference> cachedConfigs = new AtomicReference<>(new LinkedList<>()); + private final AtomicReference> cachedConfigs = new AtomicReference<>(new LinkedList<>()); public RemoteConfigsManager(RemoteConfigs remoteConfigs) { this(remoteConfigs, TimeUnit.SECONDS.toMillis(10)); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/RemoteConfigRowMapper.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/RemoteConfigRowMapper.java index 46b8d5428..bacc6a4a7 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/RemoteConfigRowMapper.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/mappers/RemoteConfigRowMapper.java @@ -16,6 +16,10 @@ public class RemoteConfigRowMapper implements RowMapper { @Override public RemoteConfig map(ResultSet rs, StatementContext ctx) throws SQLException { - return new RemoteConfig(rs.getString(RemoteConfigs.NAME), rs.getInt(RemoteConfigs.PERCENTAGE), new HashSet<>(Arrays.asList((UUID[])rs.getArray(RemoteConfigs.UUIDS).getArray()))); + return new RemoteConfig(rs.getString(RemoteConfigs.NAME), + rs.getInt(RemoteConfigs.PERCENTAGE), + new HashSet<>(Arrays.asList((UUID[])rs.getArray(RemoteConfigs.UUIDS).getArray())), + rs.getString(RemoteConfigs.DEFAULT_VALUE), + rs.getString(RemoteConfigs.VALUE)); } } diff --git a/service/src/main/resources/accountsdb.xml b/service/src/main/resources/accountsdb.xml index 7bb8995dd..1de061d16 100644 --- a/service/src/main/resources/accountsdb.xml +++ b/service/src/main/resources/accountsdb.xml @@ -310,5 +310,12 @@ + + + + + + + diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/RemoteConfigControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/RemoteConfigControllerTest.java index d117cfe22..d1b3856dd 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/RemoteConfigControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/RemoteConfigControllerTest.java @@ -1,6 +1,8 @@ package org.whispersystems.textsecuregcm.tests.controllers; import com.google.common.collect.ImmutableSet; +import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; +import io.dropwizard.testing.junit.ResourceTestRule; import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; import org.junit.Before; import org.junit.Rule; @@ -20,26 +22,25 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; -import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; -import io.dropwizard.testing.junit.ResourceTestRule; -import static org.assertj.core.api.Java6Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; public class RemoteConfigControllerTest { - private RemoteConfigsManager remoteConfigsManager = mock(RemoteConfigsManager.class); - private List remoteConfigsAuth = new LinkedList<>() {{ - add("foo"); - add("bar"); - }}; + private final RemoteConfigsManager remoteConfigsManager = mock(RemoteConfigsManager.class); + private final List remoteConfigsAuth = List.of("foo", "bar"); @Rule public final ResourceTestRule resources = ResourceTestRule.builder() @@ -52,24 +53,15 @@ public class RemoteConfigControllerTest { @Before - public void setup() throws Exception { + public void setup() { when(remoteConfigsManager.getAll()).thenReturn(new LinkedList<>() {{ - add(new RemoteConfig("android.stickers", 25, new HashSet<>() {{ - add(AuthHelper.DISABLED_UUID); - add(AuthHelper.INVALID_UUID); - }})); - - add(new RemoteConfig("ios.stickers", 50, new HashSet<>() {{ - - }})); - - add(new RemoteConfig("always.true", 100, new HashSet<>() {{ - - }})); - - add(new RemoteConfig("only.special", 0, new HashSet<>() {{ - add(AuthHelper.VALID_UUID); - }})); + add(new RemoteConfig("android.stickers", 25, Set.of(AuthHelper.DISABLED_UUID, AuthHelper.INVALID_UUID), null, null)); + add(new RemoteConfig("ios.stickers", 50, Set.of(), null, null)); + add(new RemoteConfig("always.true", 100, Set.of(), null, null)); + add(new RemoteConfig("only.special", 0, Set.of(AuthHelper.VALID_UUID), null, null)); + add(new RemoteConfig("value.always.true", 100, Set.of(), "foo", "bar")); + add(new RemoteConfig("value.only.special", 0, Set.of(AuthHelper.VALID_UUID), "abc", "xyz")); + add(new RemoteConfig("value.always.false", 0, Set.of(), "red", "green")); }}); } @@ -83,12 +75,24 @@ public class RemoteConfigControllerTest { verify(remoteConfigsManager, times(1)).getAll(); - assertThat(configuration.getConfig().size()).isEqualTo(4); + assertThat(configuration.getConfig().size()).isEqualTo(7); assertThat(configuration.getConfig().get(0).getName()).isEqualTo("android.stickers"); assertThat(configuration.getConfig().get(1).getName()).isEqualTo("ios.stickers"); assertThat(configuration.getConfig().get(2).getName()).isEqualTo("always.true"); assertThat(configuration.getConfig().get(2).isEnabled()).isEqualTo(true); + assertThat(configuration.getConfig().get(2).getValue()).isNull(); + assertThat(configuration.getConfig().get(3).getName()).isEqualTo("only.special"); assertThat(configuration.getConfig().get(3).isEnabled()).isEqualTo(true); + assertThat(configuration.getConfig().get(2).getValue()).isNull(); + assertThat(configuration.getConfig().get(4).getName()).isEqualTo("value.always.true"); + assertThat(configuration.getConfig().get(4).isEnabled()).isEqualTo(true); + assertThat(configuration.getConfig().get(4).getValue()).isEqualTo("bar"); + assertThat(configuration.getConfig().get(5).getName()).isEqualTo("value.only.special"); + assertThat(configuration.getConfig().get(5).isEnabled()).isEqualTo(true); + assertThat(configuration.getConfig().get(5).getValue()).isEqualTo("xyz"); + assertThat(configuration.getConfig().get(6).getName()).isEqualTo("value.always.false"); + assertThat(configuration.getConfig().get(6).isEnabled()).isEqualTo(false); + assertThat(configuration.getConfig().get(6).getValue()).isEqualTo("red"); } @Test @@ -101,12 +105,24 @@ public class RemoteConfigControllerTest { verify(remoteConfigsManager, times(1)).getAll(); - assertThat(configuration.getConfig().size()).isEqualTo(4); + assertThat(configuration.getConfig().size()).isEqualTo(7); assertThat(configuration.getConfig().get(0).getName()).isEqualTo("android.stickers"); assertThat(configuration.getConfig().get(1).getName()).isEqualTo("ios.stickers"); assertThat(configuration.getConfig().get(2).getName()).isEqualTo("always.true"); assertThat(configuration.getConfig().get(2).isEnabled()).isEqualTo(true); + assertThat(configuration.getConfig().get(2).getValue()).isNull(); + assertThat(configuration.getConfig().get(3).getName()).isEqualTo("only.special"); assertThat(configuration.getConfig().get(3).isEnabled()).isEqualTo(false); + assertThat(configuration.getConfig().get(2).getValue()).isNull(); + assertThat(configuration.getConfig().get(4).getName()).isEqualTo("value.always.true"); + assertThat(configuration.getConfig().get(4).isEnabled()).isEqualTo(true); + assertThat(configuration.getConfig().get(4).getValue()).isEqualTo("bar"); + assertThat(configuration.getConfig().get(5).getName()).isEqualTo("value.only.special"); + assertThat(configuration.getConfig().get(5).isEnabled()).isEqualTo(false); + assertThat(configuration.getConfig().get(5).getValue()).isEqualTo("abc"); + assertThat(configuration.getConfig().get(6).getName()).isEqualTo("value.always.false"); + assertThat(configuration.getConfig().get(6).isEnabled()).isEqualTo(false); + assertThat(configuration.getConfig().get(6).getValue()).isEqualTo("red"); } @@ -130,7 +146,7 @@ public class RemoteConfigControllerTest { .target("/v1/config") .request() .header("Config-Token", "foo") - .put(Entity.entity(new RemoteConfig("android.stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE)); + .put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(204); @@ -143,13 +159,32 @@ public class RemoteConfigControllerTest { assertThat(captor.getValue().getUuids()).isEmpty(); } + @Test + public void testSetConfigValued() { + Response response = resources.getJerseyTest() + .target("/v1/config") + .request() + .header("Config-Token", "foo") + .put(Entity.entity(new RemoteConfig("value.sometimes", 50, Set.of(), "a", "b"), MediaType.APPLICATION_JSON_TYPE)); + + assertThat(response.getStatus()).isEqualTo(204); + + ArgumentCaptor captor = ArgumentCaptor.forClass(RemoteConfig.class); + + verify(remoteConfigsManager, times(1)).set(captor.capture()); + + assertThat(captor.getValue().getName()).isEqualTo("value.sometimes"); + assertThat(captor.getValue().getPercentage()).isEqualTo(50); + assertThat(captor.getValue().getUuids()).isEmpty(); + } + @Test public void testSetConfigUnauthorized() { Response response = resources.getJerseyTest() .target("/v1/config") .request() .header("Config-Token", "baz") - .put(Entity.entity(new RemoteConfig("android.stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE)); + .put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(401); @@ -161,7 +196,7 @@ public class RemoteConfigControllerTest { Response response = resources.getJerseyTest() .target("/v1/config") .request() - .put(Entity.entity(new RemoteConfig("android.stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE)); + .put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(401); @@ -174,7 +209,7 @@ public class RemoteConfigControllerTest { .target("/v1/config") .request() .header("Config-Token", "foo") - .put(Entity.entity(new RemoteConfig("android-stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE)); + .put(Entity.entity(new RemoteConfig("android-stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(422); @@ -187,7 +222,7 @@ public class RemoteConfigControllerTest { .target("/v1/config") .request() .header("Config-Token", "foo") - .put(Entity.entity(new RemoteConfig("", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE)); + .put(Entity.entity(new RemoteConfig("", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE)); assertThat(response.getStatus()).isEqualTo(422); @@ -232,7 +267,6 @@ public class RemoteConfigControllerTest { for (int i=0;i())) { count++; diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsManagerTest.java index 9bd87540e..6ff42948b 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsManagerTest.java @@ -14,11 +14,10 @@ import org.whispersystems.textsecuregcm.storage.RemoteConfigs; import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; -import java.util.HashSet; import java.util.List; +import java.util.Set; -import io.dropwizard.auth.Auth; -import static org.assertj.core.api.Java6Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class RemoteConfigsManagerTest { @@ -36,17 +35,18 @@ public class RemoteConfigsManagerTest { @Test public void testUpdate() throws InterruptedException { - remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>() {{ - add(AuthHelper.VALID_UUID); - }})); - remoteConfigs.set(new RemoteConfig("ios.stickers", 50, new HashSet<>())); - remoteConfigs.set(new RemoteConfig("ios.stickers", 75, new HashSet<>())); + remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("value.sometimes", 50, Set.of(), "bar", "baz")); + remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("value.sometimes", 25, Set.of(AuthHelper.VALID_UUID), "abc", "def")); Thread.sleep(501); List results = remoteConfigs.getAll(); - assertThat(results.size()).isEqualTo(2); + assertThat(results.size()).isEqualTo(3); + assertThat(results.get(0).getName()).isEqualTo("android.stickers"); assertThat(results.get(0).getPercentage()).isEqualTo(50); assertThat(results.get(0).getUuids().size()).isEqualTo(1); @@ -56,6 +56,13 @@ public class RemoteConfigsManagerTest { assertThat(results.get(1).getPercentage()).isEqualTo(75); assertThat(results.get(1).getUuids()).isEmpty(); + assertThat(results.get(2).getName()).isEqualTo("value.sometimes"); + assertThat(results.get(2).getUuids()).hasSize(1); + assertThat(results.get(2).getUuids()).contains(AuthHelper.VALID_UUID); + assertThat(results.get(2).getPercentage()).isEqualTo(25); + assertThat(results.get(2).getDefaultValue()).isEqualTo("abc"); + assertThat(results.get(2).getValue()).isEqualTo("def"); + } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsTest.java index b639384ac..3ba6a3496 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/storage/RemoteConfigsTest.java @@ -13,12 +13,10 @@ import org.whispersystems.textsecuregcm.storage.RemoteConfig; import org.whispersystems.textsecuregcm.storage.RemoteConfigs; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; -import java.sql.SQLException; -import java.util.HashSet; import java.util.List; +import java.util.Set; -import io.dropwizard.auth.Auth; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class RemoteConfigsTest { @@ -33,60 +31,84 @@ public class RemoteConfigsTest { } @Test - public void testStore() throws SQLException { - remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>() {{ - add(AuthHelper.VALID_UUID); - add(AuthHelper.VALID_UUID_TWO); - }})); + public void testStore() { + remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID, AuthHelper.VALID_UUID_TWO), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("value.sometimes", 25, Set.of(AuthHelper.VALID_UUID_TWO), "default", "custom")); List configs = remoteConfigs.getAll(); - assertThat(configs.size()).isEqualTo(1); + assertThat(configs).hasSize(2); + assertThat(configs.get(0).getName()).isEqualTo("android.stickers"); + assertThat(configs.get(0).getValue()).isEqualTo("TRUE"); + assertThat(configs.get(0).getDefaultValue()).isEqualTo("FALSE"); assertThat(configs.get(0).getPercentage()).isEqualTo(50); - assertThat(configs.get(0).getUuids().size()).isEqualTo(2); - assertThat(configs.get(0).getUuids().contains(AuthHelper.VALID_UUID)).isTrue(); - assertThat(configs.get(0).getUuids().contains(AuthHelper.VALID_UUID_TWO)).isTrue(); - assertThat(configs.get(0).getUuids().contains(AuthHelper.INVALID_UUID)).isFalse(); + assertThat(configs.get(0).getUuids()).hasSize(2); + assertThat(configs.get(0).getUuids()).contains(AuthHelper.VALID_UUID); + assertThat(configs.get(0).getUuids()).contains(AuthHelper.VALID_UUID_TWO); + assertThat(configs.get(0).getUuids()).doesNotContain(AuthHelper.INVALID_UUID); + + assertThat(configs.get(1).getName()).isEqualTo("value.sometimes"); + assertThat(configs.get(1).getValue()).isEqualTo("custom"); + assertThat(configs.get(1).getDefaultValue()).isEqualTo("default"); + assertThat(configs.get(1).getPercentage()).isEqualTo(25); + assertThat(configs.get(1).getUuids()).hasSize(1); + assertThat(configs.get(1).getUuids()).contains(AuthHelper.VALID_UUID_TWO); + assertThat(configs.get(1).getUuids()).doesNotContain(AuthHelper.VALID_UUID); + assertThat(configs.get(1).getUuids()).doesNotContain(AuthHelper.INVALID_UUID); } @Test - public void testUpdate() throws SQLException { - remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>())); - - remoteConfigs.set(new RemoteConfig("ios.stickers", 50, new HashSet<>() {{ - add(AuthHelper.DISABLED_UUID); - }})); - - remoteConfigs.set(new RemoteConfig("ios.stickers", 75, new HashSet<>())); + public void testUpdate() { + remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("value.sometimes", 22, Set.of(), "def", "!")); + remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(AuthHelper.DISABLED_UUID), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("value.sometimes", 77, Set.of(), "hey", "wut")); List configs = remoteConfigs.getAll(); - assertThat(configs.size()).isEqualTo(2); + assertThat(configs).hasSize(3); + assertThat(configs.get(0).getName()).isEqualTo("android.stickers"); assertThat(configs.get(0).getPercentage()).isEqualTo(50); - assertThat(configs.get(0).getUuids().size()).isEqualTo(0); + assertThat(configs.get(0).getUuids()).isEmpty(); + assertThat(configs.get(0).getDefaultValue()).isEqualTo("FALSE"); + assertThat(configs.get(0).getValue()).isEqualTo("TRUE"); assertThat(configs.get(1).getName()).isEqualTo("ios.stickers"); assertThat(configs.get(1).getPercentage()).isEqualTo(75); - assertThat(configs.get(1).getUuids().size()).isEqualTo(0); + assertThat(configs.get(1).getUuids()).isEmpty(); + assertThat(configs.get(1).getDefaultValue()).isEqualTo("FALSE"); + assertThat(configs.get(1).getValue()).isEqualTo("TRUE"); + + assertThat(configs.get(2).getName()).isEqualTo("value.sometimes"); + assertThat(configs.get(2).getPercentage()).isEqualTo(77); + assertThat(configs.get(2).getUuids()).isEmpty(); + assertThat(configs.get(2).getDefaultValue()).isEqualTo("hey"); + assertThat(configs.get(2).getValue()).isEqualTo("wut"); } @Test public void testDelete() { - remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>() {{ - add(AuthHelper.VALID_UUID); - }})); - remoteConfigs.set(new RemoteConfig("ios.stickers", 50, new HashSet<>())); - remoteConfigs.set(new RemoteConfig("ios.stickers", 75, new HashSet<>())); + remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE")); + remoteConfigs.set(new RemoteConfig("value.always", 100, Set.of(), "never", "always")); remoteConfigs.delete("android.stickers"); List configs = remoteConfigs.getAll(); - assertThat(configs.size()).isEqualTo(1); + assertThat(configs).hasSize(2); + assertThat(configs.get(0).getName()).isEqualTo("ios.stickers"); assertThat(configs.get(0).getPercentage()).isEqualTo(75); + assertThat(configs.get(0).getDefaultValue()).isEqualTo("FALSE"); + assertThat(configs.get(0).getValue()).isEqualTo("TRUE"); + + assertThat(configs.get(1).getName()).isEqualTo("value.always"); + assertThat(configs.get(1).getPercentage()).isEqualTo(100); + assertThat(configs.get(1).getValue()).isEqualTo("always"); + assertThat(configs.get(1).getDefaultValue()).isEqualTo("never"); } - - }