diff --git a/libyggdrasilffi.so b/libyggdrasilffi.so index ac685c1d6..abdd6d588 100755 Binary files a/libyggdrasilffi.so and b/libyggdrasilffi.so differ diff --git a/pom.xml b/pom.xml index a6c3dcb09..af1db9973 100644 --- a/pom.xml +++ b/pom.xml @@ -56,11 +56,9 @@ - unleash-engine + yggdrasil-engine io.getunleash - system - 0.0.1-SNAPSHOT - ${project.basedir}/unleash-engine-all.jar + 0.1.0-SNAPSHOT com.google.code.gson diff --git a/src/main/java/io/getunleash/DefaultUnleash.java b/src/main/java/io/getunleash/DefaultUnleash.java index 95445489a..a8765fd70 100644 --- a/src/main/java/io/getunleash/DefaultUnleash.java +++ b/src/main/java/io/getunleash/DefaultUnleash.java @@ -32,18 +32,6 @@ public class DefaultUnleash implements Unleash { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultUnleash.class); private static ConcurrentHashMap initCounts = new ConcurrentHashMap<>(); - private static final List BUILTIN_STRATEGIES = - Arrays.asList( - new DefaultStrategy(), - new ApplicationHostnameStrategy(), - new GradualRolloutRandomStrategy(), - new GradualRolloutSessionIdStrategy(), - new GradualRolloutUserIdStrategy(), - new RemoteAddressStrategy(), - new UserWithIdStrategy(), - new FlexibleRolloutStrategy()); - - public static final UnknownStrategy UNKNOWN_STRATEGY = new UnknownStrategy(); private final UnleashEngine unleashEngine; private final UnleashMetricService metricService; @@ -341,8 +329,6 @@ public void count(final String toggleName, boolean enabled) { private static Map buildStrategyMap(@Nullable Strategy[] strategies) { Map map = new HashMap<>(); - BUILTIN_STRATEGIES.forEach(strategy -> map.put(strategy.getName(), strategy)); - if (strategies != null) { for (Strategy strategy : strategies) { map.put(strategy.getName(), strategy); diff --git a/src/main/java/io/getunleash/UnleashContext.java b/src/main/java/io/getunleash/UnleashContext.java index fa619cbfb..bd9c25819 100644 --- a/src/main/java/io/getunleash/UnleashContext.java +++ b/src/main/java/io/getunleash/UnleashContext.java @@ -103,6 +103,7 @@ public UnleashContext applyStaticFields(UnleashConfig config) { if (!this.appName.isPresent()) { builder.appName(config.getAppName()); } + return builder.build(); } diff --git a/src/main/java/io/getunleash/strategy/ApplicationHostnameStrategy.java b/src/main/java/io/getunleash/strategy/ApplicationHostnameStrategy.java deleted file mode 100644 index dbd0769a6..000000000 --- a/src/main/java/io/getunleash/strategy/ApplicationHostnameStrategy.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.getunleash.strategy; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; - -public class ApplicationHostnameStrategy implements Strategy { - public static final String HOST_NAMES_PARAM = "hostNames"; - protected final String NAME = "applicationHostname"; - private final String hostname; - - public ApplicationHostnameStrategy() { - this.hostname = resolveHostname(); - } - - private String resolveHostname() { - String hostname = System.getProperty("hostname"); - if (hostname == null) { - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - hostname = "undefined"; - } - } - return hostname; - } - - @Override - public String getName() { - return NAME; - } - - @Override - public boolean isEnabled(Map parameters) { - return Optional.ofNullable(parameters.get(HOST_NAMES_PARAM)) - .map(hostString -> hostString.toLowerCase()) - .map(hostString -> Arrays.asList(hostString.split(",\\s*"))) - .map(hostList -> hostList.contains(hostname.toLowerCase())) - .orElse(false); - } -} diff --git a/src/main/java/io/getunleash/strategy/ConstraintUtil.java b/src/main/java/io/getunleash/strategy/ConstraintUtil.java deleted file mode 100644 index ec4907289..000000000 --- a/src/main/java/io/getunleash/strategy/ConstraintUtil.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.getunleash.strategy; - -import static io.getunleash.Operator.DATE_AFTER; -import static io.getunleash.Operator.DATE_BEFORE; -import static io.getunleash.Operator.IN; -import static io.getunleash.Operator.NOT_IN; -import static io.getunleash.Operator.NUM_EQ; -import static io.getunleash.Operator.NUM_GT; -import static io.getunleash.Operator.NUM_GTE; -import static io.getunleash.Operator.NUM_LT; -import static io.getunleash.Operator.NUM_LTE; -import static io.getunleash.Operator.SEMVER_EQ; -import static io.getunleash.Operator.SEMVER_GT; -import static io.getunleash.Operator.SEMVER_LT; -import static io.getunleash.Operator.STR_CONTAINS; -import static io.getunleash.Operator.STR_ENDS_WITH; -import static io.getunleash.Operator.STR_STARTS_WITH; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import io.getunleash.lang.Nullable; -import io.getunleash.strategy.constraints.ConstraintOperator; -import io.getunleash.strategy.constraints.DateConstraintOperator; -import io.getunleash.strategy.constraints.NumberConstraintOperator; -import io.getunleash.strategy.constraints.SemverConstraintOperator; -import io.getunleash.strategy.constraints.StringConstraintOperator; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -public class ConstraintUtil { - private static final Map operators = new HashMap<>(); - - static { - operators.put(STR_CONTAINS, new StringConstraintOperator(Locale.ROOT)); - operators.put(STR_ENDS_WITH, new StringConstraintOperator(Locale.ROOT)); - operators.put(STR_STARTS_WITH, new StringConstraintOperator(Locale.ROOT)); - operators.put(IN, new StringConstraintOperator(Locale.ROOT)); - operators.put(NOT_IN, new StringConstraintOperator(Locale.ROOT)); - operators.put(NUM_LT, new NumberConstraintOperator()); - operators.put(NUM_LTE, new NumberConstraintOperator()); - operators.put(NUM_EQ, new NumberConstraintOperator()); - operators.put(NUM_GTE, new NumberConstraintOperator()); - operators.put(NUM_GT, new NumberConstraintOperator()); - operators.put(SEMVER_LT, new SemverConstraintOperator()); - operators.put(SEMVER_EQ, new SemverConstraintOperator()); - operators.put(SEMVER_GT, new SemverConstraintOperator()); - operators.put(DATE_BEFORE, new DateConstraintOperator()); - operators.put(DATE_AFTER, new DateConstraintOperator()); - } - - public static boolean validate(@Nullable List constraints, UnleashContext context) { - if (constraints != null && constraints.size() > 0) { - return constraints.stream().allMatch(c -> validateConstraint(c, context)); - } else { - return true; - } - } - - private static boolean validateConstraint(Constraint constraint, UnleashContext context) { - ConstraintOperator operator = operators.get(constraint.getOperator()); - if (operator == null) return false; - return constraint.isInverted() ^ operator.evaluate(constraint, context); - } -} diff --git a/src/main/java/io/getunleash/strategy/FlexibleRolloutStrategy.java b/src/main/java/io/getunleash/strategy/FlexibleRolloutStrategy.java deleted file mode 100644 index 40fbc44ab..000000000 --- a/src/main/java/io/getunleash/strategy/FlexibleRolloutStrategy.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.getunleash.strategy; - -import io.getunleash.UnleashContext; -import java.util.Map; -import java.util.Optional; -import java.util.function.Supplier; - -public class FlexibleRolloutStrategy implements Strategy { - protected static final String PERCENTAGE = "rollout"; - protected static final String GROUP_ID = "groupId"; - - private Supplier randomGenerator; - - public FlexibleRolloutStrategy() { - this.randomGenerator = () -> Math.random() * 10000 + ""; - } - - public FlexibleRolloutStrategy(Supplier randomGenerator) { - this.randomGenerator = randomGenerator; - } - - @Override - public String getName() { - return "flexibleRollout"; - } - - @Override - public boolean isEnabled(Map parameters) { - return false; - } - - private Optional resolveStickiness(String stickiness, UnleashContext context) { - switch (stickiness) { - case "userId": - return context.getUserId(); - case "sessionId": - return context.getSessionId(); - case "random": - return Optional.of(randomGenerator.get()); - case "default": - String value = - context.getUserId() - .orElse(context.getSessionId().orElse(this.randomGenerator.get())); - return Optional.of(value); - default: - return context.getByName(stickiness); - } - } - - @Override - public boolean isEnabled(Map parameters, UnleashContext unleashContext) { - final String stickiness = getStickiness(parameters); - final Optional stickinessId = resolveStickiness(stickiness, unleashContext); - final int percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE)); - final String groupId = parameters.getOrDefault(GROUP_ID, ""); - - return stickinessId - .map(stick -> StrategyUtils.getNormalizedNumber(stick, groupId, 0)) - .map(norm -> percentage > 0 && norm <= percentage) - .orElse(false); - } - - private String getStickiness(Map parameters) { - return parameters.getOrDefault("stickiness", "default"); - } -} diff --git a/src/main/java/io/getunleash/strategy/GradualRolloutRandomStrategy.java b/src/main/java/io/getunleash/strategy/GradualRolloutRandomStrategy.java deleted file mode 100644 index 2e448d3c0..000000000 --- a/src/main/java/io/getunleash/strategy/GradualRolloutRandomStrategy.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.getunleash.strategy; - -import java.util.Map; -import java.util.Random; - -public final class GradualRolloutRandomStrategy implements Strategy { - protected static final String PERCENTAGE = "percentage"; - private static final String STRATEGY_NAME = "gradualRolloutRandom"; - - private final Random random; - - public GradualRolloutRandomStrategy() { - random = new Random(); - } - - protected GradualRolloutRandomStrategy(long seed) { - random = new Random(seed); - } - - @Override - public String getName() { - return STRATEGY_NAME; - } - - @Override - public boolean isEnabled(final Map parameters) { - int percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE)); - int randomNumber = random.nextInt(100) + 1; - return percentage >= randomNumber; - } -} diff --git a/src/main/java/io/getunleash/strategy/GradualRolloutSessionIdStrategy.java b/src/main/java/io/getunleash/strategy/GradualRolloutSessionIdStrategy.java deleted file mode 100644 index 1075231f5..000000000 --- a/src/main/java/io/getunleash/strategy/GradualRolloutSessionIdStrategy.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.getunleash.strategy; - -import io.getunleash.UnleashContext; -import java.util.Map; -import java.util.Optional; - -/** - * Implements a gradual roll-out strategy based on session id. - * - *

Using this strategy you can target only users bound to a session and gradually expose your - * feature to higher percentage of the logged in user. - * - *

This strategy takes two parameters: - percentage : a number between 0 and 100. The percentage - * you want to enable the feature for. - groupId : a groupId used for rolling out the feature. By - * using the same groupId for different toggles you can correlate the user experience across - * toggles. - */ -public final class GradualRolloutSessionIdStrategy implements Strategy { - protected static final String PERCENTAGE = "percentage"; - protected static final String GROUP_ID = "groupId"; - - private static final String NAME = "gradualRolloutSessionId"; - - @Override - public String getName() { - return NAME; - } - - @Override - public boolean isEnabled(Map parameters) { - return false; - } - - @Override - public boolean isEnabled(final Map parameters, UnleashContext unleashContext) { - Optional sessionId = unleashContext.getSessionId(); - - if (!sessionId.isPresent()) { - return false; - } - - final int percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE)); - final String groupId = parameters.getOrDefault(GROUP_ID, ""); - - final int normalizedSessionId = - StrategyUtils.getNormalizedNumber(sessionId.get(), groupId, 0); - - return percentage > 0 && normalizedSessionId <= percentage; - } -} diff --git a/src/main/java/io/getunleash/strategy/GradualRolloutUserIdStrategy.java b/src/main/java/io/getunleash/strategy/GradualRolloutUserIdStrategy.java deleted file mode 100644 index a3dd8a735..000000000 --- a/src/main/java/io/getunleash/strategy/GradualRolloutUserIdStrategy.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.getunleash.strategy; - -import io.getunleash.UnleashContext; -import java.util.Map; -import java.util.Optional; - -/** - * Implements a gradual roll-out strategy based on userId. - * - *

Using this strategy you can target only logged in users and gradually expose your feature to - * higher percentage of the logged in user. - * - *

This strategy takes two parameters: - percentage : a number between 0 and 100. The percentage - * you want to enable the feature for. - groupId : a groupId used for rolling out the feature. By - * using the same groupId for different toggles you can correlate the user experience across - * toggles. - */ -public final class GradualRolloutUserIdStrategy implements Strategy { - protected static final String PERCENTAGE = "percentage"; - protected static final String GROUP_ID = "groupId"; - - private static final String NAME = "gradualRolloutUserId"; - - @Override - public String getName() { - return NAME; - } - - @Override - public boolean isEnabled(Map parameters) { - return false; - } - - @Override - public boolean isEnabled(final Map parameters, UnleashContext unleashContext) { - Optional userId = unleashContext.getUserId(); - - if (!userId.isPresent()) { - return false; - } - - final int percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE)); - final String groupId = parameters.getOrDefault(GROUP_ID, ""); - - final int normalizedUserId = StrategyUtils.getNormalizedNumber(userId.get(), groupId, 0); - - return percentage > 0 && normalizedUserId <= percentage; - } -} diff --git a/src/main/java/io/getunleash/strategy/RemoteAddressStrategy.java b/src/main/java/io/getunleash/strategy/RemoteAddressStrategy.java deleted file mode 100644 index 05edd33ff..000000000 --- a/src/main/java/io/getunleash/strategy/RemoteAddressStrategy.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.getunleash.strategy; - -import io.getunleash.UnleashContext; -import io.getunleash.util.IpAddressMatcher; -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -public final class RemoteAddressStrategy implements Strategy { - static final String PARAM = "IPs"; - private static final String STRATEGY_NAME = "remoteAddress"; - private static final Pattern SPLITTER = Pattern.compile(","); - - @Override - public String getName() { - return STRATEGY_NAME; - } - - @Override - public boolean isEnabled(Map parameters) { - return false; - } - - @Override - public boolean isEnabled(Map parameters, UnleashContext context) { - return Optional.ofNullable(parameters.get(PARAM)) - .map(ips -> Arrays.asList(SPLITTER.split(ips, -1))) - .map( - ips -> - ips.stream() - .flatMap( - ipAddress -> - buildIpAddressMatcher(ipAddress) - .map(Stream::of) - .orElseGet(Stream::empty)) - .map( - subnet -> - context.getRemoteAddress() - .map(subnet::matches) - .orElse(false)) - .anyMatch(Boolean.TRUE::equals)) - .orElse(false); - } - - private Optional buildIpAddressMatcher(String ipAddress) { - try { - return Optional.of(new IpAddressMatcher(ipAddress)); - } catch (IllegalArgumentException ex) { - return Optional.empty(); - } - } -} diff --git a/src/main/java/io/getunleash/strategy/Strategy.java b/src/main/java/io/getunleash/strategy/Strategy.java index 25da0eefe..9c2f0d753 100644 --- a/src/main/java/io/getunleash/strategy/Strategy.java +++ b/src/main/java/io/getunleash/strategy/Strategy.java @@ -1,12 +1,6 @@ package io.getunleash.strategy; -import io.getunleash.Constraint; -import io.getunleash.FeatureEvaluationResult; import io.getunleash.UnleashContext; -import io.getunleash.lang.Nullable; -import io.getunleash.variant.VariantDefinition; -import io.getunleash.variant.VariantUtil; -import java.util.List; import java.util.Map; public interface Strategy { @@ -15,30 +9,7 @@ public interface Strategy { boolean isEnabled(Map parameters); - /** @deprecated don't use this method, currently only accessible from tests */ - @Deprecated - default FeatureEvaluationResult getResult( - Map parameters, - UnleashContext unleashContext, - List constraints, - @Nullable List variants) { - boolean enabled = isEnabled(parameters, unleashContext, constraints); - return new FeatureEvaluationResult( - enabled, - enabled ? VariantUtil.selectVariant(parameters, variants, unleashContext) : null); - } - default boolean isEnabled(Map parameters, UnleashContext unleashContext) { return isEnabled(parameters); } - - /** @deprecated constraint validation should be delegated to Yggdrasil */ - @Deprecated - default boolean isEnabled( - Map parameters, - UnleashContext unleashContext, - List constraints) { - return ConstraintUtil.validate(constraints, unleashContext) - && isEnabled(parameters, unleashContext); - } } diff --git a/src/main/java/io/getunleash/strategy/StrategyUtils.java b/src/main/java/io/getunleash/strategy/StrategyUtils.java index 6b22c3c3d..d5c630b5f 100644 --- a/src/main/java/io/getunleash/strategy/StrategyUtils.java +++ b/src/main/java/io/getunleash/strategy/StrategyUtils.java @@ -1,32 +1,10 @@ package io.getunleash.strategy; import com.sangupta.murmur.Murmur3; -import io.getunleash.lang.Nullable; public final class StrategyUtils { private static final int ONE_HUNDRED = 100; - public static boolean isNotEmpty(final CharSequence cs) { - return !isEmpty(cs); - } - - public static boolean isEmpty(@Nullable final CharSequence cs) { - return cs == null || cs.length() == 0; - } - - public static boolean isNumeric(final CharSequence cs) { - if (isEmpty(cs)) { - return false; - } - final int sz = cs.length(); - for (int i = 0; i < sz; i++) { - if (Character.isDigit(cs.charAt(i)) == false) { - return false; - } - } - return true; - } - /** * Takes to string inputs concat them, produce a hash and return a normalized value between 0 * and 100; @@ -36,30 +14,8 @@ public static boolean isNumeric(final CharSequence cs) { * @return */ public static int getNormalizedNumber(String identifier, String groupId, long seed) { - return getNormalizedNumber(identifier, groupId, ONE_HUNDRED, seed); - } - - public static int getNormalizedNumber( - String identifier, String groupId, int normalizer, long seed) { byte[] value = (groupId + ':' + identifier).getBytes(); long hash = Murmur3.hash_x86_32(value, value.length, seed); - return (int) (hash % normalizer) + 1; - } - - /** - * Takes a numeric string value and converts it to a integer between 0 and 100. - * - *

returns 0 if the string is not numeric. - * - * @param percentage - A numeric string value - * @return a integer between 0 and 100 - */ - public static int getPercentage(String percentage) { - if (isNotEmpty(percentage) && isNumeric(percentage)) { - int p = Integer.parseInt(percentage); - return p; - } else { - return 0; - } + return (int) (hash % ONE_HUNDRED) + 1; } } diff --git a/src/main/java/io/getunleash/strategy/UnknownStrategy.java b/src/main/java/io/getunleash/strategy/UnknownStrategy.java deleted file mode 100644 index 326fb4f76..000000000 --- a/src/main/java/io/getunleash/strategy/UnknownStrategy.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.getunleash.strategy; - -import java.util.Map; - -public final class UnknownStrategy implements Strategy { - - private static final String STRATEGY_NAME = "unknown"; - - @Override - public String getName() { - return STRATEGY_NAME; - } - - @Override - public boolean isEnabled(Map parameters) { - return false; - } -} diff --git a/src/main/java/io/getunleash/strategy/UserWithIdStrategy.java b/src/main/java/io/getunleash/strategy/UserWithIdStrategy.java deleted file mode 100644 index 6fbe0bf2c..000000000 --- a/src/main/java/io/getunleash/strategy/UserWithIdStrategy.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.getunleash.strategy; - -import static java.util.Arrays.asList; - -import io.getunleash.UnleashContext; -import java.util.Map; -import java.util.Optional; - -public final class UserWithIdStrategy implements Strategy { - - protected static final String PARAM = "userIds"; - private static final String STRATEGY_NAME = "userWithId"; - - @Override - public String getName() { - return STRATEGY_NAME; - } - - @Override - public boolean isEnabled(Map parameters) { - return false; - } - - @Override - public boolean isEnabled(Map parameters, UnleashContext unleashContext) { - return unleashContext - .getUserId() - .map( - currentUserId -> - Optional.ofNullable(parameters.get(PARAM)) - .map(userIdString -> asList(userIdString.split(",\\s?"))) - .filter(f -> f.contains(currentUserId)) - .isPresent()) - .orElse(false); - } -} diff --git a/src/main/java/io/getunleash/strategy/constraints/ConstraintOperator.java b/src/main/java/io/getunleash/strategy/constraints/ConstraintOperator.java deleted file mode 100644 index 62a4f77e7..000000000 --- a/src/main/java/io/getunleash/strategy/constraints/ConstraintOperator.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.getunleash.strategy.constraints; - -import io.getunleash.Constraint; -import io.getunleash.UnleashContext; - -public interface ConstraintOperator { - boolean evaluate(Constraint constraint, UnleashContext context); -} diff --git a/src/main/java/io/getunleash/strategy/constraints/DateConstraintOperator.java b/src/main/java/io/getunleash/strategy/constraints/DateConstraintOperator.java deleted file mode 100644 index 96da1758f..000000000 --- a/src/main/java/io/getunleash/strategy/constraints/DateConstraintOperator.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.getunleash.strategy.constraints; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import java.time.ZonedDateTime; - -public class DateConstraintOperator implements ConstraintOperator { - - @Override - public boolean evaluate(Constraint constraint, UnleashContext context) { - ZonedDateTime dateToMatch = - context.getByName(constraint.getContextName()) - .map(DateParser::parseDate) - .orElseGet(() -> context.getCurrentTime().orElseGet(ZonedDateTime::now)); - try { - ZonedDateTime value = DateParser.parseDate(constraint.getValue()); - return eval(constraint.getOperator(), value, dateToMatch); - } catch (Exception e) { - return false; - } - } - - private boolean eval(Operator op, ZonedDateTime value, ZonedDateTime toMatch) { - switch (op) { - case DATE_AFTER: - return toMatch.isAfter(value); - case DATE_BEFORE: - return toMatch.isBefore(value); - default: - return false; - } - } -} diff --git a/src/main/java/io/getunleash/strategy/constraints/NumberConstraintOperator.java b/src/main/java/io/getunleash/strategy/constraints/NumberConstraintOperator.java deleted file mode 100644 index bb703332d..000000000 --- a/src/main/java/io/getunleash/strategy/constraints/NumberConstraintOperator.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.getunleash.strategy.constraints; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import java.util.Objects; - -public class NumberConstraintOperator implements ConstraintOperator { - @Override - public boolean evaluate(Constraint constraint, UnleashContext context) { - return context.getByName(constraint.getContextName()) - .map( - cVal -> { - try { - return Double.parseDouble(cVal); - } catch (NumberFormatException nfe) { - return null; - } - }) - .map( - cVal -> { - try { - if (constraint.getValues().size() > 0) { - return constraint.getValues().stream() - .map( - v -> { - try { - return Double.parseDouble(v); - } catch (NumberFormatException nfe) { - return null; - } - }) - .filter(Objects::nonNull) - .anyMatch(v -> eval(constraint.getOperator(), v, cVal)); - } else if (constraint.getValue() != null - && constraint.getValue().length() > 0) { - Double value = Double.parseDouble(constraint.getValue()); - return eval(constraint.getOperator(), value, cVal); - } else { - return null; - } - } catch (NumberFormatException nfe) { - return null; - } - }) - .orElse(false); - } - - private boolean eval(Operator operator, Double value, Double contextValue) { - - switch (operator) { - case NUM_LT: - return contextValue < value; - case NUM_LTE: - return contextValue <= value; - case NUM_EQ: - return contextValue.equals(value); - case NUM_GTE: - return contextValue >= value; - case NUM_GT: - return contextValue > value; - default: - return false; - } - } -} diff --git a/src/main/java/io/getunleash/strategy/constraints/SemanticVersion.java b/src/main/java/io/getunleash/strategy/constraints/SemanticVersion.java deleted file mode 100644 index e73101b1c..000000000 --- a/src/main/java/io/getunleash/strategy/constraints/SemanticVersion.java +++ /dev/null @@ -1,183 +0,0 @@ -package io.getunleash.strategy.constraints; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Simple implementation of semantic version parsing and comparison according to the Semantic - * Versions 2.0.0 standard (http://semver.org). - */ -public class SemanticVersion implements Comparable { - private static Pattern VERSION_REGEX = - Pattern.compile( - "^(?0|[1-9]\\d*)(\\.(?0|[1-9]\\d*))?(\\.(?0|[1-9]\\d*))?" - + "(\\-(?[0-9A-Za-z\\-\\.]+))?(\\+(?[0-9A-Za-z\\-\\.]+))?$"); - - public static class InvalidVersionException extends Exception { - public InvalidVersionException(String message) { - super(message); - } - } - - private final int major; - private final int minor; - private final int patch; - private final String preRelease; - private final String[] preReleaseComponents; - private final String build; - - public SemanticVersion(int major, int minor, int patch, String preRelease, String build) { - this.major = major; - this.minor = minor; - this.patch = patch; - this.preRelease = preRelease; - this.preReleaseComponents = preRelease == null ? null : preRelease.split("\\."); - this.build = build; - } - - public int getMajor() { - return major; - } - - public int getMinor() { - return minor; - } - - public int getPatch() { - return patch; - } - - public String getPreRelease() { - return preRelease; - } - - public String[] getPreReleaseComponents() { - return preReleaseComponents; - } - - public String getBuild() { - return build; - } - - /** - * Attempts to parse a string as a semantic version according to the Semver 2.0.0 specification. - * - * @param input the input string - * @return a SemanticVersion instance - * @throws InvalidVersionException if the version could not be parsed - */ - public static SemanticVersion parse(String input) throws InvalidVersionException { - return parse(input, false); - } - - /** - * Attempts to parse a string as a semantic version according to the Semver 2.0.0 specification, - * except that the minor and patch versions may optionally be omitted. - * - * @param input the input string - * @param allowMissingMinorAndPatch true if the parser should tolerate the absence of a minor - * and/or patch version; if absent, they will be treated as zero - * @return a SemanticVersion instance - * @throws InvalidVersionException if the version could not be parsed - */ - public static SemanticVersion parse(String input, boolean allowMissingMinorAndPatch) - throws InvalidVersionException { - Matcher matcher = VERSION_REGEX.matcher(input); - if (!matcher.matches()) { - throw new InvalidVersionException("Invalid semantic version"); - } - int major, minor, patch; - try { - major = Integer.parseInt(matcher.group("major")); - if (!allowMissingMinorAndPatch) { - if (matcher.group("minor") == null || matcher.group("patch") == null) { - throw new InvalidVersionException("Invalid semantic version"); - } - } - minor = matcher.group("minor") == null ? 0 : Integer.parseInt(matcher.group("minor")); - patch = matcher.group("patch") == null ? 0 : Integer.parseInt(matcher.group("patch")); - } catch (NumberFormatException e) { - // COVERAGE: This should be impossible, because our regex should only match if these - // strings are numeric. - throw new InvalidVersionException("Invalid semantic version"); - } - String prerelease = matcher.group("prerel"); - String build = matcher.group("build"); - return new SemanticVersion(major, minor, patch, prerelease, build); - } - - @Override - public int compareTo(SemanticVersion other) { - return comparePrecedence(other); - } - - /** - * Compares this object with another SemanticVersion according to Semver 2.0.0 precedence rules. - * - * @param other another SemanticVersion - * @return 0 if equal, -1 if the current object has lower precedence, or 1 if the current object - * has higher precedence - */ - public int comparePrecedence(SemanticVersion other) { - if (other == null) { - return 1; - } - if (major != other.major) { - return Integer.compare(major, other.major); - } - if (minor != other.minor) { - return Integer.compare(minor, other.minor); - } - if (patch != other.patch) { - return Integer.compare(patch, other.patch); - } - if (preRelease == null && other.preRelease == null) { - return 0; - } - // *no* prerelease component always has higher precedence than *any* prerelease component - if (preRelease == null) { - return 1; - } - if (other.preRelease == null) { - return -1; - } - return compareIdentifiers(preReleaseComponents, other.preReleaseComponents); - } - - private int compareIdentifiers(String[] ids1, String[] ids2) { - for (int i = 0; ; i++) { - if (i >= ids1.length) { - // x.y is always less than x.y.z - return (i >= ids2.length) ? 0 : -1; - } - if (i >= ids2.length) { - return 1; - } - // each sub-identifier is compared numerically if both are numeric; if both are - // non-numeric, - // they're compared as strings; otherwise, the numeric one is the lesser one - int n1 = 0, n2 = 0, d; - boolean isNum1, isNum2; - try { - n1 = Integer.parseInt(ids1[i]); - isNum1 = true; - } catch (NumberFormatException e) { - isNum1 = false; - } - try { - n2 = Integer.parseInt(ids2[i]); - isNum2 = true; - } catch (NumberFormatException e) { - isNum2 = false; - } - if (isNum1 && isNum2) { - d = Integer.compare(n1, n2); - } else { - d = isNum1 ? -1 : (isNum2 ? 1 : ids1[i].compareTo(ids2[i])); - } - if (d != 0) { - return d; - } - } - } -} diff --git a/src/main/java/io/getunleash/strategy/constraints/SemverConstraintOperator.java b/src/main/java/io/getunleash/strategy/constraints/SemverConstraintOperator.java deleted file mode 100644 index 6821047eb..000000000 --- a/src/main/java/io/getunleash/strategy/constraints/SemverConstraintOperator.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.getunleash.strategy.constraints; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import java.util.Objects; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SemverConstraintOperator implements ConstraintOperator { - private static final Logger LOGGER = LoggerFactory.getLogger(SemverConstraintOperator.class); - - @Override - public boolean evaluate(Constraint constraint, UnleashContext context) { - return context.getByName(constraint.getContextName()) - .map( - contextValue -> { - try { - return SemanticVersion.parse(contextValue); - } catch ( - SemanticVersion.InvalidVersionException - invalidVersionException) { - LOGGER.info( - "Couldn't parse version [{}] from context - This is dynamic on evaluation, might not be your fault", - contextValue); - return null; - } - }) - .map( - contextVersion -> { - try { - if (constraint.getValues().size() > 0) { - return constraint.getValues().stream() - .map( - v -> { - try { - return SemanticVersion.parse(v); - } catch ( - SemanticVersion - .InvalidVersionException - e) { - return null; - } - }) - .filter(Objects::nonNull) - .anyMatch( - v -> - eval( - constraint.getOperator(), - v, - contextVersion)); - } else if (constraint.getValue() != null - && constraint.getValue().length() > 0) { - SemanticVersion value = - SemanticVersion.parse(constraint.getValue()); - return eval(constraint.getOperator(), value, contextVersion); - } else { - return null; - } - } catch (SemanticVersion.InvalidVersionException ive) { - return null; - } - }) - .orElse(false); - } - - private boolean eval(Operator operator, SemanticVersion value, SemanticVersion contextVersion) { - switch (operator) { - case SEMVER_LT: - return contextVersion.compareTo(value) < 0; - case SEMVER_EQ: - return contextVersion.compareTo(value) == 0; - case SEMVER_GT: - return contextVersion.compareTo(value) > 0; - default: - return false; - } - } -} diff --git a/src/main/java/io/getunleash/strategy/constraints/StringConstraintOperator.java b/src/main/java/io/getunleash/strategy/constraints/StringConstraintOperator.java deleted file mode 100644 index 5a300b420..000000000 --- a/src/main/java/io/getunleash/strategy/constraints/StringConstraintOperator.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.getunleash.strategy.constraints; - -import io.getunleash.Constraint; -import io.getunleash.UnleashContext; -import java.util.List; -import java.util.Locale; -import java.util.Optional; - -public class StringConstraintOperator implements ConstraintOperator { - private Locale comparisonLocale; - - public StringConstraintOperator(Locale comparisonLocale) { - this.comparisonLocale = comparisonLocale; - } - - @Override - public boolean evaluate(Constraint constraint, UnleashContext context) { - List values = constraint.getValues(); - Optional contextValue = context.getByName(constraint.getContextName()); - boolean caseInsensitive = constraint.isCaseInsensitive(); - switch (constraint.getOperator()) { - case IN: - return isIn(values, contextValue, caseInsensitive); - case NOT_IN: - return !isIn(values, contextValue, caseInsensitive); - case STR_CONTAINS: - return contains(values, contextValue, caseInsensitive); - case STR_STARTS_WITH: - return startsWith(values, contextValue, caseInsensitive); - case STR_ENDS_WITH: - return endsWith(values, contextValue, caseInsensitive); - default: - return false; - } - } - - private boolean endsWith( - List values, Optional contextValue, boolean caseInsensitive) { - return contextValue - .map( - c -> - values.stream() - .anyMatch( - v -> { - if (caseInsensitive) { - return c.toLowerCase(comparisonLocale) - .endsWith( - v.toLowerCase( - comparisonLocale)); - } else { - return c.endsWith(v); - } - })) - .orElse(false); - } - - private boolean startsWith( - List values, Optional contextValue, boolean caseInsensitive) { - return contextValue - .map( - c -> - values.stream() - .anyMatch( - v -> { - if (caseInsensitive) { - return v.toLowerCase(comparisonLocale) - .startsWith( - c.toLowerCase( - comparisonLocale)); - } else { - return c.startsWith(v); - } - })) - .orElse(false); - } - - private boolean contains( - List values, Optional contextValue, boolean caseInsensitive) { - return contextValue - .map( - c -> - values.stream() - .anyMatch( - v -> { - if (caseInsensitive) { - return c.toLowerCase(comparisonLocale) - .contains( - v.toLowerCase( - comparisonLocale)); - } else { - return c.contains(v); - } - })) - .orElse(false); - } - - private boolean isIn(List values, Optional value, boolean caseInsensitive) { - return value.map( - v -> - values.stream() - .anyMatch( - c -> { - if (caseInsensitive) { - return c.equalsIgnoreCase(v); - } else { - return c.equals(v); - } - })) - .orElse(false); - } -} diff --git a/src/main/java/io/getunleash/util/ConstraintMerger.java b/src/main/java/io/getunleash/util/ConstraintMerger.java deleted file mode 100644 index 28db759e2..000000000 --- a/src/main/java/io/getunleash/util/ConstraintMerger.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.getunleash.util; - -import static io.getunleash.Segment.DENY_SEGMENT; - -import io.getunleash.ActivationStrategy; -import io.getunleash.Constraint; -import io.getunleash.Segment; -import io.getunleash.repository.IFeatureRepository; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ConstraintMerger { - public static List mergeConstraints( - IFeatureRepository repository, ActivationStrategy strategy) { - return Stream.of( - Optional.ofNullable(strategy.getConstraints()) - .orElseGet(Collections::emptyList), - Optional.ofNullable(strategy.getSegments()) - .orElseGet(Collections::emptyList).stream() - .map(repository::getSegment) - .map(s -> s == null ? DENY_SEGMENT : s) - .map(Segment::getConstraints) - .flatMap(Collection::stream) - .collect(Collectors.toList())) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/io/getunleash/util/IpAddressMatcher.java b/src/main/java/io/getunleash/util/IpAddressMatcher.java deleted file mode 100644 index 65f2c0c7e..000000000 --- a/src/main/java/io/getunleash/util/IpAddressMatcher.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.getunleash.util; - -import io.getunleash.lang.Nullable; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.regex.Pattern; - -/** - * Matches a request based on IP Address or subnet mask matching against the remote address. - * - *

Both IPv6 and IPv4 addresses are supported, but a matcher which is configured with an IPv4 - * address will never match a request which returns an IPv6 address, and vice-versa. - * - * @author Luke Taylor - */ -public final class IpAddressMatcher { - private static final Pattern SPLITTER = Pattern.compile("/"); - private final int nMaskBits; - @Nullable private final InetAddress requiredAddress; - - /** - * Takes a specific IP address or a range specified using the IP/Netmask (e.g. 192.168.1.0/24 or - * 202.24.0.0/14). - * - * @param ipAddress the address or range of addresses from which the request must come. - */ - public IpAddressMatcher(@Nullable String ipAddress) { - final String trimmedIpAddress = ipAddress == null ? "" : ipAddress.trim(); - - if (trimmedIpAddress.indexOf('/') > 0) { - String[] addressAndMask = SPLITTER.split(trimmedIpAddress, -1); - requiredAddress = parseAddress(addressAndMask[0]); - nMaskBits = Integer.parseInt(addressAndMask[1]); - } else { - requiredAddress = parseAddress(trimmedIpAddress); - nMaskBits = -1; - } - } - - public boolean matches(@Nullable String address) { - if (address == null || address.isEmpty() || requiredAddress == null) { - return false; - } - - InetAddress remoteAddress = parseAddress(address); - - if (remoteAddress == null || !requiredAddress.getClass().equals(remoteAddress.getClass())) { - return false; - } - - if (nMaskBits < 0) { - return remoteAddress.equals(requiredAddress); - } - - byte[] remAddr = remoteAddress.getAddress(); - byte[] reqAddr = requiredAddress.getAddress(); - - int oddBits = nMaskBits % 8; - int nMaskBytes = nMaskBits / 8 + (oddBits == 0 ? 0 : 1); - byte[] mask = new byte[nMaskBytes]; - - Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte) 0xFF); - - if (oddBits != 0) { - int finalByte = (1 << oddBits) - 1; - finalByte <<= 8 - oddBits; - mask[mask.length - 1] = (byte) finalByte; - } - - for (int i = 0; i < mask.length; i++) { - if ((remAddr[i] & mask[i]) != (reqAddr[i] & mask[i])) { - return false; - } - } - - return true; - } - - private @Nullable InetAddress parseAddress(@Nullable String address) { - if (address == null || address.isEmpty()) { - return null; - } - - try { - return InetAddress.getByName(address); - } catch (UnknownHostException e) { - throw new IllegalArgumentException("Failed to parse address " + address, e); - } - } -} diff --git a/src/main/java/io/getunleash/util/UnleashConfig.java b/src/main/java/io/getunleash/util/UnleashConfig.java index 53428b1cc..3dff27262 100644 --- a/src/main/java/io/getunleash/util/UnleashConfig.java +++ b/src/main/java/io/getunleash/util/UnleashConfig.java @@ -1,7 +1,5 @@ package io.getunleash.util; -import static io.getunleash.DefaultUnleash.UNKNOWN_STRATEGY; - import io.getunleash.CustomHttpHeadersProvider; import io.getunleash.DefaultCustomHttpHeadersProviderImpl; import io.getunleash.UnleashContextProvider; @@ -68,7 +66,7 @@ public class UnleashConfig { private final boolean synchronousFetchOnInitialisation; private final UnleashScheduledExecutor unleashScheduledExecutor; private final UnleashSubscriber unleashSubscriber; - @Nullable private final Strategy fallbackStrategy; + @Nullable private Strategy fallbackStrategy; @Nullable private final ToggleBootstrapProvider toggleBootstrapProvider; @Nullable private final Proxy proxy; @@ -123,9 +121,7 @@ private UnleashConfig( throw new IllegalStateException("You are required to specify a subscriber"); } - if (fallbackStrategy == null) { - this.fallbackStrategy = UNKNOWN_STRATEGY; - } else { + if (fallbackStrategy != null) { this.fallbackStrategy = fallbackStrategy; } diff --git a/src/main/java/io/getunleash/variant/VariantUtil.java b/src/main/java/io/getunleash/variant/VariantUtil.java deleted file mode 100644 index 0a0fd5655..000000000 --- a/src/main/java/io/getunleash/variant/VariantUtil.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.getunleash.variant; - -import io.getunleash.FeatureToggle; -import io.getunleash.UnleashContext; -import io.getunleash.Variant; -import io.getunleash.lang.Nullable; -import io.getunleash.strategy.StrategyUtils; -import java.util.*; -import java.util.List; -import java.util.function.Predicate; - -public final class VariantUtil { - static final String GROUP_ID_KEY = "groupId"; - // To avoid using the same seed for gradual rollout and variant selection. - // This caused an unfortunate bias since we'd already excluded x % of the hash results. - // This is the 5.000.001st prime. - public static final Long VARIANT_NORMALIZATION_SEED = 86028157L; - - private VariantUtil() {} - - private static Predicate overrideMatchesContext(UnleashContext context) { - return (override) -> { - Optional contextValue; - switch (override.getContextName()) { - case "userId": - { - contextValue = context.getUserId(); - break; - } - case "sessionId": - { - contextValue = context.getSessionId(); - break; - } - case "remoteAddress": - { - contextValue = context.getRemoteAddress(); - break; - } - default: - contextValue = - Optional.ofNullable( - context.getProperties().get(override.getContextName())); - break; - } - return override.getValues().contains(contextValue.orElse("")); - }; - } - - private static Optional getOverride( - List variants, UnleashContext context) { - return variants.stream() - .filter( - variant -> - variant.getOverrides().stream() - .anyMatch(overrideMatchesContext(context))) - .findFirst(); - } - - private static String getIdentifier(UnleashContext context) { - return context.getUserId() - .orElse( - context.getSessionId() - .orElse( - context.getRemoteAddress() - .orElse(Double.toString(Math.random())))); - } - - private static String randomString() { - int randSeed = new Random().nextInt(100000); - return "" + randSeed; - } - - private static String getSeed(UnleashContext unleashContext, Optional stickiness) { - return stickiness - .map(s -> unleashContext.getByName(s).orElse(randomString())) - .orElse(getIdentifier(unleashContext)); - } - - public static Variant selectVariant( - @Nullable FeatureToggle featureToggle, UnleashContext context, Variant defaultVariant) { - if (featureToggle == null) { - return defaultVariant; - } - - Variant variant = - selectVariant( - Collections.singletonMap("groupId", featureToggle.getName()), - featureToggle.getVariants(), - context); - - return variant != null ? variant : defaultVariant; - } - - public static @Nullable Variant selectVariant( - Map parameters, - @Nullable List variants, - UnleashContext context) { - if (variants != null) { - int totalWeight = variants.stream().mapToInt(VariantDefinition::getWeight).sum(); - if (totalWeight <= 0) { - return null; - } - Optional variantOverride = getOverride(variants, context); - if (variantOverride.isPresent()) { - return variantOverride.get().toVariant(); - } - - Optional customStickiness = - variants.stream() - .filter( - f -> - f.getStickiness() != null - && !"default".equals(f.getStickiness())) - .map(VariantDefinition::getStickiness) - .findFirst(); - int target = - StrategyUtils.getNormalizedNumber( - getSeed(context, customStickiness), - parameters.get(GROUP_ID_KEY), - totalWeight, - VARIANT_NORMALIZATION_SEED); - - int counter = 0; - for (VariantDefinition variant : variants) { - if (variant.getWeight() != 0) { - counter += variant.getWeight(); - if (counter >= target) { - return variant.toVariant(); - } - } - } - } - return null; - } -} diff --git a/src/test/java/io/getunleash/DefaultUnleashTest.java b/src/test/java/io/getunleash/DefaultUnleashTest.java index bf130dcb5..3de5faa50 100644 --- a/src/test/java/io/getunleash/DefaultUnleashTest.java +++ b/src/test/java/io/getunleash/DefaultUnleashTest.java @@ -131,7 +131,6 @@ public void should_evaluate_segment_collection_with_one_missing_segment_as_false @Test public void should_allow_fallback_strategy() { Strategy fallback = mock(Strategy.class); - when(fallback.getResult(anyMap(), any(), anyList(), anyList())).thenCallRealMethod(); UnleashConfig unleashConfigWithFallback = UnleashConfig.builder() diff --git a/src/test/java/io/getunleash/UnleashTest.java b/src/test/java/io/getunleash/UnleashTest.java index 23cce206e..8c12fcb1b 100644 --- a/src/test/java/io/getunleash/UnleashTest.java +++ b/src/test/java/io/getunleash/UnleashTest.java @@ -11,7 +11,6 @@ import io.getunleash.repository.*; import io.getunleash.repository.UnleashEngineStateHandler; import io.getunleash.strategy.Strategy; -import io.getunleash.strategy.UserWithIdStrategy; import io.getunleash.util.UnleashConfig; import io.getunleash.util.UnleashScheduledExecutor; import io.getunleash.variant.Payload; @@ -46,7 +45,7 @@ public void setup() { .unleashContextProvider(contextProvider) .build(); - unleash = new DefaultUnleash(config, toggleRepository, new UserWithIdStrategy()); + unleash = new DefaultUnleash(config, toggleRepository); stateHandler = new UnleashEngineStateHandler((DefaultUnleash) unleash); } @@ -130,7 +129,6 @@ public void should_register_custom_strategies() { // custom strategy Strategy customStrategy = mock(Strategy.class); when(customStrategy.getName()).thenReturn("custom"); - when(customStrategy.getResult(anyMap(), any(), anyList(), anyList())).thenCallRealMethod(); // register custom strategy UnleashConfig config = diff --git a/src/test/java/io/getunleash/integration/ClientSpecificationTest.java b/src/test/java/io/getunleash/integration/ClientSpecificationTest.java index cfc550a77..efb8d0ee7 100644 --- a/src/test/java/io/getunleash/integration/ClientSpecificationTest.java +++ b/src/test/java/io/getunleash/integration/ClientSpecificationTest.java @@ -18,7 +18,7 @@ import io.getunleash.UnleashContext; import io.getunleash.Variant; import io.getunleash.repository.UnleashEngineStateHandler; -import io.getunleash.strategy.constraints.DateParser; +import io.getunleash.util.DateParser; import io.getunleash.util.UnleashConfig; import java.io.BufferedReader; import java.io.File; diff --git a/src/test/java/io/getunleash/repository/UnleashEngineStateHandler.java b/src/test/java/io/getunleash/repository/UnleashEngineStateHandler.java index 013b02b10..50febb057 100644 --- a/src/test/java/io/getunleash/repository/UnleashEngineStateHandler.java +++ b/src/test/java/io/getunleash/repository/UnleashEngineStateHandler.java @@ -47,7 +47,6 @@ public void setState(FeatureCollection madeUp) { public void setState(String raw) { try { - this.unleashEngine.takeState(raw); } catch (YggdrasilInvalidInputException e) { throw new RuntimeException(e); diff --git a/src/test/java/io/getunleash/strategy/ApplicationHostnameStrategyTest.java b/src/test/java/io/getunleash/strategy/ApplicationHostnameStrategyTest.java index 66cf7b1d2..4e3f463ca 100644 --- a/src/test/java/io/getunleash/strategy/ApplicationHostnameStrategyTest.java +++ b/src/test/java/io/getunleash/strategy/ApplicationHostnameStrategyTest.java @@ -2,16 +2,42 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; - +import static org.mockito.Mockito.mock; + +import com.google.common.collect.ImmutableList; +import io.getunleash.ActivationStrategy; +import io.getunleash.DefaultUnleash; +import io.getunleash.FeatureToggle; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ApplicationHostnameStrategyTest { + private DefaultUnleash engine; + private UnleashEngineStateHandler stateHandler; + + @BeforeEach + void init() { + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .build(); + + engine = new DefaultUnleash(config); + stateHandler = new UnleashEngineStateHandler(engine); + } + @AfterEach public void remove_hostname_property() { System.getProperties().remove("hostname"); @@ -19,46 +45,28 @@ public void remove_hostname_property() { @Test public void should_be_disabled_if_no_HostNames_in_params() { - Strategy strategy = new ApplicationHostnameStrategy(); Map params = new HashMap<>(); params.put("hostNames", null); - assertFalse(strategy.isEnabled(params)); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("applicationHostname", params)))); + assertFalse(engine.isEnabled("test")); } @Test public void should_be_disabled_if_hostname_not_in_list() { - Strategy strategy = new ApplicationHostnameStrategy(); - Map params = new HashMap<>(); params.put("hostNames", "MegaHost,MiniHost, happyHost"); - assertFalse(strategy.isEnabled(params)); - } - - @Test - public void should_be_enabled_for_hostName() { - String hostName = "my-super-host"; - System.setProperty("hostname", hostName); - - Strategy strategy = new ApplicationHostnameStrategy(); - - Map params = new HashMap<>(); - params.put("hostNames", "MegaHost," + hostName + ",MiniHost, happyHost"); - assertTrue(strategy.isEnabled(params)); - } - - @Test - public void should_handle_weird_casing() { - String hostName = "my-super-host"; - System.setProperty("hostname", hostName); - - Strategy strategy = new ApplicationHostnameStrategy(); - - Map params = new HashMap<>(); - - params.put("hostNames", "MegaHost," + hostName.toUpperCase() + ",MiniHost, happyHost"); - assertTrue(strategy.isEnabled(params)); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("applicationHostname", params)))); + assertFalse(engine.isEnabled("test")); } @Test @@ -66,12 +74,15 @@ public void so_close_but_no_cigar() { String hostName = "my-super-host"; System.setProperty("hostname", hostName); - Strategy strategy = new ApplicationHostnameStrategy(); - Map params = new HashMap<>(); params.put("hostNames", "MegaHost, MiniHost, SuperhostOne"); - assertFalse(strategy.isEnabled(params)); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("applicationHostname", params)))); + assertFalse(engine.isEnabled("test")); } @Test @@ -79,28 +90,24 @@ public void should_be_enabled_for_InetAddress() throws UnknownHostException { String hostName = InetAddress.getLocalHost().getHostName(); System.setProperty("hostname", hostName); - Strategy strategy = new ApplicationHostnameStrategy(); - - Map params = new HashMap<>(); - params.put("hostNames", "MegaHost," + hostName + ",MiniHost, happyHost"); - assertTrue(strategy.isEnabled(params)); - } - - @Test - public void should_be_enabled_for_dashed_host() throws UnknownHostException { - String hostName = "super-wiEred-host"; - System.setProperty("hostname", hostName); - - Strategy strategy = new ApplicationHostnameStrategy(); - Map params = new HashMap<>(); params.put("hostNames", "MegaHost," + hostName + ",MiniHost, happyHost"); - assertTrue(strategy.isEnabled(params)); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("applicationHostname", params)))); + assertTrue(engine.isEnabled("test")); } @Test public void null_test() { - Strategy strategy = new ApplicationHostnameStrategy(); - assertFalse(strategy.isEnabled(new HashMap<>())); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("applicationHostname", new HashMap<>())))); + assertFalse(engine.isEnabled("test")); } } diff --git a/src/main/java/io/getunleash/strategy/DefaultStrategy.java b/src/test/java/io/getunleash/strategy/DefaultStrategy.java similarity index 100% rename from src/main/java/io/getunleash/strategy/DefaultStrategy.java rename to src/test/java/io/getunleash/strategy/DefaultStrategy.java diff --git a/src/test/java/io/getunleash/strategy/FlexibleRolloutStrategyTest.java b/src/test/java/io/getunleash/strategy/FlexibleRolloutStrategyTest.java index 7d90e98fd..8fd698b16 100644 --- a/src/test/java/io/getunleash/strategy/FlexibleRolloutStrategyTest.java +++ b/src/test/java/io/getunleash/strategy/FlexibleRolloutStrategyTest.java @@ -1,187 +1,247 @@ package io.getunleash.strategy; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; - -import io.getunleash.UnleashContext; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import com.google.common.collect.ImmutableList; +import io.getunleash.*; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class FlexibleRolloutStrategyTest { - @Test - public void should_have_correct_name() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); - assertEquals("flexibleRollout", strategy.getName()); + private DefaultUnleash engine; + private UnleashEngineStateHandler stateHandler; + + @BeforeEach + void init() { + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .build(); + + engine = new DefaultUnleash(config); + stateHandler = new UnleashEngineStateHandler(engine); } @Test public void should_always_be_false() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); - assertFalse(strategy.isEnabled(new HashMap<>())); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("flexibleRollout", new HashMap<>())))); + assertFalse(engine.isEnabled("test")); } @Test public void should_NOT_be_enabled_for_rollout_9_and_userId_61() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); Map params = new HashMap<>(); params.put("rollout", "9"); params.put("stickiness", "default"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().userId("61").build(); - boolean enabled = strategy.isEnabled(params, context); - assertFalse(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertFalse(engine.isEnabled("test", context)); } @Test public void should_be_enabled_for_rollout_10_and_userId_61() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); Map params = new HashMap<>(); params.put("rollout", "10"); params.put("stickiness", "default"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().userId("61").build(); - boolean enabled = strategy.isEnabled(params, context); - assertTrue(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertTrue(engine.isEnabled("test", context)); } @Test public void should_be_enabled_for_rollout_10_and_userId_61_and_stickiness_userId() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); Map params = new HashMap<>(); params.put("rollout", "10"); params.put("stickiness", "userId"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().userId("61").build(); - boolean enabled = strategy.isEnabled(params, context); - assertTrue(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertTrue(engine.isEnabled("test", context)); } @Test public void should_be_disabled_when_userId_missing() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); Map params = new HashMap<>(); params.put("rollout", "100"); params.put("stickiness", "userId"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().build(); - boolean enabled = strategy.isEnabled(params, context); - assertFalse(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertFalse(engine.isEnabled("test", context)); } @Test public void should_be_enabled_for_rollout_10_and_sessionId_61() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); Map params = new HashMap<>(); params.put("rollout", "10"); params.put("stickiness", "default"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().sessionId("61").build(); - boolean enabled = strategy.isEnabled(params, context); - assertTrue(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertTrue(engine.isEnabled("test", context)); } @Test public void should_be_enabled_for_rollout_10_and_randomId_61_and_stickiness_sessionId() { - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(); Map params = new HashMap<>(); params.put("rollout", "10"); params.put("stickiness", "sessionId"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().sessionId("61").build(); - boolean enabled = strategy.isEnabled(params, context); - assertTrue(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertTrue(engine.isEnabled("test", context)); } @Test + @Disabled // TODO we can't inject a random generator into the strategy it can be done providing + // a custom strategy though in which case we should keep the strategy classes around + // but it can't override the existing flexibleRollout public void should_be_enabled_for_rollout_10_and_randomId_61_and_stickiness_default() { - Supplier radnomGenerator = () -> "61"; - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(radnomGenerator); Map params = new HashMap<>(); params.put("rollout", "10"); params.put("stickiness", "default"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().build(); - boolean enabled = strategy.isEnabled(params, context); - assertTrue(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertTrue(engine.isEnabled("test", context)); } @Test + @Disabled // TODO we can't inject a random generator into the strategy it can be done providing + // a custom strategy though in which case we should keep the strategy classes around + // but it can't override the existing flexibleRollout public void should_be_enabled_for_rollout_10_and_randomId_61_and_stickiness_random() { - Supplier radnomGenerator = () -> "61"; - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(radnomGenerator); Map params = new HashMap<>(); params.put("rollout", "10"); params.put("stickiness", "random"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().build(); - boolean enabled = strategy.isEnabled(params, context); - assertTrue(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertTrue(engine.isEnabled("test", context)); } @Test public void should_NOT_be_enabled_for_rollout_10_and_randomId_1() { - Supplier radnomGenerator = () -> "1"; - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(radnomGenerator); Map params = new HashMap<>(); params.put("rollout", "10"); params.put("stickiness", "default"); params.put("groupId", "Demo"); UnleashContext context = UnleashContext.builder().build(); - boolean enabled = strategy.isEnabled(params, context); - assertFalse(enabled); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertFalse(engine.isEnabled("test", context)); } @Test public void should_not_be_enabled_for_custom_field_402() { - Supplier randomGenerator = () -> "2"; - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(randomGenerator); Map params = new HashMap<>(); params.put("rollout", "50"); params.put("stickiness", "customField"); params.put("groupId", "Feature.flexible.rollout.custom.stickiness_50"); UnleashContext context = UnleashContext.builder().addProperty("customField", "402").build(); - boolean enabled = strategy.isEnabled(params, context); - assertThat(enabled).isFalse(); + + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertFalse(engine.isEnabled("test", context)); } @Test public void should_not_be_enabled_for_custom_field_388_and_39() { - Supplier randomGenerator = () -> "2"; - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(randomGenerator); Map params = new HashMap<>(); params.put("rollout", "50"); params.put("stickiness", "customField"); params.put("groupId", "Feature.flexible.rollout.custom.stickiness_50"); + + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + UnleashContext context = UnleashContext.builder().addProperty("customField", "388").build(); - boolean enabled = strategy.isEnabled(params, context); - assertThat(enabled).isTrue(); + assertTrue(engine.isEnabled("test", context)); context = UnleashContext.builder().addProperty("customField", "39").build(); - enabled = strategy.isEnabled(params, context); - assertThat(enabled).isTrue(); + assertTrue(engine.isEnabled("test", context)); } @Test public void should_not_be_enabled_with_custom_stickiness_if_custom_field_is_missing() { - Supplier randomGenerator = () -> "2"; - FlexibleRolloutStrategy strategy = new FlexibleRolloutStrategy(randomGenerator); Map params = new HashMap<>(); params.put("rollout", "50"); params.put("stickiness", "customField"); params.put("groupId", "Feature.flexible.rollout.custom.stickiness_50"); UnleashContext context = UnleashContext.builder().build(); - boolean enabled = strategy.isEnabled(params, context); - assertThat(enabled).isFalse(); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("flexibleRollout", params)))); + assertFalse(engine.isEnabled("test", context)); } } diff --git a/src/test/java/io/getunleash/strategy/GradualRolloutRandomStrategyTest.java b/src/test/java/io/getunleash/strategy/GradualRolloutRandomStrategyTest.java index 13b044fb3..6b655e9ee 100644 --- a/src/test/java/io/getunleash/strategy/GradualRolloutRandomStrategyTest.java +++ b/src/test/java/io/getunleash/strategy/GradualRolloutRandomStrategyTest.java @@ -2,29 +2,48 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; - +import static org.mockito.Mockito.mock; + +import com.google.common.collect.ImmutableList; +import io.getunleash.*; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; +import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Random; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public final class GradualRolloutRandomStrategyTest { - - private static GradualRolloutRandomStrategy gradualRolloutRandomStrategy; - - @BeforeAll - public static void setUp() { - long seed = new Random().nextLong(); - System.out.println("GradualRolloutRandomStrategyTest running with seed: " + seed); - gradualRolloutRandomStrategy = new GradualRolloutRandomStrategy(seed); + private DefaultUnleash engine; + private UnleashEngineStateHandler stateHandler; + + @BeforeEach + void setUp() { + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .build(); + + engine = new DefaultUnleash(config); + stateHandler = new UnleashEngineStateHandler(engine); } @Test public void should_not_be_enabled_when_percentage_not_set() { final Map parameters = new HashMap<>(); - final boolean enabled = gradualRolloutRandomStrategy.isEnabled(parameters); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutRandom", parameters)))); + final boolean enabled = engine.isEnabled("test"); assertFalse(enabled); } @@ -38,7 +57,14 @@ public void should_not_be_enabled_when_percentage_is_not_a_not_a_number() { } }; - final boolean enabled = gradualRolloutRandomStrategy.isEnabled(parameters); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutRandom", parameters)), + Collections.emptyList())); + final boolean enabled = engine.isEnabled("test"); assertFalse(enabled); } @@ -52,7 +78,14 @@ public void should_not_be_enabled_when_percentage_is_not_a_not_a_valid_percentag } }; - final boolean enabled = gradualRolloutRandomStrategy.isEnabled(parameters); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutRandom", parameters)), + Collections.emptyList())); + final boolean enabled = engine.isEnabled("test"); assertFalse(enabled); } @@ -66,8 +99,16 @@ public void should_never_be_enabled_when_0_percent() { } }; + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutRandom", parameters)), + Collections.emptyList())); + for (int i = 0; i < 1000; i++) { - final boolean enabled = gradualRolloutRandomStrategy.isEnabled(parameters); + final boolean enabled = engine.isEnabled("test"); assertFalse(enabled); } } @@ -81,8 +122,15 @@ public void should_always_be_enabled_when_100_percent() { } }; + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutRandom", parameters)), + Collections.emptyList())); for (int i = 0; i <= 100; i++) { - final boolean enabled = gradualRolloutRandomStrategy.isEnabled(parameters); + final boolean enabled = engine.isEnabled("test"); assertTrue(enabled, "Should be enabled for p=" + i); } } @@ -103,8 +151,15 @@ public void should_diverage_at_most_with_one_percent_point() { int rounds = 20000; int countEnabled = 0; + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutRandom", parameters)), + Collections.emptyList())); for (int i = 0; i < rounds; i++) { - final boolean enabled = gradualRolloutRandomStrategy.isEnabled(parameters); + final boolean enabled = engine.isEnabled("test"); if (enabled) { countEnabled = countEnabled + 1; } diff --git a/src/test/java/io/getunleash/strategy/GradualRolloutSessionIdStrategyTest.java b/src/test/java/io/getunleash/strategy/GradualRolloutSessionIdStrategyTest.java index 324d815e1..461f1cc86 100644 --- a/src/test/java/io/getunleash/strategy/GradualRolloutSessionIdStrategyTest.java +++ b/src/test/java/io/getunleash/strategy/GradualRolloutSessionIdStrategyTest.java @@ -3,9 +3,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; import com.google.common.collect.ImmutableList; -import io.getunleash.UnleashContext; +import io.getunleash.*; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,8 +26,11 @@ public class GradualRolloutSessionIdStrategyTest { Random rand = new Random(SEED); List percentages; + private DefaultUnleash engine; + private UnleashEngineStateHandler stateHandler; + @BeforeEach - public void init() { + void setUp() { percentages = ImmutableList.builder() .add(1) @@ -36,42 +43,59 @@ public void init() { .add(99) .add(100) .build(); - } - @Test - public void should_have_a_name() { - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); - assertThat(gradualRolloutStrategy.getName()).isEqualTo("gradualRolloutSessionId"); + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .build(); + + engine = new DefaultUnleash(config); + stateHandler = new UnleashEngineStateHandler(engine); } @Test public void should_require_context() { - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); - assertThat(gradualRolloutStrategy.isEnabled(new HashMap<>())).isFalse(); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "gradualRolloutSessionId", new HashMap<>())))); + assertThat(engine.isEnabled("test")).isFalse(); } @Test public void should_be_disabled_when_missing_user_id() { UnleashContext context = UnleashContext.builder().build(); - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); - - assertThat(gradualRolloutStrategy.isEnabled(new HashMap<>(), context)).isFalse(); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "gradualRolloutSessionId", new HashMap<>())))); + assertThat(engine.isEnabled("test", context)).isFalse(); } @Test public void should_have_same_result_for_multiple_executions() { UnleashContext context = UnleashContext.builder().sessionId("1574576830").build(); - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); Map params = buildParams(1, "innfinn"); - boolean firstRunResult = gradualRolloutStrategy.isEnabled(params, context); - + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutSessionId", params)))); + + boolean firstRunResult = engine.isEnabled("test", context); for (int i = 0; i < 10; i++) { - boolean subsequentRunResult = gradualRolloutStrategy.isEnabled(params, context); + boolean subsequentRunResult = engine.isEnabled("test", context); assertThat(firstRunResult) .isEqualTo(subsequentRunResult) .withFailMessage("loginId should return same result when unchanged parameters"); @@ -81,11 +105,15 @@ public void should_have_same_result_for_multiple_executions() { @Test public void should_be_enabled_when_using_100percent_rollout() { UnleashContext context = UnleashContext.builder().sessionId("1574576830").build(); - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); Map params = buildParams(100, "innfinn"); - boolean result = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutSessionId", params)))); + boolean result = engine.isEnabled("test", context); assertThat(result).isTrue(); } @@ -93,11 +121,15 @@ public void should_be_enabled_when_using_100percent_rollout() { @Test public void should_not_be_enabled_when_0percent_rollout() { UnleashContext context = UnleashContext.builder().sessionId("1574576830").build(); - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); Map params = buildParams(0, "innfinn"); - boolean actual = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutSessionId", params)))); + boolean actual = engine.isEnabled("test", context); assertFalse(actual, "should not be enabled when 0% rollout"); } @@ -110,12 +142,16 @@ public void should_be_enabled_above_minimum_percentage() { UnleashContext context = UnleashContext.builder().sessionId(sessionId).build(); - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); - for (int p = minimumPercentage; p <= 100; p++) { Map params = buildParams(p, groupId); - boolean actual = gradualRolloutStrategy.isEnabled(params, context); + // Ok, we're going to stress the setting the state + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutSessionId", params)))); + boolean actual = engine.isEnabled("test", context); assertTrue(actual, "should be enabled when " + p + "% rollout"); } } @@ -148,11 +184,14 @@ protected int checkRandomLoginIDs(int numberOfIDs, int percentage) { UnleashContext context = UnleashContext.builder().sessionId(sessionId.toString()).build(); - GradualRolloutSessionIdStrategy gradualRolloutStrategy = - new GradualRolloutSessionIdStrategy(); - Map params = buildParams(percentage, ""); - boolean enabled = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutSessionId", params)))); + boolean enabled = engine.isEnabled("test", context); if (enabled) { numberOfEnabledUsers++; } @@ -162,8 +201,8 @@ protected int checkRandomLoginIDs(int numberOfIDs, int percentage) { private Map buildParams(int percentage, String groupId) { Map params = new HashMap(); - params.put(GradualRolloutSessionIdStrategy.PERCENTAGE, String.valueOf(percentage)); - params.put(GradualRolloutSessionIdStrategy.GROUP_ID, groupId); + params.put("percentage", String.valueOf(percentage)); + params.put("groupId", groupId); return params; } diff --git a/src/test/java/io/getunleash/strategy/GradualRolloutUserIdStrategyTest.java b/src/test/java/io/getunleash/strategy/GradualRolloutUserIdStrategyTest.java index 50f238f1c..ef4734653 100644 --- a/src/test/java/io/getunleash/strategy/GradualRolloutUserIdStrategyTest.java +++ b/src/test/java/io/getunleash/strategy/GradualRolloutUserIdStrategyTest.java @@ -3,9 +3,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; import com.google.common.collect.ImmutableList; -import io.getunleash.UnleashContext; +import io.getunleash.*; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,6 +27,9 @@ public class GradualRolloutUserIdStrategyTest { Random rand = new Random(SEED); List percentages; + private DefaultUnleash engine; + private UnleashEngineStateHandler stateHandler; + @BeforeEach public void init() { percentages = @@ -37,38 +44,57 @@ public void init() { .add(99) .add(100) .build(); - } - @Test - public void should_have_a_name() { - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); - assertThat(gradualRolloutStrategy.getName()).isEqualTo("gradualRolloutUserId"); + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .build(); + + engine = new DefaultUnleash(config); + stateHandler = new UnleashEngineStateHandler(engine); } @Test public void should_require_context() { - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); - assertThat(gradualRolloutStrategy.isEnabled(new HashMap<>())).isFalse(); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutUserId", new HashMap<>())))); + assertThat(engine.isEnabled("test")).isFalse(); } @Test public void should_be_disabled_when_missing_user_id() { UnleashContext context = UnleashContext.builder().build(); - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); - assertThat(gradualRolloutStrategy.isEnabled(new HashMap<>(), context)).isFalse(); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutUserId", new HashMap<>())))); + assertThat(engine.isEnabled("test", context)).isFalse(); } @Test public void should_have_same_result_for_multiple_executions() { UnleashContext context = UnleashContext.builder().userId("1574576830").build(); - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); Map params = buildParams(1, "innfinn"); - boolean firstRunResult = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("gradualRolloutUserId", params)))); + boolean firstRunResult = engine.isEnabled("test", context); for (int i = 0; i < 10; i++) { - boolean subsequentRunResult = gradualRolloutStrategy.isEnabled(params, context); + boolean subsequentRunResult = engine.isEnabled("test", context); assertThat(firstRunResult).isEqualTo(subsequentRunResult); } } @@ -76,10 +102,14 @@ public void should_have_same_result_for_multiple_executions() { @Test public void should_be_enabled_when_using_100percent_rollout() { UnleashContext context = UnleashContext.builder().userId("1574576830").build(); - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); Map params = buildParams(100, "innfinn"); - boolean result = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("gradualRolloutUserId", params)))); + boolean result = engine.isEnabled("test", context); assertThat(result).isTrue(); } @@ -87,10 +117,14 @@ public void should_be_enabled_when_using_100percent_rollout() { @Test public void should_not_be_enabled_when_0percent_rollout() { UnleashContext context = UnleashContext.builder().userId("1574576830").build(); - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); Map params = buildParams(0, "innfinn"); - boolean actual = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("gradualRolloutUserId", params)))); + boolean actual = engine.isEnabled("test", context); assertFalse(actual, "should not be enabled when 0% rollout"); } @@ -101,12 +135,15 @@ public void should_be_enabled_above_minimum_percentage() { int minimumPercentage = StrategyUtils.getNormalizedNumber(userId, groupId, 0); UnleashContext context = UnleashContext.builder().userId(userId).build(); - - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); - for (int p = minimumPercentage; p <= 100; p++) { Map params = buildParams(p, groupId); - boolean actual = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutUserId", params)))); + boolean actual = engine.isEnabled("test", context); assertTrue(actual, "should be enabled when " + p + "% rollout"); } } @@ -120,12 +157,16 @@ public void should_at_most_miss_with_one_percent_when_rolling_out_to_specified_p Map params = buildParams(percentage, groupId); - GradualRolloutUserIdStrategy gradualRolloutStrategy = new GradualRolloutUserIdStrategy(); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("gradualRolloutUserId", params)))); for (int userId = 0; userId < rounds; userId++) { UnleashContext context = UnleashContext.builder().userId("user" + userId).build(); - if (gradualRolloutStrategy.isEnabled(params, context)) { + if (engine.isEnabled("test", context)) { enabledCount++; } } @@ -162,11 +203,14 @@ protected int checkRandomLoginIDs(int numberOfIDs, int percentage) { Long userId = getRandomLoginId(); UnleashContext context = UnleashContext.builder().userId(userId.toString()).build(); - GradualRolloutUserIdStrategy gradualRolloutStrategy = - new GradualRolloutUserIdStrategy(); - Map params = buildParams(percentage, ""); - boolean enabled = gradualRolloutStrategy.isEnabled(params, context); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("gradualRolloutUserId", params)))); + boolean enabled = engine.isEnabled("test", context); if (enabled) { numberOfEnabledUsers++; } @@ -176,8 +220,8 @@ protected int checkRandomLoginIDs(int numberOfIDs, int percentage) { private Map buildParams(int percentage, String groupId) { Map params = new HashMap(); - params.put(GradualRolloutUserIdStrategy.PERCENTAGE, String.valueOf(percentage)); - params.put(GradualRolloutUserIdStrategy.GROUP_ID, groupId); + params.put("percentage", String.valueOf(percentage)); + params.put("groupId", groupId); return params; } diff --git a/src/test/java/io/getunleash/strategy/RemoteAddressStrategyTest.java b/src/test/java/io/getunleash/strategy/RemoteAddressStrategyTest.java index f391ca23a..b9d4993d2 100644 --- a/src/test/java/io/getunleash/strategy/RemoteAddressStrategyTest.java +++ b/src/test/java/io/getunleash/strategy/RemoteAddressStrategyTest.java @@ -1,17 +1,16 @@ package io.getunleash.strategy; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; -import io.getunleash.UnleashContext; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import io.getunleash.*; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; +import java.util.*; import java.util.stream.Stream; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -35,8 +34,6 @@ class RemoteAddressStrategyTest { private static final List ALL = ImmutableList.builder().addAll(ALL_IPV4).addAll(ALL_IPV6).build(); - private RemoteAddressStrategy strategy; - static Stream data() { return Stream.of( Arguments.of(FIRST_IPV4, FIRST_IPV4, true), @@ -72,23 +69,35 @@ static Stream data() { Arguments.of(FIRST_IPV6, String.join(".", ALL), false)); } - @BeforeEach - void setUp() { - strategy = new RemoteAddressStrategy(); - } - - @Test - void should_have_a_name() { - assertThat(strategy.getName()).isEqualTo("remoteAddress"); - } - @ParameterizedTest @MethodSource("data") void test_all_combinations(String actualIp, String parameterString, boolean expected) { - UnleashContext context = UnleashContext.builder().remoteAddress(actualIp).build(); + UnleashContextProvider contextProvider = mock(UnleashContextProvider.class); + when(contextProvider.getContext()) + .thenReturn(UnleashContext.builder().remoteAddress(actualIp).build()); + + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .unleashContextProvider(contextProvider) + .build(); + Map parameters = setupParameterMap(parameterString); - assertThat(strategy.isEnabled(parameters, context)).isEqualTo(expected); + DefaultUnleash engine = new DefaultUnleash(config); + new UnleashEngineStateHandler(engine) + .setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy("remoteAddress", parameters)), + Collections.emptyList())); + + assertThat(engine.isEnabled("test")).isEqualTo(expected); } private Map setupParameterMap(String ipString) { @@ -97,7 +106,7 @@ private Map setupParameterMap(String ipString) { } Map parameters = new HashMap<>(); - parameters.put(RemoteAddressStrategy.PARAM, ipString); + parameters.put("IPs", ipString); return parameters; } } diff --git a/src/test/java/io/getunleash/strategy/StrategyTest.java b/src/test/java/io/getunleash/strategy/StrategyTest.java index 971654b22..5fcba2b00 100644 --- a/src/test/java/io/getunleash/strategy/StrategyTest.java +++ b/src/test/java/io/getunleash/strategy/StrategyTest.java @@ -1,15 +1,20 @@ package io.getunleash.strategy; import static org.junit.jupiter.api.Assertions.*; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.collect.ImmutableList; +import io.getunleash.*; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class StrategyTest { @@ -26,67 +31,121 @@ public boolean isEnabled(Map parameters) { } } + private DefaultUnleash engine; + private UnleashEngineStateHandler stateHandler; + + @BeforeEach + void init() { + UnleashContextProvider contextProvider = mock(UnleashContextProvider.class); + when(contextProvider.getContext()).thenReturn(UnleashContext.builder().build()); + + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .unleashContextProvider(contextProvider) + .build(); + + engine = new DefaultUnleash(config, new AlwaysEnabled()); + stateHandler = new UnleashEngineStateHandler(engine); + } + @Test public void should_be_enabled_for_empty_constraints() { - Strategy s = new AlwaysEnabled(); Map parameters = new HashMap(); UnleashContext context = UnleashContext.builder().build(); List constraints = new ArrayList<>(); - - boolean result = s.isEnabled(parameters, context, constraints); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "enabled", parameters, constraints, null, null)))); + + boolean result = engine.isEnabled("test", context); assertTrue(result); } @Test public void should_be_enabled_for_null_constraints() { - Strategy s = new AlwaysEnabled(); Map parameters = new HashMap(); UnleashContext context = UnleashContext.builder().build(); List constraints = null; - boolean result = s.isEnabled(parameters, context, constraints); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "enabled", parameters, constraints, null, null)))); + + boolean result = engine.isEnabled("test", context); assertTrue(result); } @Test public void should_be_disabled_when_constraint_not_satisfied() { - Strategy s = new AlwaysEnabled(); Map parameters = new HashMap(); UnleashContext context = UnleashContext.builder().environment("test").build(); List constraints = new ArrayList<>(); constraints.add(new Constraint("environment", Operator.IN, Arrays.asList("prod"))); - boolean result = s.isEnabled(parameters, context, constraints); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "enabled", parameters, constraints, null, null)))); + + boolean result = engine.isEnabled("test", context); assertFalse(result); } @Test public void should_be_enabled_when_constraint_is_satisfied() { - Strategy s = new AlwaysEnabled(); Map parameters = new HashMap(); UnleashContext context = UnleashContext.builder().environment("test").build(); List constraints = new ArrayList<>(); constraints.add(new Constraint("environment", Operator.IN, Arrays.asList("test", "prod"))); - boolean result = s.isEnabled(parameters, context, constraints); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "enabled", parameters, constraints, null, null)))); + + boolean result = engine.isEnabled("test", context); assertTrue(result); } @Test public void should_be_enabled_when_constraint_NOT_IN_satisfied() { - Strategy s = new AlwaysEnabled(); Map parameters = new HashMap(); UnleashContext context = UnleashContext.builder().environment("test").build(); List constraints = new ArrayList<>(); constraints.add(new Constraint("environment", Operator.NOT_IN, Arrays.asList("prod"))); - boolean result = s.isEnabled(parameters, context, constraints); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "enabled", parameters, constraints, null, null)))); + + boolean result = engine.isEnabled("test", context); assertTrue(result); } @Test public void should_be_enabled_when_all_constraints_are_satisfied() { - Strategy s = new AlwaysEnabled(); Map parameters = new HashMap(); UnleashContext context = UnleashContext.builder() @@ -99,13 +158,20 @@ public void should_be_enabled_when_all_constraints_are_satisfied() { constraints.add(new Constraint("userId", Operator.IN, Arrays.asList("123"))); constraints.add(new Constraint("customerId", Operator.IN, Arrays.asList("red", "blue"))); - boolean result = s.isEnabled(parameters, context, constraints); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "enabled", parameters, constraints, null, null)))); + + boolean result = engine.isEnabled("test", context); assertTrue(result); } @Test public void should_be_disabled_when_not_all_constraints_are_satisfied() { - Strategy s = new AlwaysEnabled(); Map parameters = new HashMap(); UnleashContext context = UnleashContext.builder() @@ -118,7 +184,15 @@ public void should_be_disabled_when_not_all_constraints_are_satisfied() { constraints.add(new Constraint("userId", Operator.IN, Arrays.asList("123"))); constraints.add(new Constraint("customerId", Operator.IN, Arrays.asList("red", "blue"))); - boolean result = s.isEnabled(parameters, context, constraints); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of( + new ActivationStrategy( + "enabled", parameters, constraints, null, null)))); + + boolean result = engine.isEnabled("test", context); assertFalse(result); } } diff --git a/src/test/java/io/getunleash/strategy/StrategyUsingContext.java b/src/test/java/io/getunleash/strategy/StrategyUsingContext.java deleted file mode 100644 index 2c9a01383..000000000 --- a/src/test/java/io/getunleash/strategy/StrategyUsingContext.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.getunleash.strategy; - -import static java.util.Arrays.asList; - -import io.getunleash.UnleashContext; -import java.util.List; -import java.util.Map; - -public class StrategyUsingContext implements Strategy { - - @Override - public String getName() { - return "usingContext"; - } - - @Override - public boolean isEnabled(Map parameters) { - return false; - } - - @Override - public boolean isEnabled(Map parameters, UnleashContext unleashContext) { - String userIdString = parameters.get("userIds"); - List userIds = asList(userIdString.split(",\\s?")); - if (unleashContext.getUserId().isPresent()) { - String userId = unleashContext.getUserId().get(); - return userIds.contains(userId); - } else { - return false; - } - } -} diff --git a/src/test/java/io/getunleash/strategy/StrategyUtilsTest.java b/src/test/java/io/getunleash/strategy/StrategyUtilsTest.java index 3b5486f63..a9135dbc0 100644 --- a/src/test/java/io/getunleash/strategy/StrategyUtilsTest.java +++ b/src/test/java/io/getunleash/strategy/StrategyUtilsTest.java @@ -1,11 +1,7 @@ package io.getunleash.strategy; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import io.getunleash.variant.VariantUtil; -import java.util.UUID; -import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; public class StrategyUtilsTest { @@ -15,46 +11,4 @@ public void normalized_values_are_the_same_across_node_java_and_go_clients() { assertEquals(73, StrategyUtils.getNormalizedNumber("123", "gr1", 0)); assertEquals(25, StrategyUtils.getNormalizedNumber("999", "groupX", 0)); } - - @Test - public void normalized_values_with_variant_seed_are_the_same_across_node_java() { - assertThat( - StrategyUtils.getNormalizedNumber( - "123", "gr1", VariantUtil.VARIANT_NORMALIZATION_SEED)) - .isEqualTo(96); - assertThat( - StrategyUtils.getNormalizedNumber( - "999", "groupX", VariantUtil.VARIANT_NORMALIZATION_SEED)) - .isEqualTo(60); - } - - @Test - public void - selecting_ten_percent_of_users_and_then_finding_variants_should_still_have_variants_evenly_distributed() { - int ones = 0, twos = 0, threes = 0, loopSize = 500000, selectionSize = 0; - for (int i = 0; i < loopSize; i++) { - String id = UUID.randomUUID().toString(); - int featureRollout = - StrategyUtils.getNormalizedNumber(id, "feature.name.that.is.quite.long", 0); - if (featureRollout < 11) { - int variantGroup = - StrategyUtils.getNormalizedNumber( - id, - "feature.name.that.is.quite.long", - 1000, - VariantUtil.VARIANT_NORMALIZATION_SEED); - if (variantGroup <= 333) { - ones++; - } else if (variantGroup <= 666) { - twos++; - } else if (variantGroup <= 1000) { - threes++; - } - selectionSize++; - } - } - assertThat(ones / (double) (selectionSize)).isCloseTo(0.33, Offset.offset(0.01)); - assertThat(twos / (double) (selectionSize)).isCloseTo(0.33, Offset.offset(0.01)); - assertThat(threes / (double) (selectionSize)).isCloseTo(0.33, Offset.offset(0.01)); - } } diff --git a/src/test/java/io/getunleash/strategy/StrategyWithContextTest.java b/src/test/java/io/getunleash/strategy/StrategyWithContextTest.java deleted file mode 100644 index 5cf9f242b..000000000 --- a/src/test/java/io/getunleash/strategy/StrategyWithContextTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.getunleash.strategy; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.getunleash.UnleashContext; -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class StrategyWithContextTest { - private StrategyUsingContext strategy; - - @BeforeEach - public void setup() { - strategy = new StrategyUsingContext(); - } - - @Test - public void should_be_enabled_for_known_user() { - // Params - Map params = new HashMap<>(); - params.put("userIds", "123"); - - // Context - UnleashContext context = UnleashContext.builder().userId("123").build(); - - assertTrue(strategy.isEnabled(params, context)); - } - - @Test - public void should_not_enabled_for_unknown_user() { - // Params - Map params = new HashMap<>(); - params.put("userIds", "123"); - - // Context - UnleashContext context = UnleashContext.builder().userId("other").build(); - - assertFalse(strategy.isEnabled(params, context)); - } -} diff --git a/src/test/java/io/getunleash/strategy/UserWithIdStrategyTest.java b/src/test/java/io/getunleash/strategy/UserWithIdStrategyTest.java index edbe30efc..096d17f0c 100644 --- a/src/test/java/io/getunleash/strategy/UserWithIdStrategyTest.java +++ b/src/test/java/io/getunleash/strategy/UserWithIdStrategyTest.java @@ -1,26 +1,38 @@ package io.getunleash.strategy; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import com.google.common.collect.ImmutableList; +import io.getunleash.ActivationStrategy; +import io.getunleash.DefaultUnleash; +import io.getunleash.FeatureToggle; import io.getunleash.UnleashContext; +import io.getunleash.repository.UnleashEngineStateHandler; +import io.getunleash.util.UnleashConfig; +import io.getunleash.util.UnleashScheduledExecutor; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class UserWithIdStrategyTest { - private UserWithIdStrategy strategy; + private DefaultUnleash engine; + private UnleashEngineStateHandler stateHandler; @BeforeEach - public void setup() { - strategy = new UserWithIdStrategy(); - } - - @Test - public void should_have_expected_strategy_name() { - assertThat(strategy.getName()).isEqualTo("userWithId"); + void setup() { + UnleashConfig config = + new UnleashConfig.Builder() + .appName("test") + .unleashAPI("http://localhost:4242/api/") + .environment("test") + .scheduledExecutor(mock(UnleashScheduledExecutor.class)) + .build(); + + engine = new DefaultUnleash(config); + stateHandler = new UnleashEngineStateHandler(engine); } @Test @@ -28,9 +40,14 @@ public void should_match_one_userId() { Map parameters = new HashMap<>(); UnleashContext context = UnleashContext.builder().userId("123").build(); - parameters.put(strategy.PARAM, "123"); - - assertTrue(strategy.isEnabled(parameters, context)); + parameters.put("userIds", "123"); + + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertTrue(engine.isEnabled("test", context)); } @Test @@ -38,9 +55,13 @@ public void should_match_first_userId_in_list() { Map parameters = new HashMap<>(); UnleashContext context = UnleashContext.builder().userId("123").build(); - parameters.put(strategy.PARAM, "123, 122, 121"); - - assertTrue(strategy.isEnabled(parameters, context)); + parameters.put("userIds", "123, 122, 121"); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertTrue(engine.isEnabled("test", context)); } @Test @@ -48,9 +69,13 @@ public void should_match_middle_userId_in_list() { Map parameters = new HashMap<>(); UnleashContext context = UnleashContext.builder().userId("123").build(); - parameters.put(strategy.PARAM, "123, 122, 121"); - - assertTrue(strategy.isEnabled(parameters, context)); + parameters.put("userIds", "123, 122, 121"); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertTrue(engine.isEnabled("test", context)); } @Test @@ -58,9 +83,13 @@ public void should_match_last_userId_in_list() { Map parameters = new HashMap<>(); UnleashContext context = UnleashContext.builder().userId("123").build(); - parameters.put(strategy.PARAM, "123, 122, 121"); - - assertTrue(strategy.isEnabled(parameters, context)); + parameters.put("userIds", "123, 122, 121"); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertTrue(engine.isEnabled("test", context)); } @Test @@ -68,9 +97,13 @@ public void should_not_match_subparts_of_ids() { Map parameters = new HashMap<>(); UnleashContext context = UnleashContext.builder().userId("12").build(); - parameters.put(strategy.PARAM, "123, 122, 121, 212"); - - assertFalse(strategy.isEnabled(parameters, context)); + parameters.put("userIds", "123, 122, 121, 212"); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertFalse(engine.isEnabled("test", context)); } @Test @@ -78,9 +111,13 @@ public void should_not_match_csv_without_space() { Map parameters = new HashMap<>(); UnleashContext context = UnleashContext.builder().userId("123").build(); - parameters.put(strategy.PARAM, "123,122,121"); - - assertTrue(strategy.isEnabled(parameters, context)); + parameters.put("userIds", "123,122,121"); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertTrue(engine.isEnabled("test", context)); } @Test @@ -89,10 +126,14 @@ public void should_match_real_ids() { UnleashContext context = UnleashContext.builder().userId("298261117").build(); parameters.put( - strategy.PARAM, + "userIds", "160118738, 1823311338, 1422637466, 2125981185, 298261117, 1829486714, 463568019, 271166598"); - - assertTrue(strategy.isEnabled(parameters, context)); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertTrue(engine.isEnabled("test", context)); } @Test @@ -101,18 +142,26 @@ public void should_not_match_real_ids() { UnleashContext context = UnleashContext.builder().userId("32667774").build(); parameters.put( - strategy.PARAM, + "userIds", "160118738, 1823311338, 1422637466, 2125981185, 298261117, 1829486714, 463568019, 271166598"); - - assertFalse(strategy.isEnabled(parameters, context)); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertFalse(engine.isEnabled("test", context)); } @Test public void should_not_be_enabled_without_id() { Map parameters = new HashMap<>(); - parameters.put(strategy.PARAM, "160118738, 1823311338"); - - assertFalse(strategy.isEnabled(parameters)); + parameters.put("userIds", "160118738, 1823311338"); + stateHandler.setState( + new FeatureToggle( + "test", + true, + ImmutableList.of(new ActivationStrategy("userWithId", parameters)))); + assertFalse(engine.isEnabled("test")); } } diff --git a/src/test/java/io/getunleash/strategy/constraints/DateConstraintOperatorTest.java b/src/test/java/io/getunleash/strategy/constraints/DateConstraintOperatorTest.java deleted file mode 100644 index 5349a3eab..000000000 --- a/src/test/java/io/getunleash/strategy/constraints/DateConstraintOperatorTest.java +++ /dev/null @@ -1,171 +0,0 @@ -package io.getunleash.strategy.constraints; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import io.getunleash.strategy.DefaultStrategy; -import io.getunleash.strategy.Strategy; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class DateConstraintOperatorTest { - DateTimeFormatter ISO = DateTimeFormatter.ISO_INSTANT; - - @Test - public void shouldSupportAfterDate() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "releaseDate", - Operator.DATE_AFTER, - ZonedDateTime.now().minusHours(1).format(ISO))); - Map parameters = new HashMap<>(); - // If there's not a date in the defined context field, compare with Instant.now() - UnleashContext enabled = UnleashContext.builder().environment("dev").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - // If there's a date in the defined context field, compare with that - UnleashContext equal = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isTrue(); - - UnleashContext disabled = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().minusHours(2).format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - } - - @Test - public void afterDateSupportsInversion() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "releaseDate", - Operator.DATE_AFTER, - ZonedDateTime.now().minusHours(1).format(ISO), - true)); - Map parameters = new HashMap<>(); - // If there's not a date in the defined context field, compare with Instant.now() - UnleashContext enabled = UnleashContext.builder().environment("dev").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isFalse(); - // If there's a date in the defined context field, compare with that - UnleashContext equal = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isFalse(); - - UnleashContext disabled = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().minusHours(2).format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isTrue(); - } - - @Test - public void shouldSupportBeforeDate() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "releaseDate", - Operator.DATE_BEFORE, - ZonedDateTime.now().plusDays(2).format(ISO))); - Map parameters = new HashMap<>(); - // If there's not a date in the defined context field, compare with Instant.now() - UnleashContext now = UnleashContext.builder().environment("dev").build(); - assertThat(strategy.isEnabled(parameters, now, constraintList)).isTrue(); - // If there's a date in the defined context field, compare with that - UnleashContext equal = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().plusDays(1).format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isTrue(); - - UnleashContext fourDaysFromNow = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().plusDays(4).format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, fourDaysFromNow, constraintList)).isFalse(); - } - - @Test - public void beforeDateShouldSupportInversion() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "releaseDate", - Operator.DATE_BEFORE, - ZonedDateTime.now().plusDays(2).format(ISO), - Collections.emptyList(), - true, - false)); - Map parameters = new HashMap<>(); - // If there's not a date in the defined context field, compare with Instant.now() - UnleashContext now = UnleashContext.builder().environment("dev").build(); - assertThat(strategy.isEnabled(parameters, now, constraintList)).isFalse(); - // If there's a date in the defined context field, compare with that - UnleashContext equal = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().plusDays(1).format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isFalse(); - - UnleashContext fourDaysFromNow = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", ZonedDateTime.now().plusDays(4).format(ISO)) - .build(); - assertThat(strategy.isEnabled(parameters, fourDaysFromNow, constraintList)).isTrue(); - } - - @Test - public void dateParserSupportsNotPassingInZone() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint("releaseDate", Operator.DATE_BEFORE, "2022-11-05T11:05:00")); - Map parameters = new HashMap<>(); - UnleashContext equal = - UnleashContext.builder() - .environment("dev") - .addProperty("releaseDate", "2022-11-04T11:05:00") - .build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isTrue(); - } - - @Test - public void dateBefore() { - Strategy strategy = new DefaultStrategy(); - List c = - Collections.singletonList( - new Constraint( - "currentTime", Operator.DATE_BEFORE, "2022-01-29T13:00:00.000Z")); - Map parameters = new HashMap<>(); - UnleashContext context = - UnleashContext.builder() - .environment("dev") - .addProperty("currentTime", "2022-01-30T13:00:00.000Z") - .build(); - assertThat(strategy.isEnabled(parameters, context, c)).isFalse(); - } -} diff --git a/src/test/java/io/getunleash/strategy/constraints/NumberConstraintOperatorTest.java b/src/test/java/io/getunleash/strategy/constraints/NumberConstraintOperatorTest.java deleted file mode 100644 index 19e948cf3..000000000 --- a/src/test/java/io/getunleash/strategy/constraints/NumberConstraintOperatorTest.java +++ /dev/null @@ -1,198 +0,0 @@ -package io.getunleash.strategy.constraints; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import io.getunleash.strategy.DefaultStrategy; -import io.getunleash.strategy.Strategy; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class NumberConstraintOperatorTest { - - @Test - public void shouldSupportEqForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", Operator.NUM_EQ, Collections.singletonList("42"))); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder().environment("dev").addProperty("userAge", "42").build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isTrue(); - } - - @Test - public void shouldSupportInvertingNumberEq() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", Operator.NUM_EQ, Collections.singletonList("42"), true)); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder().environment("dev").addProperty("userAge", "42").build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isFalse(); - } - - @Test - public void shouldSupportGreaterThanComparisonForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", Operator.NUM_GT, Collections.singletonList("42"))); - Map parameters = new HashMap<>(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - } - - @Test - public void shouldSupportInvertingGreaterThanComparisonForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", Operator.NUM_GT, Collections.singletonList("42"), true)); - Map parameters = new HashMap<>(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - } - - @Test - public void shouldSupportLesserThanForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", - Operator.NUM_LT, - Collections.singletonList("42"), - false)); - Map parameters = new HashMap<>(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - } - - @Test - public void shouldSupportInvertingLesserThanForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", Operator.NUM_LT, Collections.singletonList("42"), true)); - Map parameters = new HashMap<>(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - } - - @Test - public void shouldSupportLessThanOrEqualForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", - Operator.NUM_LTE, - Collections.singletonList("42"), - false)); - Map parameters = new HashMap<>(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - UnleashContext equal = - UnleashContext.builder().environment("dev").addProperty("userAge", "42").build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isTrue(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - } - - @Test - public void shouldSupportInvertingLessThanOrEqualForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", - Operator.NUM_LTE, - Collections.singletonList("42"), - true)); - Map parameters = new HashMap<>(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isFalse(); - UnleashContext equal = - UnleashContext.builder().environment("dev").addProperty("userAge", "42").build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isFalse(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isTrue(); - } - - @Test - public void shouldSupportGreaterThanOrEqualForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", - Operator.NUM_GTE, - Collections.singletonList("42"), - false)); - Map parameters = new HashMap<>(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - UnleashContext equal = - UnleashContext.builder().environment("dev").addProperty("userAge", "42").build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isTrue(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - } - - @Test - public void shouldSupportInvertingGreaterThanOrEqualForNumbers() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "userAge", - Operator.NUM_GTE, - Collections.singletonList("42"), - true)); - Map parameters = new HashMap<>(); - UnleashContext enabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "45").build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isFalse(); - UnleashContext equal = - UnleashContext.builder().environment("dev").addProperty("userAge", "42").build(); - assertThat(strategy.isEnabled(parameters, equal, constraintList)).isFalse(); - UnleashContext disabled = - UnleashContext.builder().environment("dev").addProperty("userAge", "35").build(); - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isTrue(); - } -} diff --git a/src/test/java/io/getunleash/strategy/constraints/SemanticVersionTest.java b/src/test/java/io/getunleash/strategy/constraints/SemanticVersionTest.java deleted file mode 100644 index 8904e91c4..000000000 --- a/src/test/java/io/getunleash/strategy/constraints/SemanticVersionTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.getunleash.strategy.constraints; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -class SemanticVersionTest { - - @Test - public void obeysSgnRequirementForComparator() throws SemanticVersion.InvalidVersionException { - SemanticVersion a = SemanticVersion.parse("1.0.0"); - SemanticVersion b = SemanticVersion.parse("1.0.1"); - assertThat(a).isLessThan(b); - assertThat(b).isGreaterThan(a); - } - - @Test - public void isTransitive() throws SemanticVersion.InvalidVersionException { - SemanticVersion a = SemanticVersion.parse("1.0.0"); - SemanticVersion b = SemanticVersion.parse("1.0.1"); - SemanticVersion c = SemanticVersion.parse("1.1.0"); - assertThat(a).isLessThan(b); - assertThat(b).isLessThan(c); - assertThat(a).isLessThan(c); - } - - @Test - public void rcIsGreaterThanAlphaAndBetaButSmallerThanRelease() - throws SemanticVersion.InvalidVersionException { - SemanticVersion rc = SemanticVersion.parse("1.0.0-rc"); - SemanticVersion alpha = SemanticVersion.parse("1.0.0-alpha.1"); - SemanticVersion beta = SemanticVersion.parse("1.0.0-beta.1"); - SemanticVersion release = SemanticVersion.parse("1.0.0"); - - assertThat(rc).isGreaterThan(alpha); - assertThat(rc).isGreaterThan(beta); - assertThat(rc).isLessThan(release); - } - - @Test - public void alphaVersionsAlsoCounted() throws SemanticVersion.InvalidVersionException { - SemanticVersion alpha = SemanticVersion.parse("1.0.0-alpha.1"); - SemanticVersion alpha2 = SemanticVersion.parse("1.0.0-alpha.2"); - assertThat(alpha.compareTo(alpha2)).isLessThan(0); - assertThat(alpha).isLessThan(alpha2); - } -} diff --git a/src/test/java/io/getunleash/strategy/constraints/SemverConstraintOperatorTest.java b/src/test/java/io/getunleash/strategy/constraints/SemverConstraintOperatorTest.java deleted file mode 100644 index 4b80173cb..000000000 --- a/src/test/java/io/getunleash/strategy/constraints/SemverConstraintOperatorTest.java +++ /dev/null @@ -1,147 +0,0 @@ -package io.getunleash.strategy.constraints; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import io.getunleash.strategy.DefaultStrategy; -import io.getunleash.strategy.Strategy; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class SemverConstraintOperatorTest { - @Test - public void shouldSupportSemverEq() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "version", Operator.SEMVER_EQ, Collections.singletonList("1.2.4"))); - Map parameters = new HashMap<>(); - UnleashContext correctVersion = - UnleashContext.builder().environment("dev").addProperty("version", "1.2.4").build(); - assertThat(strategy.isEnabled(parameters, correctVersion, constraintList)).isTrue(); - UnleashContext wrongVersion = - UnleashContext.builder().environment("dev").addProperty("version", "2.2.4").build(); - assertThat(strategy.isEnabled(parameters, wrongVersion, constraintList)).isFalse(); - } - - @Test - public void shouldSupportInvertingSemverEq() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "version", - Operator.SEMVER_EQ, - Collections.singletonList("1.2.4"), - true)); - Map parameters = new HashMap<>(); - UnleashContext correctVersion = - UnleashContext.builder().environment("dev").addProperty("version", "1.2.4").build(); - assertThat(strategy.isEnabled(parameters, correctVersion, constraintList)).isFalse(); - UnleashContext wrongVersion = - UnleashContext.builder().environment("dev").addProperty("version", "2.2.4").build(); - assertThat(strategy.isEnabled(parameters, wrongVersion, constraintList)).isTrue(); - } - - @Test - public void shouldSupportSemverLt() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "version", Operator.SEMVER_LT, Collections.singletonList("2.0.0"))); - Map parameters = new HashMap<>(); - UnleashContext correctVersion = - UnleashContext.builder().environment("dev").addProperty("version", "1.2.4").build(); - assertThat(strategy.isEnabled(parameters, correctVersion, constraintList)).isTrue(); - UnleashContext wrongVersion = - UnleashContext.builder().environment("dev").addProperty("version", "2.2.4").build(); - assertThat(strategy.isEnabled(parameters, wrongVersion, constraintList)).isFalse(); - } - - @Test - public void shouldSupportInvertingSemverLt() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "version", - Operator.SEMVER_LT, - Collections.singletonList("2.0.0"), - true)); - Map parameters = new HashMap<>(); - UnleashContext correctVersion = - UnleashContext.builder().environment("dev").addProperty("version", "1.2.4").build(); - assertThat(strategy.isEnabled(parameters, correctVersion, constraintList)).isFalse(); - UnleashContext wrongVersion = - UnleashContext.builder().environment("dev").addProperty("version", "2.2.4").build(); - assertThat(strategy.isEnabled(parameters, wrongVersion, constraintList)).isTrue(); - } - - @Test - public void shouldSupportSemverGt() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "version", Operator.SEMVER_GT, Collections.singletonList("2.0.0"))); - Map parameters = new HashMap<>(); - // If there's not a date in the defined context field, compare with Instant.now() - UnleashContext newerVersion = - UnleashContext.builder().environment("dev").addProperty("version", "2.2.4").build(); - assertThat(strategy.isEnabled(parameters, newerVersion, constraintList)).isTrue(); - UnleashContext olderVersion = - UnleashContext.builder().environment("dev").addProperty("version", "1.2.4").build(); - assertThat(strategy.isEnabled(parameters, olderVersion, constraintList)).isFalse(); - } - - @Test - public void shouldSupportInvertingSemverGt() { - - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "version", - Operator.SEMVER_GT, - Collections.singletonList("2.0.0"), - true)); - Map parameters = new HashMap<>(); - // If there's not a date in the defined context field, compare with Instant.now() - UnleashContext newerVersion = - UnleashContext.builder().environment("dev").addProperty("version", "2.2.4").build(); - assertThat(strategy.isEnabled(parameters, newerVersion, constraintList)).isFalse(); - UnleashContext olderVersion = - UnleashContext.builder().environment("dev").addProperty("version", "1.2.4").build(); - assertThat(strategy.isEnabled(parameters, olderVersion, constraintList)).isTrue(); - } - - @Test - public void shouldSupportSemverInRange() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Arrays.asList( - new Constraint( - "version", Operator.SEMVER_GT, Collections.singletonList("2.0.0")), - new Constraint( - "version", Operator.SEMVER_LT, Collections.singletonList("3.0.0"))); - Map parameters = new HashMap<>(); - // If there's not a date in the defined context field, compare with Instant.now() - UnleashContext inRange = - UnleashContext.builder().environment("dev").addProperty("version", "2.2.4").build(); - assertThat(strategy.isEnabled(parameters, inRange, constraintList)).isTrue(); - UnleashContext lessThanRange = - UnleashContext.builder().environment("dev").addProperty("version", "1.2.4").build(); - assertThat(strategy.isEnabled(parameters, lessThanRange, constraintList)).isFalse(); - UnleashContext moreThanRange = - UnleashContext.builder().environment("dev").addProperty("version", "3.2.4").build(); - assertThat(strategy.isEnabled(parameters, moreThanRange, constraintList)).isFalse(); - } -} diff --git a/src/test/java/io/getunleash/strategy/constraints/StringConstraintOperatorTest.java b/src/test/java/io/getunleash/strategy/constraints/StringConstraintOperatorTest.java deleted file mode 100644 index 34234ee85..000000000 --- a/src/test/java/io/getunleash/strategy/constraints/StringConstraintOperatorTest.java +++ /dev/null @@ -1,214 +0,0 @@ -package io.getunleash.strategy.constraints; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.getunleash.Constraint; -import io.getunleash.Operator; -import io.getunleash.UnleashContext; -import io.getunleash.strategy.DefaultStrategy; -import io.getunleash.strategy.Strategy; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class StringConstraintOperatorTest { - @Test - public void shouldBeEnabledWhenEmailStartsWith() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_STARTS_WITH, - Collections.singletonList("example"))); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@getunleash.ai") - .build(); - assertThat(strategy.isEnabled(new HashMap<>(), ctx, constraintList)).isTrue(); - } - - @Test - public void shouldBeEnabledWhenEmailStartsWithMultiple() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_STARTS_WITH, - Arrays.asList("other", "example"))); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@getunleash.ai") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isTrue(); - } - - @Test - public void shouldBeEnabledWhenEmailEndsWith() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_ENDS_WITH, - Collections.singletonList("@getunleash.ai"))); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@getunleash.ai") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isTrue(); - } - - @Test - public void shouldBeEnabledWhenEmailEndsWithIgnoringCase() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_ENDS_WITH, - Collections.singletonList("@getunleash.ai"), - false, - true)); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@GETunleash.ai") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isTrue(); - } - - @Test - public void shouldNotBeEnabledWhenEmailEndsWithCaseSensitive() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_ENDS_WITH, - Collections.singletonList("@getunleash.ai"))); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@GETunleash.ai") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isFalse(); - } - - // Testing inversion of evaluation result - @Test - public void shouldBeEnabledWhenEmailDoesNotEndWith() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_ENDS_WITH, - Collections.singletonList("@getunleash.ai"), - true, - false)); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@something.ai") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isTrue(); - } - - @Test - public void shouldBeEnabledWhenEmailEndsWithMultipleEmails() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_ENDS_WITH, - Arrays.asList("@getunleash.ai", "somerandom-email.com"))); - Map parameters = new HashMap<>(); - UnleashContext enabled = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@getunleash.ai") - .build(); - assertThat(strategy.isEnabled(parameters, enabled, constraintList)).isTrue(); - UnleashContext enabled2 = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@somerandom-email.com") - .build(); - assertThat(strategy.isEnabled(parameters, enabled2, constraintList)).isTrue(); - UnleashContext disabled = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@some-email.com") - .build(); - - assertThat(strategy.isEnabled(parameters, disabled, constraintList)).isFalse(); - } - - @Test - public void shouldNotBeDisabledWhenEmailDoesNotEndWith() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_ENDS_WITH, - Collections.singletonList("@getunleash.ai"))); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@something-else") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isFalse(); - } - - @Test - public void shouldBeEnabledWhenEmailContains() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", Operator.STR_CONTAINS, Collections.singletonList("some"))); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example-some@getunleash.ai") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isTrue(); - } - - @Test - public void shouldSupportInvertingStringContains() { - Strategy strategy = new DefaultStrategy(); - List constraintList = - Collections.singletonList( - new Constraint( - "email", - Operator.STR_CONTAINS, - Collections.singletonList("ample"), - true)); - Map parameters = new HashMap<>(); - UnleashContext ctx = - UnleashContext.builder() - .environment("dev") - .addProperty("email", "example@GETunleash.ai") - .build(); - assertThat(strategy.isEnabled(parameters, ctx, constraintList)).isFalse(); - } -} diff --git a/src/main/java/io/getunleash/strategy/constraints/DateParser.java b/src/test/java/io/getunleash/util/DateParser.java similarity index 97% rename from src/main/java/io/getunleash/strategy/constraints/DateParser.java rename to src/test/java/io/getunleash/util/DateParser.java index 1be4389f4..7061e7375 100644 --- a/src/main/java/io/getunleash/strategy/constraints/DateParser.java +++ b/src/test/java/io/getunleash/util/DateParser.java @@ -1,4 +1,4 @@ -package io.getunleash.strategy.constraints; +package io.getunleash.util; import java.time.LocalDateTime; import java.time.ZoneOffset; diff --git a/src/test/java/io/getunleash/util/IpAddressMatcherTest.java b/src/test/java/io/getunleash/util/IpAddressMatcherTest.java deleted file mode 100644 index f9d48f37c..000000000 --- a/src/test/java/io/getunleash/util/IpAddressMatcherTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2002-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.getunleash.util; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -/** @author Luke Taylor */ -class IpAddressMatcherTest { - private final IpAddressMatcher v6matcher = new IpAddressMatcher("fe80::21f:5bff:fe33:bd68"); - private final IpAddressMatcher v4matcher = new IpAddressMatcher("192.168.1.104"); - - @Test - void ipv6MatcherMatchesIpv6Address() { - assertThat(v6matcher.matches("fe80::21f:5bff:fe33:bd68")).isTrue(); - } - - @Test - void ipv6MatcherDoesntMatchNull() { - assertThat(v6matcher.matches(null)).isFalse(); - assertThat(new IpAddressMatcher("::1").matches(null)).isFalse(); - } - - @Test - void ipv4MatcherDoesntMatchNull() { - assertThat(v4matcher.matches(null)).isFalse(); - assertThat(new IpAddressMatcher("127.0.0.1").matches(null)).isFalse(); - } - - @Test - void ipv6MatcherDoesntMatchIpv4Address() { - assertThat(v6matcher.matches("192.168.1.104")).isFalse(); - } - - @Test - void ipv4MatcherMatchesIpv4Address() { - assertThat(v4matcher.matches("192.168.1.104")).isTrue(); - } - - @Test - void ipv4SubnetMatchesCorrectly() { - IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.0/24"); - assertThat(matcher.matches("192.168.1.104")).isTrue(); - matcher = new IpAddressMatcher("192.168.1.128/25"); - assertThat(matcher.matches("192.168.1.104")).isFalse(); - assertThat(matcher.matches("192.168.1.159")).isTrue(); - } - - @Test - void ipv6RangeMatches() { - IpAddressMatcher matcher = new IpAddressMatcher("2001:DB8::/48"); - - assertThat(matcher.matches("2001:DB8:0:0:0:0:0:0")).isTrue(); - assertThat(matcher.matches("2001:DB8:0:0:0:0:0:1")).isTrue(); - assertThat(matcher.matches("2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF")).isTrue(); - assertThat(matcher.matches("2001:DB8:0:ffff:ffff:ffff:ffff:ffff")).isTrue(); - assertThat(matcher.matches("2001:DB8:1:0:0:0:0:0")).isFalse(); - } - - @Test - void zeroMaskMatchesAnything() { - IpAddressMatcher matcher = new IpAddressMatcher("0.0.0.0/0"); - - assertThat(matcher.matches("123.4.5.6")).isTrue(); - assertThat(matcher.matches("192.168.0.159")).isTrue(); - - matcher = new IpAddressMatcher("192.168.0.159/0"); - assertThat(matcher.matches("123.4.5.6")).isTrue(); - assertThat(matcher.matches("192.168.0.159")).isTrue(); - } -} diff --git a/src/test/java/io/getunleash/variant/VariantUtilTest.java b/src/test/java/io/getunleash/variant/VariantUtilTest.java deleted file mode 100644 index ee431edb6..000000000 --- a/src/test/java/io/getunleash/variant/VariantUtilTest.java +++ /dev/null @@ -1,413 +0,0 @@ -package io.getunleash.variant; - -import static io.getunleash.Variant.DISABLED_VARIANT; -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -import io.getunleash.ActivationStrategy; -import io.getunleash.FeatureToggle; -import io.getunleash.UnleashContext; -import io.getunleash.Variant; -import io.getunleash.util.UnleashConfig; -import io.getunleash.util.UnleashScheduledExecutor; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class VariantUtilTest { - - private ActivationStrategy defaultStrategy; - - @BeforeEach - void setUp() { - UnleashConfig defaultConfig = - new UnleashConfig.Builder() - .appName("test") - .unleashAPI("http://localhost:4242/api/") - .scheduledExecutor(mock(UnleashScheduledExecutor.class)) - .fetchTogglesInterval(200L) - .synchronousFetchOnInitialisation(true) - .build(); - - defaultStrategy = new ActivationStrategy("default", Collections.emptyMap()); - } - - @Test - public void should_return_default_variant_when_toggle_has_no_variants() { - FeatureToggle toggle = new FeatureToggle("test.variants", true, asList(defaultStrategy)); - UnleashContext context = UnleashContext.builder().build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant).isEqualTo(DISABLED_VARIANT); - } - - @Test - public void should_return_variant1() { - VariantDefinition v1 = - new VariantDefinition( - "a", 33, new Payload("string", "asd"), Collections.emptyList()); - VariantDefinition v2 = new VariantDefinition("b", 33); - VariantDefinition v3 = new VariantDefinition("c", 34); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = UnleashContext.builder().userId("11").build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant.getName()).isEqualTo(v1.getName()); - assertThat(variant.getPayload()).hasValue(v1.getPayload()); - assertThat(variant.isEnabled()).isTrue(); - } - - @Test - public void should_return_variant2() { - VariantDefinition v1 = - new VariantDefinition( - "a", 33, new Payload("string", "asd"), Collections.emptyList()); - VariantDefinition v2 = new VariantDefinition("b", 33); - VariantDefinition v3 = new VariantDefinition("c", 34); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = UnleashContext.builder().userId("80").build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant.getName()).isEqualTo(v2.getName()); - } - - @Test - public void should_return_variant3() { - VariantDefinition v1 = new VariantDefinition("a", 33); - VariantDefinition v2 = new VariantDefinition("b", 33); - VariantDefinition v3 = new VariantDefinition("c", 34); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = UnleashContext.builder().userId("163").build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant.getName()).isEqualTo(v3.getName()); - } - - @Test - public void should_return_variant_override() { - VariantDefinition v1 = new VariantDefinition("a", 33); - VariantOverride override = new VariantOverride("userId", asList("11", "12", "123", "44")); - VariantDefinition v2 = new VariantDefinition("b", 33, null, asList(override)); - VariantDefinition v3 = new VariantDefinition("c", 34); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = UnleashContext.builder().userId("123").build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant.getName()).isEqualTo(v2.getName()); - } - - @Test - public void should_return_variant_override_on_remote_adr() { - VariantDefinition v1 = - new VariantDefinition( - "a", 33, new Payload("string", "asd"), Collections.emptyList()); - VariantDefinition v2 = new VariantDefinition("b", 33, null, Collections.emptyList()); - VariantOverride override = new VariantOverride("remoteAddress", asList("11.11.11.11")); - VariantDefinition v3 = - new VariantDefinition("c", 34, new Payload("string", "blob"), asList(override)); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = UnleashContext.builder().remoteAddress("11.11.11.11").build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant.getName()).isEqualTo(v3.getName()); - assertThat(variant.getPayload()).hasValue(v3.getPayload()); - assertThat(variant.isEnabled()).isTrue(); - } - - @Test - public void should_return_variant_override_on_custom_prop() { - VariantDefinition v1 = new VariantDefinition("a", 33); - VariantOverride override = new VariantOverride("env", asList("ci", "local", "dev")); - VariantDefinition v2 = new VariantDefinition("b", 33, null, asList(override)); - VariantDefinition v3 = new VariantDefinition("c", 34); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = - UnleashContext.builder().userId("11").addProperty("env", "dev").build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant.getName()).isEqualTo(v2.getName()); - } - - @Test - public void should_return_variant_override_on_sessionId() { - String sessionId = "122221"; - - VariantDefinition v1 = new VariantDefinition("a", 33); - VariantOverride override_env = new VariantOverride("env", asList("dev")); - VariantOverride override_session = new VariantOverride("sessionId", asList(sessionId)); - VariantDefinition v2 = - new VariantDefinition("b", 33, null, asList(override_env, override_session)); - VariantDefinition v3 = new VariantDefinition("c", 34); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = - UnleashContext.builder() - .userId("11") - .addProperty("env", "prod") - .sessionId(sessionId) - .build(); - - Variant variant = VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT); - - assertThat(variant.getName()).isEqualTo(v2.getName()); - } - - @Test - public void should_distribute_variants_according_to_stickiness() { - VariantDefinition v1 = new VariantDefinition("blue", 1, null, null, "customField"); - VariantDefinition v2 = new VariantDefinition("red", 1, null, null, "customField"); - VariantDefinition v3 = new VariantDefinition("green", 1, null, null, "customField"); - VariantDefinition v4 = new VariantDefinition("yellow", 1, null, null, "customField"); - FeatureToggle variantToggle = - new FeatureToggle( - "toggle-with-variants", - true, - asList(defaultStrategy), - asList(v1, v2, v3, v4)); - UnleashContext context = - UnleashContext.builder() - .userId("11") - .addProperty("env", "prod") - .sessionId("1222221") - .build(); - Map> variantResults = - IntStream.range(0, 10000) - .mapToObj( - i -> - VariantUtil.selectVariant( - variantToggle, context, DISABLED_VARIANT)) - .collect(Collectors.groupingBy(Variant::getName)); - assertThat(variantResults) - .allSatisfy( - (name, variantResult) -> - assertThat(variantResult).hasSizeBetween(2300, 2700)); - } - - @Test - public void should_return_same_variant_when_stickiness_is_set_to_default() { - VariantDefinition v1 = new VariantDefinition("a", 33, null, null, "default"); - VariantDefinition v2 = new VariantDefinition("b", 33, null, null, "default"); - VariantDefinition v3 = new VariantDefinition("c", 34, null, null, "default"); - - FeatureToggle toggle = - new FeatureToggle( - "test.variants", true, asList(defaultStrategy), asList(v1, v2, v3)); - - UnleashContext context = UnleashContext.builder().userId("163;").build(); - List results = - IntStream.range(0, 500) - .mapToObj(i -> VariantUtil.selectVariant(toggle, context, DISABLED_VARIANT)) - .collect(Collectors.toList()); - assertThat(results) - .allSatisfy( - (Consumer) - variant -> assertThat(variant.getName()).isEqualTo(v3.getName())); - } - - @Test - public void custom_stickiness_variants() { - Map parameters = new HashMap<>(); - parameters.put("rollout", "100"); - parameters.put("stickiness", "customField"); - parameters.put("groupId", "Feature.flexible.rollout.custom.stickiness_100"); - ActivationStrategy flexibleRollout = new ActivationStrategy("flexibleRollout", parameters); - List variants = new ArrayList<>(); - variants.add( - new VariantDefinition( - "blue", - 25, - new Payload("string", "val1"), - Collections.emptyList(), - "customField")); - variants.add( - new VariantDefinition( - "red", - 25, - new Payload("string", "val1"), - Collections.emptyList(), - "customField")); - variants.add( - new VariantDefinition( - "green", - 25, - new Payload("string", "val1"), - Collections.emptyList(), - "customField")); - variants.add( - new VariantDefinition( - "yellow", - 25, - new Payload("string", "val1"), - Collections.emptyList(), - "customField")); - FeatureToggle toggle = - new FeatureToggle( - "Feature.flexible.rollout.custom.stickiness_100", - true, - asList(flexibleRollout), - variants); - Variant variantCustom616 = - VariantUtil.selectVariant( - toggle, - UnleashContext.builder().addProperty("customField", "616").build(), - DISABLED_VARIANT); - assertThat(variantCustom616.getName()).isEqualTo("blue"); - Variant variantCustom503 = - VariantUtil.selectVariant( - toggle, - UnleashContext.builder().addProperty("customField", "503").build(), - DISABLED_VARIANT); - assertThat(variantCustom503.getName()).isEqualTo("red"); - Variant variantCustom438 = - VariantUtil.selectVariant( - toggle, - UnleashContext.builder().addProperty("customField", "438").build(), - DISABLED_VARIANT); - assertThat(variantCustom438.getName()).isEqualTo("green"); - Variant variantCustom44 = - VariantUtil.selectVariant( - toggle, - UnleashContext.builder().addProperty("customField", "44").build(), - DISABLED_VARIANT); - assertThat(variantCustom44.getName()).isEqualTo("yellow"); - } - - @Test - public void feature_variants_variant_b_client_spec_tests() { - List variants = new ArrayList<>(); - variants.add( - new VariantDefinition( - "variant1", 1, new Payload("string", "val1"), Collections.emptyList())); - variants.add( - new VariantDefinition( - "variant2", 1, new Payload("string", "val2"), Collections.emptyList())); - FeatureToggle toggle = - new FeatureToggle("Feature.Variants.B", true, Collections.emptyList(), variants); - Variant variantUser2 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("2").build(), DISABLED_VARIANT); - assertThat(variantUser2.getName()).isEqualTo("variant2"); - Variant variantUser0 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("0").build(), DISABLED_VARIANT); - assertThat(variantUser0.getName()).isEqualTo("variant1"); - } - - @Test - public void feature_variants_variant_c_client_spec_tests() { - List variants = new ArrayList<>(); - variants.add( - new VariantDefinition( - "variant1", 33, new Payload("string", "val1"), Collections.emptyList())); - variants.add( - new VariantDefinition( - "variant2", 33, new Payload("string", "val1"), Collections.emptyList())); - variants.add( - new VariantDefinition( - "variant3", 33, new Payload("string", "val1"), Collections.emptyList())); - FeatureToggle toggle = - new FeatureToggle("Feature.Variants.C", true, Collections.emptyList(), variants); - Variant variantUser232 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("232").build(), DISABLED_VARIANT); - assertThat(variantUser232.getName()).isEqualTo("variant1"); - Variant variantUser607 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("607").build(), DISABLED_VARIANT); - assertThat(variantUser607.getName()).isEqualTo("variant2"); - Variant variantUser656 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("656").build(), DISABLED_VARIANT); - assertThat(variantUser656.getName()).isEqualTo("variant3"); - } - - @Test - public void feature_variants_variant_d_client_spec_tests() { - List variants = new ArrayList<>(); - variants.add( - new VariantDefinition( - "variant1", 1, new Payload("string", "val1"), Collections.emptyList())); - variants.add( - new VariantDefinition( - "variant2", 49, new Payload("string", "val2"), Collections.emptyList())); - variants.add( - new VariantDefinition( - "variant3", 50, new Payload("string", "val3"), Collections.emptyList())); - FeatureToggle toggle = - new FeatureToggle("Feature.Variants.D", true, Collections.emptyList(), variants); - Variant variantUser712 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("712").build(), DISABLED_VARIANT); - assertThat(variantUser712.getName()).isEqualTo("variant1"); - Variant variantUser525 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("525").build(), DISABLED_VARIANT); - assertThat(variantUser525.getName()).isEqualTo("variant2"); - Variant variantUser537 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("537").build(), DISABLED_VARIANT); - assertThat(variantUser537.getName()).isEqualTo("variant3"); - } - - @Test - public void feature_variants_variant_d_with_override_client_spec_tests() { - List variants = new ArrayList<>(); - variants.add( - new VariantDefinition( - "variant1", - 33, - new Payload("string", "val1"), - Arrays.asList(new VariantOverride("userId", asList("132", "61"))))); - variants.add( - new VariantDefinition( - "variant2", 33, new Payload("string", "val2"), Collections.emptyList())); - variants.add( - new VariantDefinition( - "variant3", 34, new Payload("string", "val3"), Collections.emptyList())); - FeatureToggle toggle = - new FeatureToggle( - "Feature.Variants.override.D", true, Collections.emptyList(), variants); - Variant variantUser10 = - VariantUtil.selectVariant( - toggle, UnleashContext.builder().userId("10").build(), DISABLED_VARIANT); - assertThat(variantUser10.getName()).isEqualTo("variant2"); - } -} diff --git a/unleash-engine-all.jar b/unleash-engine-all.jar index 0207d7af2..e41d42be7 100644 Binary files a/unleash-engine-all.jar and b/unleash-engine-all.jar differ