Repository: Unleash/unleash-client-java
Pull request #242 (2024-05-08 08:15:35)

chore: Use client spec 5.1.5 instead of 5.0.2
To make sure the SDK is up to date with our tests. This bumps to the newest client-spec.


Patch:
diff --git a/pom.xml b/pom.xml
index 0d5b1051..b95cd793 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
         <version.junit5>5.10.0</version.junit5>
         <version.okhttp>4.10.0</version.okhttp>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <version.unleash.specification>5.0.2</version.unleash.specification>
+        <version.unleash.specification>5.1.5</version.unleash.specification>
         <arguments />
         <version.jackson>2.14.3</version.jackson>
     </properties>
diff --git a/src/main/java/io/getunleash/strategy/FlexibleRolloutStrategy.java b/src/main/java/io/getunleash/strategy/FlexibleRolloutStrategy.java
index 40fbc44a..5c951869 100644
--- a/src/main/java/io/getunleash/strategy/FlexibleRolloutStrategy.java
+++ b/src/main/java/io/getunleash/strategy/FlexibleRolloutStrategy.java
@@ -59,8 +59,4 @@ public class FlexibleRolloutStrategy implements Strategy {
                 .map(norm -> percentage > 0 && norm <= percentage)
                 .orElse(false);
     }
-
-    private String getStickiness(Map<String, String> parameters) {
-        return parameters.getOrDefault("stickiness", "default");
-    }
 }
