From 6e5286b0ee51297e2fb330af7ca9b7aa78e32648 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 23 May 2025 10:17:10 -0700 Subject: [PATCH 1/2] Added prereq matcher --- .../engine/matchers/PrerequisitesMatcher.java | 60 +++++++++++++++++++ .../matchers/PrerequisitesMatcherTest.java | 52 ++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java create mode 100644 client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java diff --git a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java new file mode 100644 index 00000000..2fabf58f --- /dev/null +++ b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java @@ -0,0 +1,60 @@ +package io.split.engine.matchers; + +import io.split.client.dtos.Prerequisites; +import io.split.engine.evaluator.EvaluationContext; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class PrerequisitesMatcher implements Matcher { + private List _prerequisites; + + public PrerequisitesMatcher(List prerequisites) { + _prerequisites = prerequisites; + } + + @Override + public boolean match(Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) { + if (matchValue == null) { + return false; + } + + if (!(matchValue instanceof String)) { + return false; + } + for (Prerequisites prerequisites : _prerequisites) { + String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey, prerequisites.featureFlagName, attributes). treatment; + if (!prerequisites.treatments.contains(treatment)) { + return false; + } + } + return true; + } + + @Override + public String toString() { + StringBuilder bldr = new StringBuilder(); + bldr.append("prerequisites: "); + bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); + return bldr.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PrerequisitesMatcher that = (PrerequisitesMatcher) o; + + return Objects.equals(_prerequisites, that._prerequisites); + } + + @Override + public int hashCode() { + int result = _prerequisites != null ? _prerequisites.hashCode() : 0; + result = 31 * result + (_prerequisites != null ? _prerequisites.hashCode() : 0); + return result; + } +} diff --git a/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java b/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java new file mode 100644 index 00000000..23c2af1c --- /dev/null +++ b/client/src/test/java/io/split/engine/matchers/PrerequisitesMatcherTest.java @@ -0,0 +1,52 @@ +package io.split.engine.matchers; + +import io.split.client.dtos.Prerequisites; +import io.split.client.utils.Json; +import io.split.engine.evaluator.EvaluationContext; +import io.split.engine.evaluator.Evaluator; +import io.split.engine.evaluator.EvaluatorImp; +import io.split.storages.RuleBasedSegmentCache; +import io.split.storages.SegmentCache; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.List; + +/** + * Tests for Prerequisites matcher + */ +public class PrerequisitesMatcherTest { + + @Test + public void works() { + Evaluator evaluator = Mockito.mock(Evaluator.class); + EvaluationContext evaluationContext = new EvaluationContext(evaluator, Mockito.mock(SegmentCache.class), Mockito.mock(RuleBasedSegmentCache.class)); + List prerequisites = Arrays.asList(Json.fromJson("{\"n\": \"split1\", \"ts\": [\"on\"]}", Prerequisites.class), Json.fromJson("{\"n\": \"split2\", \"ts\": [\"off\"]}", Prerequisites.class)); + PrerequisitesMatcher matcher = new PrerequisitesMatcher(prerequisites); + Assert.assertEquals("prerequisites: split1 [on], split2 [off]", matcher.toString()); + PrerequisitesMatcher matcher2 = new PrerequisitesMatcher(prerequisites); + Assert.assertTrue(matcher.equals(matcher2)); + Assert.assertTrue(matcher.hashCode() != 0); + + Mockito.when(evaluator.evaluateFeature("user", "user", "split1", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("on", "")); + Mockito.when(evaluator.evaluateFeature("user", "user", "split2", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("off", "")); + Assert.assertTrue(matcher.match("user", "user", null, evaluationContext)); + + Mockito.when(evaluator.evaluateFeature("user", "user", "split2", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("on", "")); + Assert.assertFalse(matcher.match("user", "user", null, evaluationContext)); + } + + @Test + public void invalidParams() { + Evaluator evaluator = Mockito.mock(Evaluator.class); + EvaluationContext evaluationContext = new EvaluationContext(evaluator, Mockito.mock(SegmentCache.class), Mockito.mock(RuleBasedSegmentCache.class)); + + List prerequisites = Arrays.asList(Json.fromJson("{\"n\": \"split1\", \"ts\": [\"on\"]}", Prerequisites.class), Json.fromJson("{\"n\": \"split2\", \"ts\": [\"off\"]}", Prerequisites.class)); + PrerequisitesMatcher matcher = new PrerequisitesMatcher(prerequisites); + Mockito.when(evaluator.evaluateFeature("user", "user", "split1", null)).thenReturn(new EvaluatorImp.TreatmentLabelAndChangeNumber("on", "")); + Assert.assertFalse(matcher.match(null, null, null, evaluationContext)); + Assert.assertFalse(matcher.match(123, null, null, evaluationContext)); + } +} \ No newline at end of file From d84392052e9ee637bda9e701782ef27f7b1b0f56 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 23 May 2025 10:48:22 -0700 Subject: [PATCH 2/2] polish --- .../java/io/split/engine/matchers/PrerequisitesMatcher.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java index 2fabf58f..b6e11180 100644 --- a/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java +++ b/client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java @@ -25,7 +25,8 @@ public boolean match(Object matchValue, String bucketingKey, Map return false; } for (Prerequisites prerequisites : _prerequisites) { - String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey, prerequisites.featureFlagName, attributes). treatment; + String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey, + prerequisites.featureFlagName, attributes). treatment; if (!prerequisites.treatments.contains(treatment)) { return false; } @@ -37,7 +38,8 @@ public boolean match(Object matchValue, String bucketingKey, Map public String toString() { StringBuilder bldr = new StringBuilder(); bldr.append("prerequisites: "); - bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); + bldr.append(this._prerequisites.stream().map(pr -> pr.featureFlagName + " " + + pr.treatments.toString()).map(Object::toString).collect(Collectors.joining(", "))); return bldr.toString(); }