From 88bb0370093c2d557bb7440c419359cb70529439 Mon Sep 17 00:00:00 2001 From: Mauro Sanz <51236193+sanzmauro@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:58:56 -0300 Subject: [PATCH 1/4] Update SplitFetcherImp.java --- .../java/io/split/engine/experiments/SplitFetcherImp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java index a2d8681d..30d374cd 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java +++ b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java @@ -100,7 +100,7 @@ public FetchResult forceRefresh(FetchOptions options) { Thread.currentThread().interrupt(); return new FetchResult(false, true, new HashSet<>()); } catch (Exception e) { - _log.error("RefreshableSplitFetcher failed: " + e.getMessage()); + _log.error("SplitFetcherImp failed: " + e.getMessage()); if (_log.isDebugEnabled()) { _log.debug("Reason:", e); } @@ -166,4 +166,4 @@ private Set runWithoutExceptionHandling(FetchOptions options) throws Int return segments; } -} \ No newline at end of file +} From 653764af44d6763552dad196c17d90ccbbc98128 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 14 Oct 2025 09:43:21 -0700 Subject: [PATCH 2/4] Added fallback string treatment --- .../dtos/FallbackTreatmentsConfiguration.java | 46 ++++++++++++++ .../client/JsonLocalhostSplitFactoryTest.java | 9 +++ .../client/LocalhostSplitFactoryTest.java | 34 +++++++++- .../client/LocalhostSplitFactoryYamlTest.java | 62 +++++++++++++++++-- .../split/client/SplitClientConfigTest.java | 6 +- .../io/split/client/SplitClientImplTest.java | 10 ++- .../client/SplitClientIntegrationTest.java | 8 +-- .../io/split/client/SplitFactoryImplTest.java | 2 +- .../FallbackTreatmentCalculationImpTest.java | 6 +- .../FallbackTreatmentConfigurationTest.java | 32 ++++++++++ .../split/engine/evaluator/EvaluatorTest.java | 4 +- 11 files changed, 194 insertions(+), 25 deletions(-) create mode 100644 client/src/test/java/io/split/client/dtos/FallbackTreatmentConfigurationTest.java diff --git a/client/src/main/java/io/split/client/dtos/FallbackTreatmentsConfiguration.java b/client/src/main/java/io/split/client/dtos/FallbackTreatmentsConfiguration.java index aa47d116..55a0a27d 100644 --- a/client/src/main/java/io/split/client/dtos/FallbackTreatmentsConfiguration.java +++ b/client/src/main/java/io/split/client/dtos/FallbackTreatmentsConfiguration.java @@ -1,5 +1,6 @@ package io.split.client.dtos; +import java.util.HashMap; import java.util.Map; public class FallbackTreatmentsConfiguration { @@ -11,9 +12,54 @@ public FallbackTreatmentsConfiguration(FallbackTreatment globalFallbackTreatment _byFlagFallbackTreatment = byFlagFallbackTreatment; } + public FallbackTreatmentsConfiguration(Map byFlagFallbackTreatment) { + _globalFallbackTreatment = null; + _byFlagFallbackTreatment = byFlagFallbackTreatment; + } + + public FallbackTreatmentsConfiguration(HashMap byFlagFallbackTreatment) { + _globalFallbackTreatment = null; + _byFlagFallbackTreatment = buildByFlagFallbackTreatment(byFlagFallbackTreatment); + } + + public FallbackTreatmentsConfiguration(FallbackTreatment globalFallbackTreatment) { + _globalFallbackTreatment = globalFallbackTreatment; + _byFlagFallbackTreatment = null; + } + + public FallbackTreatmentsConfiguration(String globalFallbackTreatment, Map byFlagFallbackTreatment) { + _globalFallbackTreatment = new FallbackTreatment(globalFallbackTreatment); + _byFlagFallbackTreatment = byFlagFallbackTreatment; + } + + public FallbackTreatmentsConfiguration(String globalFallbackTreatment) { + _globalFallbackTreatment = new FallbackTreatment(globalFallbackTreatment); + _byFlagFallbackTreatment = null; + } + + + public FallbackTreatmentsConfiguration(String globalFallbackTreatment, HashMap byFlagFallbackTreatment) { + _globalFallbackTreatment = new FallbackTreatment(globalFallbackTreatment); + _byFlagFallbackTreatment = buildByFlagFallbackTreatment(byFlagFallbackTreatment); + } + + public FallbackTreatmentsConfiguration(FallbackTreatment globalFallbackTreatment, HashMap byFlagFallbackTreatment) { + _globalFallbackTreatment = globalFallbackTreatment; + _byFlagFallbackTreatment = buildByFlagFallbackTreatment(byFlagFallbackTreatment); + } + public FallbackTreatment getGlobalFallbackTreatment() { return _globalFallbackTreatment; } public Map getByFlagFallbackTreatment() { return _byFlagFallbackTreatment;} + + private Map buildByFlagFallbackTreatment(Map byFlagFallbackTreatment) { + Map result = new HashMap<>(); + for (Map.Entry entry : byFlagFallbackTreatment.entrySet()) { + result.put(entry.getKey(), new FallbackTreatment(entry.getValue())); + } + + return result; + } } diff --git a/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java b/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java index 1152615a..2df64c08 100644 --- a/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java +++ b/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java @@ -1,5 +1,7 @@ package io.split.client; +import io.split.client.dtos.FallbackTreatment; +import io.split.client.dtos.FallbackTreatmentsConfiguration; import org.junit.Assert; import org.junit.Test; @@ -12,10 +14,14 @@ public class JsonLocalhostSplitFactoryTest { @Test public void works() throws IOException, URISyntaxException, InterruptedException, TimeoutException { + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-global"), + new HashMap() {{ put("feature", new FallbackTreatment("off-local", "{\"prop2\", \"val2\"}")); }}); + SplitClientConfig config = SplitClientConfig.builder() .splitFile("src/test/resources/splits_localhost.json") .segmentDirectory("src/test/resources") .setBlockUntilReadyTimeout(10000) + .fallbackTreatments(fallbackTreatmentsConfiguration) .build(); SplitFactory splitFactory = SplitFactoryBuilder.build("localhost", config); SplitClient client = splitFactory.client(); @@ -30,6 +36,9 @@ public void works() throws IOException, URISyntaxException, InterruptedException Assert.assertEquals("off", client.getTreatment("bilal", "test_split")); Assert.assertEquals("on", client.getTreatment("bilal", "push_test")); Assert.assertEquals("on_whitelist", client.getTreatment("admin", "push_test")); + Assert.assertEquals("off-local", client.getTreatment("bilal", "feature")); + Assert.assertEquals("on-global", client.getTreatment("bilal", "feature2")); + client.destroy(); } diff --git a/client/src/test/java/io/split/client/LocalhostSplitFactoryTest.java b/client/src/test/java/io/split/client/LocalhostSplitFactoryTest.java index 8c4ad4e0..983bf4cc 100644 --- a/client/src/test/java/io/split/client/LocalhostSplitFactoryTest.java +++ b/client/src/test/java/io/split/client/LocalhostSplitFactoryTest.java @@ -1,8 +1,11 @@ package io.split.client; import com.google.common.collect.Maps; +import io.split.client.dtos.FallbackTreatment; +import io.split.client.dtos.FallbackTreatmentsConfiguration; import io.split.client.utils.LocalhostUtils; import io.split.grammar.Treatments; +import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -10,6 +13,7 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.util.HashMap; import java.util.Map; import static org.junit.Assert.assertEquals; @@ -35,7 +39,9 @@ public void works() throws IOException, URISyntaxException, InterruptedException LocalhostUtils.writeFile(file, map); - SplitClientConfig config = SplitClientConfig.builder().splitFile(folder.getRoot().getAbsolutePath()).build(); + SplitClientConfig config = SplitClientConfig.builder() + .splitFile(folder.getRoot().getAbsolutePath()) + .build(); SplitFactory splitFactory = SplitFactoryBuilder.build("localhost", config); SplitClient client = splitFactory.client(); @@ -48,4 +54,30 @@ public void works() throws IOException, URISyntaxException, InterruptedException assertEquals("a", client.getTreatment("user1", "test")); assertEquals("a", client.getTreatment("user2", "test")); } + + @Test + public void testFallbackTreatments() throws IOException, URISyntaxException, InterruptedException { + File file = folder.newFile(LegacyLocalhostSplitChangeFetcher.FILENAME); + + Map map = Maps.newHashMap(); + map.put(SplitAndKey.of("onboarding"), LocalhostSplit.of("on")); + map.put(SplitAndKey.of("onboarding", "user1"), LocalhostSplit.of("off")); + map.put(SplitAndKey.of("onboarding", "user2"), LocalhostSplit.of("off")); + map.put(SplitAndKey.of("test"), LocalhostSplit.of("a")); + + LocalhostUtils.writeFile(file, map); + + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-global"), + new HashMap() {{ put("feature", new FallbackTreatment("off-local", "{\"prop2\", \"val2\"}")); }}); + + SplitClientConfig config = SplitClientConfig.builder() + .splitFile(folder.getRoot().getAbsolutePath()) + .fallbackTreatments(fallbackTreatmentsConfiguration) + .build(); + SplitFactory splitFactory = SplitFactoryBuilder.build("localhost", config); + SplitClient client = splitFactory.client(); + + assertEquals("off-local", client.getTreatment("user1", "feature")); + assertEquals("on-global", client.getTreatment("user1", "feature2")); + } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java b/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java index abcc551f..989da1a1 100644 --- a/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java +++ b/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java @@ -1,5 +1,7 @@ package io.split.client; +import io.split.client.dtos.FallbackTreatment; +import io.split.client.dtos.FallbackTreatmentsConfiguration; import io.split.client.utils.LocalhostUtils; import io.split.grammar.Treatments; import org.junit.Rule; @@ -11,10 +13,7 @@ import java.io.IOException; import java.io.StringWriter; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -106,4 +105,59 @@ public void works() throws IOException, URISyntaxException { // unchanged assertThat(client.getTreatment("user_b", "split_1"), is(equalTo("on"))); } + + @Test + public void testFallbackTreatment() throws IOException, URISyntaxException { + File file = folder.newFile(SplitClientConfig.LOCALHOST_DEFAULT_FILE); + + List> allSplits = new ArrayList(); + + Map split1_user_a = new LinkedHashMap<>(); + Map split1_user_a_data = new LinkedHashMap<>(); + split1_user_a_data.put("keys", "user_a"); + split1_user_a_data.put("treatment", "off"); + split1_user_a_data.put("config", "{ \"size\" : 20 }"); + split1_user_a.put("split_1", split1_user_a_data); + allSplits.add(split1_user_a); + + Map split1_user_b = new LinkedHashMap<>(); + Map split1_user_b_data = new LinkedHashMap<>(); + split1_user_b_data.put("keys", "user_b"); + split1_user_b_data.put("treatment", "on"); + split1_user_b.put("split_1", split1_user_b_data); + allSplits.add(split1_user_b); + + Map split2_user_a = new LinkedHashMap<>(); + Map split2_user_a_data = new LinkedHashMap<>(); + split2_user_a_data.put("keys", "user_a"); + split2_user_a_data.put("treatment", "off"); + split2_user_a_data.put("config", "{ \"size\" : 20 }"); + split2_user_a.put("split_2", split2_user_a_data); + allSplits.add(split2_user_a); + + + Yaml yaml = new Yaml(); + StringWriter writer = new StringWriter(); + yaml.dump(allSplits, writer); + + String expectedYaml = "- split_1: {keys: user_a, treatment: 'off', config: '{ \"size\" : 20 }'}\n" + + "- split_1: {keys: user_b, treatment: 'on'}\n" + + "- split_2: {keys: user_a, treatment: 'off', config: '{ \"size\" : 20 }'}\n"; + + assertEquals(expectedYaml, writer.toString()); + + LocalhostUtils.writeFile(file, writer); + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-global"), + new HashMap() {{ put("feature", new FallbackTreatment("off-local", "{\"prop2\", \"val2\"}")); }}); + + SplitClientConfig config = SplitClientConfig.builder() + .splitFile(file.getAbsolutePath()) + .fallbackTreatments(fallbackTreatmentsConfiguration) + .build(); + SplitFactory splitFactory = SplitFactoryBuilder.build("localhost", config); + SplitClient client = splitFactory.client(); + + assertEquals("off-local", client.getTreatment("user1", "feature")); + assertEquals("on-global", client.getTreatment("user1", "feature2")); + } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/SplitClientConfigTest.java b/client/src/test/java/io/split/client/SplitClientConfigTest.java index e8ed6dfd..69c95d03 100644 --- a/client/src/test/java/io/split/client/SplitClientConfigTest.java +++ b/client/src/test/java/io/split/client/SplitClientConfigTest.java @@ -367,18 +367,18 @@ public void mustUseP12PassKeyWithProxyMtls() throws MalformedURLException, FileN @Test public void fallbackTreatmentCheckRegex() { SplitClientConfig config = SplitClientConfig.builder() - .fallbackTreatments(new FallbackTreatmentsConfiguration(new FallbackTreatment("12#2"), null)) + .fallbackTreatments(new FallbackTreatmentsConfiguration(new FallbackTreatment("12#2"))) .build(); Assert.assertEquals(null, config.fallbackTreatments().getGlobalFallbackTreatment().getTreatment()); config = SplitClientConfig.builder() - .fallbackTreatments(new FallbackTreatmentsConfiguration(null, new HashMap() {{ put("flag", new FallbackTreatment("12#2")); }} )) + .fallbackTreatments(new FallbackTreatmentsConfiguration(new HashMap() {{ put("flag", new FallbackTreatment("12#2")); }} )) .build(); Assert.assertEquals(0, config.fallbackTreatments().getByFlagFallbackTreatment().size()); config = SplitClientConfig.builder() .fallbackTreatments(new FallbackTreatmentsConfiguration( - new FallbackTreatment("on"), + "on", new HashMap() {{ put("flag", new FallbackTreatment("off")); }} )) .build(); Assert.assertEquals("on", config.fallbackTreatments().getGlobalFallbackTreatment().getTreatment()); diff --git a/client/src/test/java/io/split/client/SplitClientImplTest.java b/client/src/test/java/io/split/client/SplitClientImplTest.java index 2556508c..26a85057 100644 --- a/client/src/test/java/io/split/client/SplitClientImplTest.java +++ b/client/src/test/java/io/split/client/SplitClientImplTest.java @@ -2308,8 +2308,7 @@ public void fallbackTreatmentWithExceptionsResult() { String fallbcakConfigGlobal = "{\"prop1\", \"val1\"}"; FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration( - new FallbackTreatment("on", fallbcakConfigGlobal), - null); + new FallbackTreatment("on", fallbcakConfigGlobal)); FallbackTreatmentCalculator fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); SplitClientImpl client = new SplitClientImpl( @@ -2388,7 +2387,7 @@ public void fallbackTreatmentWithExceptionsResult() { assertEquals("off", client.getTreatmentsWithConfigByFlagSets("adil@relateiq.com", Arrays.asList("flag")).get("feature").treatment()); assertEquals(fallbcakConfigByFlag, client.getTreatmentsWithConfigByFlagSets("adil@relateiq.com", Arrays.asList("flag")).get("feature").config()); - fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(null, + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration( new HashMap() {{ put("feature", new FallbackTreatment("off", fallbcakConfigByFlag)); }}); fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); @@ -2458,8 +2457,7 @@ public void fallbackTreatmentWithSplitNotFoundResult() { String fallbcakConfigGlobal = "{\"prop1\", \"val1\"}"; FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration( - new FallbackTreatment("on", fallbcakConfigGlobal), - null); + new FallbackTreatment("on", fallbcakConfigGlobal)); FallbackTreatmentCalculator fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); SplitClientImpl client = new SplitClientImpl( @@ -2573,7 +2571,7 @@ public void fallbackTreatmentWithSplitNotFoundResult() { assertEquals("on", results.get("test3").treatment()); assertEquals(fallbcakConfigGlobal, results.get("test3").config()); - fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(null, + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration( new HashMap() {{ put("test2", new FallbackTreatment("off-fallback", fallbcakConfigByFlag)); }}); fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index 4bdbc598..69b1b366 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -1282,8 +1282,7 @@ public MockResponse dispatch(RecordedRequest request) { server.start(); String serverURL = String.format("http://%s:%s", server.getHostName(), server.getPort()); - FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-fallback", "{\"prop1\", \"val1\"}"), - null); + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-fallback", "{\"prop1\", \"val1\"}")); SplitClientConfig config = SplitClientConfig.builder() .setBlockUntilReadyTimeout(10000) @@ -1354,7 +1353,7 @@ public MockResponse dispatch(RecordedRequest request) { server.start(); String serverURL = String.format("http://%s:%s", server.getHostName(), server.getPort()); - FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(null, + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration( new HashMap() {{ put("feature", new FallbackTreatment("off-fallback", "{\"prop2\", \"val2\"}")); }}); SplitClientConfig config = SplitClientConfig.builder() @@ -1427,8 +1426,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio server.start(); String serverURL = String.format("http://%s:%s", server.getHostName(), server.getPort()); - FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on-fallback", "{\"prop1\", \"val1\"}"), - null); + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration("on-fallback"); SplitClientConfig config = SplitClientConfig.builder() .setBlockUntilReadyTimeout(10000) diff --git a/client/src/test/java/io/split/client/SplitFactoryImplTest.java b/client/src/test/java/io/split/client/SplitFactoryImplTest.java index dcf51055..82f3ea8c 100644 --- a/client/src/test/java/io/split/client/SplitFactoryImplTest.java +++ b/client/src/test/java/io/split/client/SplitFactoryImplTest.java @@ -60,7 +60,7 @@ public void testFactoryInstantiation() throws Exception { .authServiceURL(AUTH_SERVICE) .setBlockUntilReadyTimeout(10000) .telemetryURL(SplitClientConfig.TELEMETRY_ENDPOINT) - .fallbackTreatments(new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), null)) + .fallbackTreatments(new FallbackTreatmentsConfiguration(new FallbackTreatment("on"))) .build(); SplitFactoryImpl splitFactory = new SplitFactoryImpl(API_KEY, splitClientConfig); diff --git a/client/src/test/java/io/split/client/dtos/FallbackTreatmentCalculationImpTest.java b/client/src/test/java/io/split/client/dtos/FallbackTreatmentCalculationImpTest.java index 4e082e00..854a1a0e 100644 --- a/client/src/test/java/io/split/client/dtos/FallbackTreatmentCalculationImpTest.java +++ b/client/src/test/java/io/split/client/dtos/FallbackTreatmentCalculationImpTest.java @@ -16,20 +16,20 @@ public class FallbackTreatmentCalculationImpTest { @Test public void TestWorks() { - FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), null); + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on")); FallbackTreatmentCalculator fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); assertEquals("on", fallbackTreatmentCalculator.resolve("anyflag", "exception").getTreatment()); assertEquals("fallback - exception", fallbackTreatmentCalculator.resolve("anyflag", "exception").getLabel()); fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), - new HashMap() {{ put("flag", new FallbackTreatment("off")); }} ); + new HashMap() {{ put("flag", "off"); }} ); fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); assertEquals("on", fallbackTreatmentCalculator.resolve("anyflag", "exception").getTreatment()); assertEquals("fallback - exception", fallbackTreatmentCalculator.resolve("anyflag", "exception").getLabel()); assertEquals("off", fallbackTreatmentCalculator.resolve("flag", "exception").getTreatment()); assertEquals("fallback - exception", fallbackTreatmentCalculator.resolve("flag", "exception").getLabel()); - fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(null, + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration( new HashMap() {{ put("flag", new FallbackTreatment("off")); }} ); fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); assertEquals("control", fallbackTreatmentCalculator.resolve("anyflag", "exception").getTreatment()); diff --git a/client/src/test/java/io/split/client/dtos/FallbackTreatmentConfigurationTest.java b/client/src/test/java/io/split/client/dtos/FallbackTreatmentConfigurationTest.java new file mode 100644 index 00000000..a09cf599 --- /dev/null +++ b/client/src/test/java/io/split/client/dtos/FallbackTreatmentConfigurationTest.java @@ -0,0 +1,32 @@ +package io.split.client.dtos; + +import org.junit.Test; + +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; + +public class FallbackTreatmentConfigurationTest { + + @Test + public void TestWorks() { + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on")); + assertEquals("on", fallbackTreatmentsConfiguration.getGlobalFallbackTreatment().getTreatment()); + assertEquals(null, fallbackTreatmentsConfiguration.getByFlagFallbackTreatment()); + + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on", "{\"prop\":\"val\"}"), + new HashMap() {{ put("flag", new FallbackTreatment("off", "{\"prop2\":\"val2\"}")); }} ); + assertEquals("on", fallbackTreatmentsConfiguration.getGlobalFallbackTreatment().getTreatment()); + assertEquals("{\"prop\":\"val\"}", fallbackTreatmentsConfiguration.getGlobalFallbackTreatment().getConfig()); + assertEquals(null, fallbackTreatmentsConfiguration.getGlobalFallbackTreatment().getLabel()); + assertEquals("off", fallbackTreatmentsConfiguration.getByFlagFallbackTreatment().get("flag").getTreatment()); + assertEquals("{\"prop2\":\"val2\"}", fallbackTreatmentsConfiguration.getByFlagFallbackTreatment().get("flag").getConfig()); + assertEquals(null, fallbackTreatmentsConfiguration.getByFlagFallbackTreatment().get("flag").getLabel()); + + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration("on", + new HashMap() {{ put("flag", "off"); }} ); + assertEquals("on", fallbackTreatmentsConfiguration.getGlobalFallbackTreatment().getTreatment()); + assertEquals("off", fallbackTreatmentsConfiguration.getByFlagFallbackTreatment().get("flag").getTreatment()); + + } +} diff --git a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java index 05a87a61..33ebf6d6 100644 --- a/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java +++ b/client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java @@ -228,7 +228,7 @@ public void evaluateWithPrerequisites() { @Test public void evaluateFallbackTreatmentWorks() { Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(null); - FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on"), null); + FallbackTreatmentsConfiguration fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new FallbackTreatment("on")); FallbackTreatmentCalculator fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); _evaluator = new EvaluatorImp(_splitCacheConsumer, _segmentCacheConsumer, _ruleBasedSegmentCacheConsumer, fallbackTreatmentCalculator); @@ -245,7 +245,7 @@ public void evaluateFallbackTreatmentWorks() { // using byflag only Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(null); Mockito.when(_splitCacheConsumer.get("another_name")).thenReturn(null); - fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(null, new HashMap() {{ put(SPLIT_NAME, new FallbackTreatment("off")); }} ); + fallbackTreatmentsConfiguration = new FallbackTreatmentsConfiguration(new HashMap() {{ put(SPLIT_NAME, new FallbackTreatment("off")); }} ); fallbackTreatmentCalculator = new FallbackTreatmentCalculatorImp(fallbackTreatmentsConfiguration); _evaluator = new EvaluatorImp(_splitCacheConsumer, _segmentCacheConsumer, _ruleBasedSegmentCacheConsumer, fallbackTreatmentCalculator); From 68187a542d63bd9eaaea6ec1d200008132fffc9a Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 14 Oct 2025 11:32:04 -0700 Subject: [PATCH 3/4] Added fix for redis manager splitnames --- client/pom.xml | 4 +-- .../UserCustomSplitAdapterConsumerTest.java | 28 +++++++++++++++++++ okhttp-modules/pom.xml | 4 +-- pluggable-storage/pom.xml | 2 +- pom.xml | 2 +- redis-wrapper/pom.xml | 4 +-- .../src/main/java/redis/RedisSingle.java | 4 ++- .../src/test/java/redis/RedisSingleTest.java | 24 ++++++++++++++++ testing/pom.xml | 4 +-- 9 files changed, 65 insertions(+), 11 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 93bb93ee..198f80a3 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -5,9 +5,9 @@ io.split.client java-client-parent - 4.18.1 + 4.18.2 - 4.18.1 + 4.18.2 java-client jar Java Client diff --git a/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java b/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java index d12badc0..befe9645 100644 --- a/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java +++ b/client/src/test/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterConsumerTest.java @@ -120,6 +120,34 @@ public void testGetAll() { Mockito.verify(_userStorageWrapper, Mockito.times(1)).getMany(Mockito.anyObject()); } + @Test + public void testGetSplitNames() { + Split split = getSplit(SPLIT_NAME); + Split split2 = getSplit(SPLIT_NAME+"2"); + List listResultExpected = Stream.of(split, split2).collect(Collectors.toList()); + Set keysResult = Stream.of(SPLIT_NAME, SPLIT_NAME+"2").collect(Collectors.toSet()); + Mockito.when(_userStorageWrapper.getKeysByPrefix(Mockito.anyObject())). + thenReturn(keysResult); + List splitsResult = _userCustomSplitAdapterConsumer.splitNames(); + Assert.assertNotNull(splitsResult); + Assert.assertEquals(listResultExpected.size(), splitsResult.size()); + Assert.assertEquals(SPLIT_NAME, splitsResult.get(1)); + Assert.assertEquals(SPLIT_NAME+"2", splitsResult.get(0)); + Mockito.verify(_userStorageWrapper, Mockito.times(1)).getKeysByPrefix(Mockito.anyString()); + + // default prefix + listResultExpected = Stream.of(split, split2).collect(Collectors.toList()); + keysResult = Stream.of("SPLITIO.split." + SPLIT_NAME, "SPLITIO.split." + SPLIT_NAME+"2").collect(Collectors.toSet()); + Mockito.when(_userStorageWrapper.getKeysByPrefix(Mockito.anyObject())). + thenReturn(keysResult); + + splitsResult = _userCustomSplitAdapterConsumer.splitNames(); + Assert.assertNotNull(splitsResult); + Assert.assertEquals(listResultExpected.size(), splitsResult.size()); + Assert.assertEquals(SPLIT_NAME, splitsResult.get(1)); + Assert.assertEquals(SPLIT_NAME+"2", splitsResult.get(0)); + } + @Test public void testGetAllWithWrapperFailing() { Mockito.when(_userStorageWrapper.get(PrefixAdapter.buildGetAllSplit())). diff --git a/okhttp-modules/pom.xml b/okhttp-modules/pom.xml index 2971f627..3753050e 100644 --- a/okhttp-modules/pom.xml +++ b/okhttp-modules/pom.xml @@ -5,10 +5,10 @@ java-client-parent io.split.client - 4.18.1 + 4.18.2 4.0.0 - 4.18.1 + 4.18.2 okhttp-modules jar http-modules diff --git a/pluggable-storage/pom.xml b/pluggable-storage/pom.xml index 0b67454d..c6d3abb7 100644 --- a/pluggable-storage/pom.xml +++ b/pluggable-storage/pom.xml @@ -6,7 +6,7 @@ java-client-parent io.split.client - 4.18.1 + 4.18.2 2.1.0 diff --git a/pom.xml b/pom.xml index 58fa6680..d0d321ed 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.split.client java-client-parent - 4.18.1 + 4.18.2 diff --git a/redis-wrapper/pom.xml b/redis-wrapper/pom.xml index 0794a708..486d5dc8 100644 --- a/redis-wrapper/pom.xml +++ b/redis-wrapper/pom.xml @@ -6,10 +6,10 @@ java-client-parent io.split.client - 4.18.1 + 4.18.2 redis-wrapper - 3.1.1 + 3.1.2 jar Package for Redis Wrapper Implementation Implements Redis Pluggable Storage diff --git a/redis-wrapper/src/main/java/redis/RedisSingle.java b/redis-wrapper/src/main/java/redis/RedisSingle.java index f55da2b4..97fdbbf9 100644 --- a/redis-wrapper/src/main/java/redis/RedisSingle.java +++ b/redis-wrapper/src/main/java/redis/RedisSingle.java @@ -95,7 +95,9 @@ public String getAndSet(String key, String item) throws Exception { public Set getKeysByPrefix(String prefix) throws Exception { try (Jedis jedis = this.jedisPool.getResource()) { Set keysWithPrefix = jedis.keys(_commonRedis.buildKeyWithPrefix(prefix)); - keysWithPrefix = keysWithPrefix.stream().map(key -> key.replace(_commonRedis.getPrefix() + ".", "")).collect(Collectors.toSet()); + if (!_commonRedis.getPrefix().isEmpty()) { + keysWithPrefix = keysWithPrefix.stream().map(key -> key.replace(_commonRedis.getPrefix() + ".", "")).collect(Collectors.toSet()); + } return keysWithPrefix; } catch (Exception ex) { throw new RedisException(ex.getMessage()); diff --git a/redis-wrapper/src/test/java/redis/RedisSingleTest.java b/redis-wrapper/src/test/java/redis/RedisSingleTest.java index 9b7e0c4d..8f70c324 100644 --- a/redis-wrapper/src/test/java/redis/RedisSingleTest.java +++ b/redis-wrapper/src/test/java/redis/RedisSingleTest.java @@ -104,6 +104,30 @@ public void testGetKeysByPrefix() throws Exception { } } + @Test + public void testGetKeysByPrefixWithoutCustomPrefix() throws Exception { + Map map = new HashMap<>(); + map.put("SPLITIO.item-1", "1"); + map.put("SPLITIO.item-2", "2"); + map.put("SPLITIO.item-3", "3"); + map.put("SPLITIO.i-4", "4"); + RedisSingle storageWrapper = new RedisSingle(new JedisPool(), ""); + try { + for (Map.Entry entry : map.entrySet()) { + storageWrapper.set(entry.getKey(), entry.getValue()); + } + + Set result = storageWrapper.getKeysByPrefix("SPLITIO.item*"); + + Assert.assertEquals(3, result.size()); + Assert.assertTrue(result.contains("SPLITIO.item-1")); + Assert.assertTrue(result.contains("SPLITIO.item-2")); + Assert.assertTrue(result.contains("SPLITIO.item-3")); + } finally { + storageWrapper.delete(new ArrayList<>(map.keySet())); + } + } + @Test public void testIncrementAndDecrement() throws Exception { Map map = new HashMap<>(); diff --git a/testing/pom.xml b/testing/pom.xml index 6439f616..d101f697 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -5,11 +5,11 @@ io.split.client java-client-parent - 4.18.1 + 4.18.2 java-client-testing jar - 4.18.1 + 4.18.2 Java Client For Testing Testing suite for Java SDK for Split From 20ef2ed7c101b23854b350e2db874c61030f12c3 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 15 Oct 2025 09:32:27 -0700 Subject: [PATCH 4/4] Prepare for 4.18.2 release --- CHANGES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index ff03376f..8973780e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +4.18.2 (Oct 15, 2025) +- Fixed an issue where Manager.splitNames() return incorrect formatted result using redis storage and no custom prefix. +- Added using String only parameter for treatments in FallbackTreatmentConfiguration class. + 4.18.1 (Sep 30, 2025) - Fixed an issue where Streaming client hangs during token renew process.