diff --git a/src/main/java/io/getunleash/strategy/Strategy.java b/src/main/java/io/getunleash/strategy/Strategy.java
index fe940303..e0e2fe7b 100644
--- a/src/main/java/io/getunleash/strategy/Strategy.java
+++ b/src/main/java/io/getunleash/strategy/Strategy.java
@@ -21,9 +21,13 @@ public interface Strategy {
             List<Constraint> constraints,
             @Nullable List<VariantDefinition> variants) {
         boolean enabled = isEnabled(parameters, unleashContext, constraints);
+        String strategyStickiness = getStickiness(parameters);
         return new FeatureEvaluationResult(
                 enabled,
-                enabled ? VariantUtil.selectVariant(parameters, variants, unleashContext) : null);
+                enabled
+                        ? VariantUtil.selectVariant(
+                                parameters, variants, unleashContext, strategyStickiness)
+                        : null);
     }
 
     /**
@@ -61,4 +65,11 @@ public interface Strategy {
         return ConstraintUtil.validate(constraints, unleashContext)
                 && isEnabled(parameters, unleashContext);
     }
+
+    default String getStickiness(@Nullable Map<String, String> parameters) {
+        if (parameters != null) {
+            return parameters.getOrDefault("stickiness", "default");
+        }
+        return null;
+    }
 }
diff --git a/src/main/java/io/getunleash/variant/VariantUtil.java b/src/main/java/io/getunleash/variant/VariantUtil.java
index 4e3796bf..dcd6ce92 100644
--- a/src/main/java/io/getunleash/variant/VariantUtil.java
+++ b/src/main/java/io/getunleash/variant/VariantUtil.java
@@ -95,7 +95,8 @@ public final class VariantUtil {
     public static @Nullable Variant selectVariant(
             Map<String, String> parameters,
             @Nullable List<VariantDefinition> variants,
-            UnleashContext context) {
+            UnleashContext context,
+            @Nullable String strategyStickiness) {
         if (variants != null) {
             int totalWeight = variants.stream().mapToInt(VariantDefinition::getWeight).sum();
             if (totalWeight <= 0) {
@@ -106,7 +107,7 @@ public final class VariantUtil {
                 return variantOverride.get().toVariant();
             }
 
-            Optional<String> customStickiness =
+            Optional<String> variantCustomStickiness =
                     variants.stream()
                             .filter(
                                     f ->
@@ -114,6 +115,14 @@ public final class VariantUtil {
                                                     && !"default".equals(f.getStickiness()))
                             .map(VariantDefinition::getStickiness)
                             .findFirst();
+            Optional<String> customStickiness;
+            if (!variantCustomStickiness.isPresent()) {
+                customStickiness =
+                        Optional.ofNullable(strategyStickiness)
+                                .filter(stickiness -> !stickiness.equalsIgnoreCase("default"));
+            } else {
+                customStickiness = variantCustomStickiness;
+            }
             int target =
                     StrategyUtils.getNormalizedNumber(
                             getSeed(context, customStickiness),
@@ -134,6 +143,13 @@ public final class VariantUtil {
         return null;
     }
 
+    public static @Nullable Variant selectVariant(
+            Map<String, String> parameters,
+            @Nullable List<VariantDefinition> variants,
+            UnleashContext context) {
+        return selectVariant(parameters, variants, context, null);
+    }
+
     /**
      * Uses the old pre 9.0.0 way of hashing for finding the Variant to return
      *


Test patch:
diff --git a/src/test/java/io/getunleash/strategy/StrategyVariantTest.java b/src/test/java/io/getunleash/strategy/StrategyVariantTest.java
new file mode 100644
index 00000000..6508c623
--- /dev/null
+++ b/src/test/java/io/getunleash/strategy/StrategyVariantTest.java
@@ -0,0 +1,128 @@
+package io.getunleash.strategy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.getunleash.FeatureEvaluationResult;
+import io.getunleash.UnleashContext;
+import io.getunleash.Variant;
+import io.getunleash.variant.Payload;
+import io.getunleash.variant.VariantDefinition;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import org.junit.jupiter.api.Test;
+
+public class StrategyVariantTest {
+
+    @Test
+    public void should_inherit_stickiness_from_the_strategy_first_variant() {
+        HashMap<String, String> params = new HashMap<>();
+        params.put("rollout", "100");
+        params.put("stickiness", "clientId");
+        params.put("groupId", "a");
+        FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy();
+        VariantDefinition varA =
+                new VariantDefinition(
+                        "variantNameA",
+                        1,
+                        new Payload("string", "variantValueA"),
+                        Collections.emptyList());
+        VariantDefinition varB =
+                new VariantDefinition(
+                        "variantNameB",
+                        1,
+                        new Payload("string", "variantValueB"),
+                        Collections.emptyList());
+
+        UnleashContext context = UnleashContext.builder().addProperty("clientId", "1").build();
+        FeatureEvaluationResult result =
+                strategy.getResult(
+                        params, context, Collections.emptyList(), Arrays.asList(varA, varB));
+        Variant selectedVariant = result.getVariant();
+        assert selectedVariant != null;
+        assertThat(selectedVariant.getName()).isEqualTo("variantNameA");
+    }
+
+    @Test
+    public void should_inherit_stickiness_from_the_strategy_second_variant() {
+        HashMap<String, String> params = new HashMap<>();
+        params.put("rollout", "100");
+        params.put("stickiness", "clientId");
+        params.put("groupId", "a");
+        FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy();
+        VariantDefinition varA =
+                new VariantDefinition(
+                        "variantNameA",
+                        1,
+                        new Payload("string", "variantValueA"),
+                        Collections.emptyList());
+        VariantDefinition varB =
+                new VariantDefinition(
+                        "variantNameB",
+                        1,
+                        new Payload("string", "variantValueB"),
+                        Collections.emptyList());
+
+        UnleashContext context = UnleashContext.builder().addProperty("clientId", "2").build();
+        FeatureEvaluationResult result =
+                strategy.getResult(
+                        params, context, Collections.emptyList(), Arrays.asList(varA, varB));
+        Variant selectedVariant = result.getVariant();
+        assert selectedVariant != null;
+        assertThat(selectedVariant.getName()).isEqualTo("variantNameB");
+    }
+
+    @Test
+    public void multiple_variants_should_choose_first_variant() {
+        HashMap<String, String> params = new HashMap<>();
+        params.put("rollout", "100");
+        params.put("groupId", "a");
+        params.put("stickiness", "default");
+        FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy();
+        VariantDefinition varA =
+                new VariantDefinition(
+                        "variantNameA",
+                        1,
+                        new Payload("string", "variantValueA"),
+                        Collections.emptyList());
+        VariantDefinition varB =
+                new VariantDefinition(
+                        "variantNameB",
+                        1,
+                        new Payload("string", "variantValueB"),
+                        Collections.emptyList());
+        UnleashContext context = UnleashContext.builder().userId("5").build();
+        FeatureEvaluationResult result =
+                strategy.getResult(
+                        params, context, Collections.emptyList(), Arrays.asList(varA, varB));
+        Variant selectedVariant = result.getVariant();
+        assertThat(selectedVariant.getName()).isEqualTo("variantNameA");
+    }
+
+    @Test
+    public void multiple_variants_should_choose_second_variant() {
+        HashMap<String, String> params = new HashMap<>();
+        params.put("rollout", "100");
+        params.put("groupId", "a");
+        params.put("stickiness", "default");
+        FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy();
+        VariantDefinition varA =
+                new VariantDefinition(
+                        "variantNameA",
+                        1,
+                        new Payload("string", "variantValueA"),
+                        Collections.emptyList());
+        VariantDefinition varB =
+                new VariantDefinition(
+                        "variantNameB",
+                        1,
+                        new Payload("string", "variantValueB"),
+                        Collections.emptyList());
+        UnleashContext context = UnleashContext.builder().userId("0").build();
+        FeatureEvaluationResult result =
+                strategy.getResult(
+                        params, context, Collections.emptyList(), Arrays.asList(varA, varB));
+        Variant selectedVariant = result.getVariant();
+        assertThat(selectedVariant.getName()).isEqualTo("variantNameB");
+    }
+}