diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 35ab54025..af0dccf0a 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -45,7 +45,7 @@ jobs: test: if: startsWith(github.ref, 'refs/tags/') != true - runs-on: ubuntu-18.04 + runs-on: macos-latest strategy: fail-fast: false matrix: diff --git a/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java b/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java index d05df3bbb..e59c4f3aa 100644 --- a/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java +++ b/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java @@ -36,6 +36,8 @@ public class OptimizelyUserContext { @Nonnull private final Map attributes; + private List qualifiedSegments; + @Nonnull private final Optimizely optimizely; @@ -44,19 +46,14 @@ public class OptimizelyUserContext { public OptimizelyUserContext(@Nonnull Optimizely optimizely, @Nonnull String userId, @Nonnull Map attributes) { - this.optimizely = optimizely; - this.userId = userId; - if (attributes != null) { - this.attributes = Collections.synchronizedMap(new HashMap<>(attributes)); - } else { - this.attributes = Collections.synchronizedMap(new HashMap<>()); - } + this(optimizely, userId, attributes, Collections.EMPTY_MAP, null); } public OptimizelyUserContext(@Nonnull Optimizely optimizely, @Nonnull String userId, @Nonnull Map attributes, - @Nullable Map forcedDecisionsMap) { + @Nullable Map forcedDecisionsMap, + @Nullable List qualifiedSegments) { this.optimizely = optimizely; this.userId = userId; if (attributes != null) { @@ -65,8 +62,10 @@ public OptimizelyUserContext(@Nonnull Optimizely optimizely, this.attributes = Collections.synchronizedMap(new HashMap<>()); } if (forcedDecisionsMap != null) { - this.forcedDecisionsMap = new ConcurrentHashMap<>(forcedDecisionsMap); + this.forcedDecisionsMap = new ConcurrentHashMap<>(forcedDecisionsMap); } + + this.qualifiedSegments = Collections.synchronizedList( qualifiedSegments == null ? new LinkedList<>(): qualifiedSegments); } public OptimizelyUserContext(@Nonnull Optimizely optimizely, @Nonnull String userId) { @@ -86,7 +85,16 @@ public Optimizely getOptimizely() { } public OptimizelyUserContext copy() { - return new OptimizelyUserContext(optimizely, userId, attributes, forcedDecisionsMap); + return new OptimizelyUserContext(optimizely, userId, attributes, forcedDecisionsMap, qualifiedSegments); + } + + /** + * Returns true if the user is qualified for the given segment name + * @param segment A String segment key which will be checked in the qualified segments list that if it exists then user is qualified. + * @return boolean Is user qualified for a segment. + */ + public boolean isQualifiedFor(@Nonnull String segment) { + return qualifiedSegments.contains(segment); } /** @@ -265,7 +273,14 @@ public boolean removeAllForcedDecisions() { return true; } + public List getQualifiedSegments() { + return qualifiedSegments; + } + public void setQualifiedSegments(List qualifiedSegments) { + this.qualifiedSegments.clear(); + this.qualifiedSegments.addAll(qualifiedSegments); + } // Utils diff --git a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java index c7ee0b3f3..84d47d03f 100644 --- a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java +++ b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java @@ -152,7 +152,7 @@ public DecisionResponse getVariation(@Nonnull Experiment experiment, } } - DecisionResponse decisionMeetAudience = ExperimentUtils.doesUserMeetAudienceConditions(projectConfig, experiment, user.getAttributes(), EXPERIMENT, experiment.getKey()); + DecisionResponse decisionMeetAudience = ExperimentUtils.doesUserMeetAudienceConditions(projectConfig, experiment, user, EXPERIMENT, experiment.getKey()); reasons.merge(decisionMeetAudience.getReasons()); if (decisionMeetAudience.getResult()) { String bucketingId = getBucketingId(user.getUserId(), user.getAttributes()); @@ -693,7 +693,7 @@ DecisionResponse getVariationFromDeliveryRule(@Nonnull DecisionResponse audienceDecisionResponse = ExperimentUtils.doesUserMeetAudienceConditions( projectConfig, rule, - user.getAttributes(), + user, RULE, String.valueOf(ruleIndex + 1) ); diff --git a/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java b/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java index 9620f5cbf..65edf5768 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java @@ -63,6 +63,8 @@ public class DatafileProjectConfig implements ProjectConfig { private final boolean anonymizeIP; private final boolean sendFlagDecisions; private final Boolean botFiltering; + private final String hostForODP; + private final String publicKeyForODP; private final List attributes; private final List audiences; private final List typedAudiences; @@ -71,6 +73,7 @@ public class DatafileProjectConfig implements ProjectConfig { private final List featureFlags; private final List groups; private final List rollouts; + private final List integrations; // key to entity mappings private final Map attributeKeyMapping; @@ -121,6 +124,7 @@ public DatafileProjectConfig(String accountId, String projectId, String version, experiments, null, groups, + null, null ); } @@ -142,8 +146,8 @@ public DatafileProjectConfig(String accountId, List experiments, List featureFlags, List groups, - List rollouts) { - + List rollouts, + List integrations) { this.accountId = accountId; this.projectId = projectId; this.version = version; @@ -182,6 +186,24 @@ public DatafileProjectConfig(String accountId, allExperiments.addAll(aggregateGroupExperiments(groups)); this.experiments = Collections.unmodifiableList(allExperiments); + String publicKeyForODP = ""; + String hostForODP = ""; + if (integrations == null) { + this.integrations = Collections.emptyList(); + } else { + this.integrations = Collections.unmodifiableList(integrations); + for (Integration integration: this.integrations) { + if (integration.getKey().equals("odp")) { + hostForODP = integration.getHost(); + publicKeyForODP = integration.getPublicKey(); + break; + } + } + } + + this.publicKeyForODP = publicKeyForODP; + this.hostForODP = hostForODP; + Map variationIdToExperimentMap = new HashMap(); for (Experiment experiment : this.experiments) { for (Variation variation : experiment.getVariations()) { @@ -448,6 +470,11 @@ public List getTypedAudiences() { return typedAudiences; } + @Override + public List getIntegrations() { + return integrations; + } + @Override public Audience getAudience(String audienceId) { return audienceIdMapping.get(audienceId); @@ -524,6 +551,16 @@ public Variation getFlagVariationByKey(String flagKey, String variationKey) { return null; } + @Override + public String getHostForODP() { + return hostForODP; + } + + @Override + public String getPublicKeyForODP() { + return publicKeyForODP; + } + @Override public String toString() { return "ProjectConfig{" + diff --git a/core-api/src/main/java/com/optimizely/ab/config/Integration.java b/core-api/src/main/java/com/optimizely/ab/config/Integration.java new file mode 100644 index 000000000..ed24df625 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/config/Integration.java @@ -0,0 +1,67 @@ +/** + * + * Copyright 2022, Optimizely and contributors + * + * 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 + * + * http://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 com.optimizely.ab.config; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Represents the Optimizely Integration configuration. + * + * @see Project JSON + */ +@Immutable +@JsonIgnoreProperties(ignoreUnknown = true) +public class Integration { + private final String key; + private final String host; + private final String publicKey; + + @JsonCreator + public Integration(@JsonProperty("key") String key, + @JsonProperty("host") String host, + @JsonProperty("publicKey") String publicKey) { + this.key = key; + this.host = host; + this.publicKey = publicKey; + } + + @Nonnull + public String getKey() { + return key; + } + + @Nullable + public String getHost() { return host; } + + @Nullable + public String getPublicKey() { return publicKey; } + + @Override + public String toString() { + return "Integration{" + + "key='" + key + '\'' + + ((this.host != null) ? (", host='" + host + '\'') : "") + + ((this.publicKey != null) ? (", publicKey='" + publicKey + '\'') : "") + + '}'; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java index 10ebdc832..be512bd04 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java @@ -83,6 +83,8 @@ Experiment getExperimentForKey(@Nonnull String experimentKey, List getTypedAudiences(); + List getIntegrations(); + Audience getAudience(String audienceId); Map getExperimentKeyMapping(); @@ -107,6 +109,10 @@ Experiment getExperimentForKey(@Nonnull String experimentKey, Variation getFlagVariationByKey(String flagKey, String variationKey); + String getHostForODP(); + + String getPublicKeyForODP(); + @Override String toString(); diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java index f6561a65c..8d855e3e9 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AndCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2019, Optimizely and contributors + * Copyright 2016-2019, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ */ package com.optimizely.ab.config.audience; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nonnull; @@ -42,7 +43,7 @@ public List getConditions() { } @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, OptimizelyUserContext user) { if (conditions == null) return null; boolean foundNull = false; // According to the matrix where: @@ -53,7 +54,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { // true and true is true // null and null is null for (Condition condition : conditions) { - Boolean conditionEval = condition.evaluate(config, attributes); + Boolean conditionEval = condition.evaluate(config, user); if (conditionEval == null) { foundNull = true; } else if (!conditionEval) { // false with nulls or trues is false. diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AttributeType.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AttributeType.java new file mode 100644 index 000000000..2a1be3880 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AttributeType.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2022, Optimizely and contributors + * + * 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 + * + * http://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 com.optimizely.ab.config.audience; + +public enum AttributeType { + CUSTOM_ATTRIBUTE("custom_attribute"), + THIRD_PARTY_DIMENSION("third_party_dimension"); + + private final String key; + + AttributeType(String key) { + this.key = key; + } + + @Override + public String toString() { + return key; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java index e07757016..4b3341d4c 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/AudienceIdCondition.java @@ -1,12 +1,12 @@ /** * - * Copyright 2018-2021, Optimizely and contributors + * Copyright 2018-2022, Optimizely and contributors * * 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import com.optimizely.ab.internal.InvalidAudienceCondition; import org.slf4j.Logger; @@ -71,7 +72,7 @@ public String getOperandOrId() { @Nullable @Override - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, OptimizelyUserContext user) { if (config != null) { audience = config.getAudienceIdMapping().get(audienceId); } @@ -80,7 +81,7 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } logger.debug("Starting to evaluate audience \"{}\" with conditions: {}.", audience.getId(), audience.getConditions()); - Boolean result = audience.getConditions().evaluate(config, attributes); + Boolean result = audience.getConditions().evaluate(config, user); logger.debug("Audience \"{}\" evaluated to {}.", audience.getId(), result); return result; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java index 11b7165b9..105c9b8e0 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/Condition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2018, Optimizely and contributors + * Copyright 2016-2018, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ */ package com.optimizely.ab.config.audience; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nullable; @@ -27,7 +28,7 @@ public interface Condition { @Nullable - Boolean evaluate(ProjectConfig config, Map attributes); + Boolean evaluate(ProjectConfig config, OptimizelyUserContext user); String toJson(); diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java index 9bb355a13..61e010317 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/EmptyCondition.java @@ -1,5 +1,5 @@ /** - * Copyright 2019, Optimizely Inc. and contributors + * Copyright 2019, 2022, Optimizely Inc. and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.optimizely.ab.config.audience; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nullable; @@ -23,7 +24,7 @@ public class EmptyCondition implements Condition { @Nullable @Override - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, OptimizelyUserContext user) { return true; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java index cabc07812..9e4b2fb86 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NotCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2019, Optimizely and contributors + * Copyright 2016-2019, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,13 @@ */ package com.optimizely.ab.config.audience; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.annotation.Nonnull; -import java.util.Map; -import java.util.StringJoiner; /** * Represents a 'Not' conditions condition operation. @@ -43,9 +42,8 @@ public Condition getCondition() { } @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { - - Boolean conditionEval = condition == null ? null : condition.evaluate(config, attributes); + public Boolean evaluate(ProjectConfig config, OptimizelyUserContext user) { + Boolean conditionEval = condition == null ? null : condition.evaluate(config, user); return (conditionEval == null ? null : !conditionEval); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java index 10633aed9..44ddee7ca 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/NullCondition.java @@ -1,5 +1,5 @@ /** - * Copyright 2019, Optimizely Inc. and contributors + * Copyright 2019, 2022, Optimizely Inc. and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.optimizely.ab.config.audience; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nullable; @@ -23,7 +24,7 @@ public class NullCondition implements Condition { @Nullable @Override - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, OptimizelyUserContext user) { return null; } diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java index 293687f66..12fbbb3b2 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/OrCondition.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2019, Optimizely and contributors + * Copyright 2016-2019, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ */ package com.optimizely.ab.config.audience; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import javax.annotation.Nonnull; @@ -47,11 +48,11 @@ public List getConditions() { // false or false is false // null or null is null @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { + public Boolean evaluate(ProjectConfig config, OptimizelyUserContext user) { if (conditions == null) return null; boolean foundNull = false; for (Condition condition : conditions) { - Boolean conditionEval = condition.evaluate(config, attributes); + Boolean conditionEval = condition.evaluate(config, user); if (conditionEval == null) { // true with falses and nulls is still true foundNull = true; } else if (conditionEval) { diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java index ed029f89c..e7ff16f31 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2020, Optimizely and contributors + * Copyright 2016-2020, 2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.ProjectConfig; import com.optimizely.ab.config.audience.match.*; import org.slf4j.Logger; @@ -27,8 +28,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import java.util.Collections; -import java.util.Map; +import java.util.*; + +import static com.optimizely.ab.config.audience.AttributeType.CUSTOM_ATTRIBUTE; +import static com.optimizely.ab.config.audience.AttributeType.THIRD_PARTY_DIMENSION; /** * Represents a user attribute instance within an audience's conditions. @@ -36,13 +39,14 @@ @Immutable @JsonIgnoreProperties(ignoreUnknown = true) public class UserAttribute implements Condition { + public static final String QUALIFIED = "qualified"; private static final Logger logger = LoggerFactory.getLogger(UserAttribute.class); private final String name; private final String type; private final String match; private final Object value; - + private final static List ATTRIBUTE_TYPE = Arrays.asList(new String[]{CUSTOM_ATTRIBUTE.toString(), THIRD_PARTY_DIMENSION.toString()}); @JsonCreator public UserAttribute(@JsonProperty("name") @Nonnull String name, @JsonProperty("type") @Nonnull String type, @@ -71,19 +75,25 @@ public Object getValue() { } @Nullable - public Boolean evaluate(ProjectConfig config, Map attributes) { - if (attributes == null) { - attributes = Collections.emptyMap(); - } + public Boolean evaluate(ProjectConfig config, OptimizelyUserContext user) { + Map attributes = user.getAttributes(); // Valid for primitive types, but needs to change when a value is an object or an array Object userAttributeValue = attributes.get(name); - if (!"custom_attribute".equals(type)) { + if (!isValidType(type)) { logger.warn("Audience condition \"{}\" uses an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK.", this); return null; // unknown type } // check user attribute value is equal try { + // Handle qualified segments + if (QUALIFIED.equals(match)) { + if (value instanceof String) { + return user.isQualifiedFor(value.toString()); + } + throw new UnknownValueTypeException(); + } + // Handle other conditions Match matcher = MatchRegistry.getMatch(match); Boolean result = matcher.eval(value, userAttributeValue); if (result == null) { @@ -118,6 +128,13 @@ public Boolean evaluate(ProjectConfig config, Map attributes) { return null; } + private boolean isValidType(String type) { + if (ATTRIBUTE_TYPE.contains(type)) { + return true; + } + return false; + } + @Override public String getOperandOrId() { return null; diff --git a/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java b/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java index f78c35c8d..7563d2681 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java +++ b/core-api/src/main/java/com/optimizely/ab/config/audience/match/MatchRegistry.java @@ -1,6 +1,6 @@ /** * - * Copyright 2020-2021, Optimizely and contributors + * Copyright 2020-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileGsonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileGsonDeserializer.java index 26fe47330..f349805fa 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileGsonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileGsonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2021, Optimizely and contributors + * Copyright 2016-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,6 +86,7 @@ public ProjectConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializa List featureFlags = null; List rollouts = null; + List integrations = null; Boolean botFiltering = null; String sdkKey = null; String environmentKey = null; @@ -97,6 +98,10 @@ public ProjectConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializa Type rolloutsType = new TypeToken>() { }.getType(); rollouts = context.deserialize(jsonObject.get("rollouts").getAsJsonArray(), rolloutsType); + if (jsonObject.has("integrations")) { + Type integrationsType = new TypeToken>() {}.getType(); + integrations = context.deserialize(jsonObject.get("integrations").getAsJsonArray(), integrationsType); + } if (jsonObject.has("sdkKey")) sdkKey = jsonObject.get("sdkKey").getAsString(); if (jsonObject.has("environmentKey")) @@ -124,7 +129,8 @@ public ProjectConfig deserialize(JsonElement json, Type typeOfT, JsonDeserializa experiments, featureFlags, groups, - rollouts + rollouts, + integrations ); } } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileJacksonDeserializer.java b/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileJacksonDeserializer.java index 4cded2ecb..4ef104428 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileJacksonDeserializer.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/DatafileJacksonDeserializer.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2021, Optimizely and contributors + * Copyright 2016-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,6 +63,7 @@ public DatafileProjectConfig deserialize(JsonParser parser, DeserializationConte List featureFlags = null; List rollouts = null; + List integrations = null; String sdkKey = null; String environmentKey = null; Boolean botFiltering = null; @@ -70,6 +71,9 @@ public DatafileProjectConfig deserialize(JsonParser parser, DeserializationConte if (datafileVersion >= Integer.parseInt(DatafileProjectConfig.Version.V4.toString())) { featureFlags = JacksonHelpers.arrayNodeToList(node.get("featureFlags"), FeatureFlag.class, codec); rollouts = JacksonHelpers.arrayNodeToList(node.get("rollouts"), Rollout.class, codec); + if (node.hasNonNull("integrations")) { + integrations = JacksonHelpers.arrayNodeToList(node.get("integrations"), Integration.class, codec); + } if (node.hasNonNull("sdkKey")) { sdkKey = node.get("sdkKey").textValue(); } @@ -101,7 +105,8 @@ public DatafileProjectConfig deserialize(JsonParser parser, DeserializationConte experiments, featureFlags, groups, - rollouts + rollouts, + integrations ); } diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java index c33f30a68..ea5101054 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2021, Optimizely and contributors + * Copyright 2016-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,6 +72,7 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse List featureFlags = null; List rollouts = null; + List integrations = null; String sdkKey = null; String environmentKey = null; Boolean botFiltering = null; @@ -79,6 +80,9 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse if (datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString())) { featureFlags = parseFeatureFlags(rootObject.getJSONArray("featureFlags")); rollouts = parseRollouts(rootObject.getJSONArray("rollouts")); + if (rootObject.has("integrations")) { + integrations = parseIntegrations(rootObject.getJSONArray("integrations")); + } if (rootObject.has("sdkKey")) sdkKey = rootObject.getString("sdkKey"); if (rootObject.has("environmentKey")) @@ -106,7 +110,8 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse experiments, featureFlags, groups, - rollouts + rollouts, + integrations ); } catch (RuntimeException e) { throw new ConfigParseException("Unable to parse datafile: " + json, e); @@ -399,6 +404,21 @@ private List parseRollouts(JSONArray rolloutsJson) { return rollouts; } + private List parseIntegrations(JSONArray integrationsJson) { + List integrations = new ArrayList(integrationsJson.length()); + + for (int i = 0; i < integrationsJson.length(); i++) { + Object obj = integrationsJson.get(i); + JSONObject integrationObject = (JSONObject) obj; + String key = integrationObject.getString("key"); + String host = integrationObject.has("host") ? integrationObject.getString("host") : null; + String publicKey = integrationObject.has("publicKey") ? integrationObject.getString("publicKey") : null; + integrations.add(new Integration(key, host, publicKey)); + } + + return integrations; + } + @Override public String toJson(Object src) { JSONObject json = (JSONObject)JsonHelpers.convertToJsonObject(src); diff --git a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java index 751e651ca..c65eb6213 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java +++ b/core-api/src/main/java/com/optimizely/ab/config/parser/JsonSimpleConfigParser.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016-2021, Optimizely and contributors + * Copyright 2016-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,11 +81,15 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse List featureFlags = null; List rollouts = null; + List integrations = null; Boolean botFiltering = null; boolean sendFlagDecisions = false; if (datafileVersion >= Integer.parseInt(DatafileProjectConfig.Version.V4.toString())) { featureFlags = parseFeatureFlags((JSONArray) rootObject.get("featureFlags")); rollouts = parseRollouts((JSONArray) rootObject.get("rollouts")); + if (rootObject.containsKey("integrations")) { + integrations = parseIntegrations((JSONArray) rootObject.get("integrations")); + } if (rootObject.containsKey("botFiltering")) botFiltering = (Boolean) rootObject.get("botFiltering"); if (rootObject.containsKey("sendFlagDecisions")) @@ -109,7 +113,8 @@ public ProjectConfig parseProjectConfig(@Nonnull String json) throws ConfigParse experiments, featureFlags, groups, - rollouts + rollouts, + integrations ); } catch (RuntimeException ex) { throw new ConfigParseException("Unable to parse datafile: " + json, ex); @@ -379,6 +384,20 @@ private List parseRollouts(JSONArray rolloutsJson) { return rollouts; } + private List parseIntegrations(JSONArray integrationsJson) { + List integrations = new ArrayList<>(integrationsJson.size()); + + for (Object obj : integrationsJson) { + JSONObject integrationObject = (JSONObject) obj; + String key = (String) integrationObject.get("key"); + String host = (String) integrationObject.get("host"); + String publicKey = (String) integrationObject.get("publicKey"); + integrations.add(new Integration(key, host, publicKey)); + } + + return integrations; + } + @Override public String toJson(Object src) { return JSONValue.toJSONString(src); diff --git a/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java b/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java index c1494bbda..8da421885 100644 --- a/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java +++ b/core-api/src/main/java/com/optimizely/ab/internal/ExperimentUtils.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2021, Optimizely and contributors + * Copyright 2017-2022, Optimizely and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ */ package com.optimizely.ab.internal; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.config.Experiment; import com.optimizely.ab.config.ProjectConfig; import com.optimizely.ab.config.audience.AudienceIdCondition; @@ -54,7 +55,7 @@ public static boolean isExperimentActive(@Nonnull Experiment experiment) { * * @param projectConfig the current projectConfig * @param experiment the experiment we are evaluating audiences for - * @param attributes the attributes of the user + * @param user the current OptimizelyUserContext * @param loggingEntityType It can be either experiment or rule. * @param loggingKey In case of loggingEntityType is experiment it will be experiment key or else it will be rule number. * @return whether the user meets the criteria for the experiment @@ -62,7 +63,7 @@ public static boolean isExperimentActive(@Nonnull Experiment experiment) { @Nonnull public static DecisionResponse doesUserMeetAudienceConditions(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment experiment, - @Nonnull Map attributes, + @Nonnull OptimizelyUserContext user, @Nonnull String loggingEntityType, @Nonnull String loggingKey) { DecisionReasons reasons = DefaultDecisionReasons.newInstance(); @@ -70,9 +71,9 @@ public static DecisionResponse doesUserMeetAudienceConditions(@Nonnull DecisionResponse decisionResponse; if (experiment.getAudienceConditions() != null) { logger.debug("Evaluating audiences for {} \"{}\": {}.", loggingEntityType, loggingKey, experiment.getAudienceConditions()); - decisionResponse = evaluateAudienceConditions(projectConfig, experiment, attributes, loggingEntityType, loggingKey); + decisionResponse = evaluateAudienceConditions(projectConfig, experiment, user, loggingEntityType, loggingKey); } else { - decisionResponse = evaluateAudience(projectConfig, experiment, attributes, loggingEntityType, loggingKey); + decisionResponse = evaluateAudience(projectConfig, experiment, user, loggingEntityType, loggingKey); } Boolean resolveReturn = decisionResponse.getResult(); @@ -86,7 +87,7 @@ public static DecisionResponse doesUserMeetAudienceConditions(@Nonnull @Nonnull public static DecisionResponse evaluateAudience(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment experiment, - @Nonnull Map attributes, + @Nonnull OptimizelyUserContext user, @Nonnull String loggingEntityType, @Nonnull String loggingKey) { DecisionReasons reasons = DefaultDecisionReasons.newInstance(); @@ -108,7 +109,7 @@ public static DecisionResponse evaluateAudience(@Nonnull ProjectConfig logger.debug("Evaluating audiences for {} \"{}\": {}.", loggingEntityType, loggingKey, conditions); - Boolean result = implicitOr.evaluate(projectConfig, attributes); + Boolean result = implicitOr.evaluate(projectConfig, user); String message = reasons.addInfo("Audiences for %s \"%s\" collectively evaluated to %s.", loggingEntityType, loggingKey, result); logger.info(message); @@ -118,7 +119,7 @@ public static DecisionResponse evaluateAudience(@Nonnull ProjectConfig @Nonnull public static DecisionResponse evaluateAudienceConditions(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment experiment, - @Nonnull Map attributes, + @Nonnull OptimizelyUserContext user, @Nonnull String loggingEntityType, @Nonnull String loggingKey) { DecisionReasons reasons = DefaultDecisionReasons.newInstance(); @@ -128,7 +129,7 @@ public static DecisionResponse evaluateAudienceConditions(@Nonnull Proj Boolean result = null; try { - result = conditions.evaluate(projectConfig, attributes); + result = conditions.evaluate(projectConfig, user); String message = reasons.addInfo("Audiences for %s \"%s\" collectively evaluated to %s.", loggingEntityType, loggingKey, result); logger.info(message); } catch (Exception e) { diff --git a/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java b/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java index 49eaac733..9b65421bb 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java +++ b/core-api/src/test/java/com/optimizely/ab/config/DatafileProjectConfigTestUtils.java @@ -474,6 +474,7 @@ public static void verifyProjectConfig(@CheckForNull ProjectConfig actual, @Nonn verifyFeatureFlags(actual.getFeatureFlags(), expected.getFeatureFlags()); verifyGroups(actual.getGroups(), expected.getGroups()); verifyRollouts(actual.getRollouts(), expected.getRollouts()); + verifyIntegrations(actual.getIntegrations(), expected.getIntegrations()); } /** @@ -491,7 +492,7 @@ private static void verifyExperiments(List actual, List assertThat(actualExperiment.getGroupId(), is(expectedExperiment.getGroupId())); assertThat(actualExperiment.getStatus(), is(expectedExperiment.getStatus())); assertThat(actualExperiment.getAudienceIds(), is(expectedExperiment.getAudienceIds())); - assertEquals(actualExperiment.getAudienceConditions(), expectedExperiment.getAudienceConditions()); + assertThat(actualExperiment.getAudienceConditions(), is(expectedExperiment.getAudienceConditions())); assertThat(actualExperiment.getUserIdToVariationKeyMap(), is(expectedExperiment.getUserIdToVariationKeyMap())); @@ -627,6 +628,23 @@ private static void verifyRollouts(List actual, List expected) } } + private static void verifyIntegrations(List actual, List expected) { + if (expected == null) { + assertNull(actual); + } else { + assertEquals(expected.size(), actual.size()); + + for (int i = 0; i < actual.size(); i++) { + Integration actualIntegrations = actual.get(i); + Integration expectedIntegration = expected.get(i); + + assertEquals(expectedIntegration.getKey(), actualIntegrations.getKey()); + assertEquals(expectedIntegration.getHost(), actualIntegrations.getHost()); + assertEquals(expectedIntegration.getPublicKey(), actualIntegrations.getPublicKey()); + } + } + } + /** * Verify that the provided variation-level feature variable usage instances are equivalent. */ diff --git a/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java b/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java index f8ea02231..0ed8d5945 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java +++ b/core-api/src/test/java/com/optimizely/ab/config/ValidProjectConfigV4.java @@ -1360,6 +1360,7 @@ public class ValidProjectConfigV4 { VARIABLE_INTEGER_VARIABLE ) ); + public static final Integration odpIntegration = new Integration("odp", "https://example.com", "test-key"); public static ProjectConfig generateValidProjectConfigV4() { @@ -1429,6 +1430,9 @@ public static ProjectConfig generateValidProjectConfigV4() { rollouts.add(ROLLOUT_2); rollouts.add(ROLLOUT_3); + List integrations = new ArrayList<>(); + integrations.add(odpIntegration); + return new DatafileProjectConfig( ACCOUNT_ID, ANONYMIZE_IP, @@ -1446,7 +1450,8 @@ public static ProjectConfig generateValidProjectConfigV4() { experiments, featureFlags, groups, - rollouts + rollouts, + integrations ); } } diff --git a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java index 07a0c1ad1..5a69b35ee 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/audience/AudienceConditionEvaluationTest.java @@ -17,13 +17,16 @@ package com.optimizely.ab.config.audience; import ch.qos.logback.classic.Level; -import com.fasterxml.jackson.databind.deser.std.MapEntryDeserializer; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.internal.LogbackVerifier; +import com.optimizely.ab.testutils.OTUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.Mock; import org.mockito.internal.matchers.Or; +import org.mockito.internal.util.reflection.Whitebox; import java.math.BigInteger; import java.util.*; @@ -129,7 +132,7 @@ public void userAttributeEvaluateTrue() throws Exception { assertNull(testInstance.getMatch()); assertEquals(testInstance.getName(), "browser_type"); assertEquals(testInstance.getType(), "custom_attribute"); - assertTrue(testInstance.evaluate(null, testUserAttributes)); + assertTrue(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -138,7 +141,7 @@ public void userAttributeEvaluateTrue() throws Exception { @Test public void userAttributeEvaluateFalse() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", null, "firefox"); - assertFalse(testInstance.evaluate(null, testUserAttributes)); + assertFalse(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -147,7 +150,7 @@ public void userAttributeEvaluateFalse() throws Exception { @Test public void userAttributeUnknownAttribute() throws Exception { UserAttribute testInstance = new UserAttribute("unknown_dim", "custom_attribute", null, "unknown"); - assertFalse(testInstance.evaluate(null, testUserAttributes)); + assertFalse(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -156,7 +159,7 @@ public void userAttributeUnknownAttribute() throws Exception { @Test public void invalidMatchCondition() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "unknown_dimension", null, "chrome"); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -165,7 +168,7 @@ public void invalidMatchCondition() throws Exception { @Test public void invalidMatch() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "blah", "chrome"); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); logbackVerifier.expectMessage(Level.WARN, "Audience condition \"{name='browser_type', type='custom_attribute', match='blah', value='chrome'}\" uses an unknown match type. You may need to upgrade to a newer release of the Optimizely SDK"); } @@ -176,7 +179,7 @@ public void invalidMatch() throws Exception { @Test public void unexpectedAttributeType() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); logbackVerifier.expectMessage(Level.WARN, "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because a value of type \"java.lang.String\" was passed for user attribute \"browser_type\""); } @@ -187,7 +190,7 @@ public void unexpectedAttributeType() throws Exception { @Test public void unexpectedAttributeTypeNull() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); - assertNull(testInstance.evaluate(null, Collections.singletonMap("browser_type", null))); + assertNull(testInstance.evaluate(null, OTUtils.user(Collections.singletonMap("browser_type", null)))); logbackVerifier.expectMessage(Level.DEBUG, "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because a null value was passed for user attribute \"browser_type\""); } @@ -215,7 +218,7 @@ public void missingAttribute_returnsNullAndLogDebugMessage() throws Exception { for (Map.Entry entry : items.entrySet()) { for (Object value : entry.getValue()) { UserAttribute testInstance = new UserAttribute("n", "custom_attribute", entry.getKey(), value); - assertNull(testInstance.evaluate(null, Collections.EMPTY_MAP)); + assertNull(testInstance.evaluate(null, OTUtils.user(Collections.EMPTY_MAP))); String valueStr = (value instanceof String) ? ("'" + value + "'") : value.toString(); logbackVerifier.expectMessage(Level.DEBUG, "Audience condition \"{name='n', type='custom_attribute', match='" + entry.getKey() + "', value=" + valueStr + "}\" evaluated to UNKNOWN because no value was passed for user attribute \"n\""); @@ -226,10 +229,11 @@ public void missingAttribute_returnsNullAndLogDebugMessage() throws Exception { /** * Verify that UserAttribute.evaluate returns null on passing null attribute object. */ + @SuppressFBWarnings("NP_NULL_PARAM_DEREF_NONVIRTUAL") @Test public void nullAttribute() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "gt", 20); - assertNull(testInstance.evaluate(null, null)); + assertNull(testInstance.evaluate(null, OTUtils.user(null))); logbackVerifier.expectMessage(Level.DEBUG, "Audience condition \"{name='browser_type', type='custom_attribute', match='gt', value=20}\" evaluated to UNKNOWN because no value was passed for user attribute \"browser_type\""); } @@ -240,7 +244,7 @@ public void nullAttribute() throws Exception { @Test public void unknownConditionType() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "blah", "exists", "firefox"); - assertNull(testInstance.evaluate(null, testUserAttributes)); + assertNull(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); logbackVerifier.expectMessage(Level.WARN, "Audience condition \"{name='browser_type', type='blah', match='exists', value='firefox'}\" uses an unknown condition type. You may need to upgrade to a newer release of the Optimizely SDK."); } @@ -254,9 +258,9 @@ public void existsMatchConditionEmptyStringEvaluatesTrue() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "exists", "firefox"); Map attributes = new HashMap<>(); attributes.put("browser_type", ""); - assertTrue(testInstance.evaluate(null, attributes)); + assertTrue(testInstance.evaluate(null, OTUtils.user(attributes))); attributes.put("browser_type", null); - assertFalse(testInstance.evaluate(null, attributes)); + assertFalse(testInstance.evaluate(null, OTUtils.user(attributes))); } /** @@ -266,16 +270,16 @@ public void existsMatchConditionEmptyStringEvaluatesTrue() throws Exception { @Test public void existsMatchConditionEvaluatesTrue() throws Exception { UserAttribute testInstance = new UserAttribute("browser_type", "custom_attribute", "exists", "firefox"); - assertTrue(testInstance.evaluate(null, testUserAttributes)); + assertTrue(testInstance.evaluate(null, OTUtils.user(testUserAttributes))); UserAttribute testInstanceBoolean = new UserAttribute("is_firefox", "custom_attribute", "exists", false); UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "exists", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exists", 4.55); UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "exists", testUserAttributes); - assertTrue(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceObject.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceObject.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -286,8 +290,8 @@ public void existsMatchConditionEvaluatesTrue() throws Exception { public void existsMatchConditionEvaluatesFalse() throws Exception { UserAttribute testInstance = new UserAttribute("bad_var", "custom_attribute", "exists", "chrome"); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "exists", "chrome"); - assertFalse(testInstance.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstance.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertFalse(testInstanceNull.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -302,11 +306,11 @@ public void exactMatchConditionEvaluatesTrue() { UserAttribute testInstanceFloat = new UserAttribute("num_size", "custom_attribute", "exact", (float) 3); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exact", 3.55); - assertTrue(testInstanceString.evaluate(null, testUserAttributes)); - assertTrue(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 3))); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); + assertTrue(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceFloat.evaluate(null, OTUtils.user(Collections.singletonMap("num_size", (float) 3)))); + assertTrue(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -339,22 +343,22 @@ public void exactMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + OTUtils.user(Collections.singletonMap("num_size", bigInteger)))); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + OTUtils.user(Collections.singletonMap("num_size", invalidFloatValue)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", infiniteNANDouble))))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", largeDouble))))); } /** @@ -372,10 +376,10 @@ public void invalidExactMatchConditionEvaluatesNull() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "exact", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "exact", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstancePositiveInfinite.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNANDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -389,10 +393,10 @@ public void exactMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "exact", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exact", 5.55); - assertFalse(testInstanceString.evaluate(null, testUserAttributes)); - assertFalse(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); + assertFalse(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertFalse(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertFalse(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -408,15 +412,15 @@ public void exactMatchConditionEvaluatesNull() { UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "exact", "3.55"); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "exact", "null_val"); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceObject.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); + assertNull(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNull.evaluate(null, OTUtils.user(testTypedUserAttributes))); Map attr = new HashMap<>(); attr.put("browser_type", "true"); - assertNull(testInstanceString.evaluate(null, attr)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(attr))); } /** @@ -430,13 +434,13 @@ public void gtMatchConditionEvaluatesTrue() { UserAttribute testInstanceFloat = new UserAttribute("num_size", "custom_attribute", "gt", (float) 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "gt", 2.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 3))); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceFloat.evaluate(null, OTUtils.user(Collections.singletonMap("num_size", (float) 3)))); + assertTrue(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); Map badAttributes = new HashMap<>(); badAttributes.put("num_size", "bobs burgers"); - assertNull(testInstanceInteger.evaluate(null, badAttributes)); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(badAttributes))); } /** @@ -470,22 +474,22 @@ public void gtMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + OTUtils.user(Collections.singletonMap("num_size", bigInteger)))); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + OTUtils.user(Collections.singletonMap("num_size", invalidFloatValue)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", infiniteNANDouble))))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", largeDouble))))); } /** @@ -503,10 +507,10 @@ public void gtMatchConditionEvaluatesNullWithInvalidAttr() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "gt", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "gt", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstancePositiveInfinite.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNANDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -519,8 +523,8 @@ public void gtMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "gt", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "gt", 5.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertFalse(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -534,10 +538,10 @@ public void gtMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "gt", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "gt", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); + assertNull(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceObject.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNull.evaluate(null, OTUtils.user(testTypedUserAttributes))); } @@ -552,13 +556,13 @@ public void geMatchConditionEvaluatesTrue() { UserAttribute testInstanceFloat = new UserAttribute("num_size", "custom_attribute", "ge", (float) 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "ge", 2.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceFloat.evaluate(null, Collections.singletonMap("num_size", (float) 2))); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceFloat.evaluate(null, OTUtils.user(Collections.singletonMap("num_size", (float) 2)))); + assertTrue(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); Map badAttributes = new HashMap<>(); badAttributes.put("num_size", "bobs burgers"); - assertNull(testInstanceInteger.evaluate(null, badAttributes)); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(badAttributes))); } /** @@ -592,22 +596,22 @@ public void geMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + OTUtils.user(Collections.singletonMap("num_size", bigInteger)))); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + OTUtils.user(Collections.singletonMap("num_size", invalidFloatValue)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", infiniteNANDouble))))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", largeDouble))))); } /** @@ -625,10 +629,10 @@ public void geMatchConditionEvaluatesNullWithInvalidAttr() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "ge", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "ge", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstancePositiveInfinite.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNANDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -641,8 +645,8 @@ public void geMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "ge", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "ge", 5.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertFalse(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -656,10 +660,10 @@ public void geMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "ge", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "ge", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); + assertNull(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceObject.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNull.evaluate(null, OTUtils.user(testTypedUserAttributes))); } @@ -673,8 +677,8 @@ public void ltMatchConditionEvaluatesTrue() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "lt", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "lt", 5.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertTrue(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -687,8 +691,8 @@ public void ltMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "lt", 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "lt", 2.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertFalse(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -702,10 +706,10 @@ public void ltMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "lt", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "lt", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); + assertNull(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceObject.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNull.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -739,22 +743,22 @@ public void ltMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + OTUtils.user(Collections.singletonMap("num_size", bigInteger)))); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + OTUtils.user(Collections.singletonMap("num_size", invalidFloatValue)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", infiniteNANDouble))))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", largeDouble))))); } /** @@ -772,10 +776,10 @@ public void ltMatchConditionEvaluatesNullWithInvalidAttributes() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "lt", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "lt", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstancePositiveInfinite.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNANDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } @@ -789,8 +793,8 @@ public void leMatchConditionEvaluatesTrue() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "le", 5); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "le", 5.55); - assertTrue(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertTrue(testInstanceDouble.evaluate(null, Collections.singletonMap("num_counts", 5.55))); + assertTrue(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertTrue(testInstanceDouble.evaluate(null, OTUtils.user(Collections.singletonMap("num_counts", 5.55)))); } /** @@ -803,8 +807,8 @@ public void leMatchConditionEvaluatesFalse() { UserAttribute testInstanceInteger = new UserAttribute("num_size", "custom_attribute", "le", 2); UserAttribute testInstanceDouble = new UserAttribute("num_counts", "custom_attribute", "le", 2.55); - assertFalse(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertFalse(testInstanceDouble.evaluate(null, testTypedUserAttributes)); + assertFalse(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertFalse(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -818,10 +822,10 @@ public void leMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "le", 3.5); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "le", 3.5); - assertNull(testInstanceString.evaluate(null, testUserAttributes)); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); + assertNull(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceObject.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNull.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -855,22 +859,22 @@ public void leMatchConditionEvaluatesNullWithInvalidUserAttr() { assertNull(testInstanceInteger.evaluate( null, - Collections.singletonMap("num_size", bigInteger))); + OTUtils.user(Collections.singletonMap("num_size", bigInteger)))); assertNull(testInstanceFloat.evaluate( null, - Collections.singletonMap("num_size", invalidFloatValue))); + OTUtils.user(Collections.singletonMap("num_size", invalidFloatValue)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infinitePositiveInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( null, - Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble))); + OTUtils.user(Collections.singletonMap("num_counts", infiniteNegativeInfiniteDouble)))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", infiniteNANDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", infiniteNANDouble))))); assertNull(testInstanceDouble.evaluate( - null, Collections.singletonMap("num_counts", - Collections.singletonMap("num_counts", largeDouble)))); + null, OTUtils.user(Collections.singletonMap("num_counts", + Collections.singletonMap("num_counts", largeDouble))))); } /** @@ -888,10 +892,10 @@ public void leMatchConditionEvaluatesNullWithInvalidAttributes() { UserAttribute testInstanceNegativeInfiniteDouble = new UserAttribute("num_counts", "custom_attribute", "le", infiniteNegativeInfiniteDouble); UserAttribute testInstanceNANDouble = new UserAttribute("num_counts", "custom_attribute", "le", infiniteNANDouble); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstancePositiveInfinite.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNANDouble.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstancePositiveInfinite.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNegativeInfiniteDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNANDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); } /** @@ -901,7 +905,7 @@ public void leMatchConditionEvaluatesNullWithInvalidAttributes() { @Test public void substringMatchConditionEvaluatesTrue() { UserAttribute testInstanceString = new UserAttribute("browser_type", "custom_attribute", "substring", "chrome"); - assertTrue(testInstanceString.evaluate(null, testUserAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -911,7 +915,7 @@ public void substringMatchConditionEvaluatesTrue() { @Test public void substringMatchConditionPartialMatchEvaluatesTrue() { UserAttribute testInstanceString = new UserAttribute("browser_type", "custom_attribute", "substring", "chro"); - assertTrue(testInstanceString.evaluate(null, testUserAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -921,7 +925,7 @@ public void substringMatchConditionPartialMatchEvaluatesTrue() { @Test public void substringMatchConditionEvaluatesFalse() { UserAttribute testInstanceString = new UserAttribute("browser_type", "custom_attribute", "substring", "chr0me"); - assertFalse(testInstanceString.evaluate(null, testUserAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -936,11 +940,11 @@ public void substringMatchConditionEvaluatesNull() { UserAttribute testInstanceObject = new UserAttribute("meta_data", "custom_attribute", "substring", "chrome1"); UserAttribute testInstanceNull = new UserAttribute("null_val", "custom_attribute", "substring", "chrome1"); - assertNull(testInstanceBoolean.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceInteger.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceDouble.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceObject.evaluate(null, testTypedUserAttributes)); - assertNull(testInstanceNull.evaluate(null, testTypedUserAttributes)); + assertNull(testInstanceBoolean.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceInteger.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceDouble.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceObject.evaluate(null, OTUtils.user(testTypedUserAttributes))); + assertNull(testInstanceNull.evaluate(null, OTUtils.user(testTypedUserAttributes))); } //======== Semantic version evaluation tests ========// @@ -951,7 +955,7 @@ public void testSemanticVersionEqualsMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2.0); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } @Test @@ -959,7 +963,7 @@ public void semanticVersionInvalidMajorShouldBeNumberOnly() { Map testAttributes = new HashMap(); testAttributes.put("version", "a.1.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } @Test @@ -967,7 +971,7 @@ public void semanticVersionInvalidMinorShouldBeNumberOnly() { Map testAttributes = new HashMap(); testAttributes.put("version", "1.b.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } @Test @@ -975,7 +979,7 @@ public void semanticVersionInvalidPatchShouldBeNumberOnly() { Map testAttributes = new HashMap(); testAttributes.put("version", "1.2.c"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test SemanticVersionEqualsMatch returns null if given invalid UserCondition Variable type @@ -984,7 +988,7 @@ public void testSemanticVersionEqualsMatchInvalidUserConditionVariable() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.0"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", 2.0); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test SemanticVersionGTMatch returns null if given invalid value type @@ -993,7 +997,7 @@ public void testSemanticVersionGTMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", false); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test SemanticVersionGEMatch returns null if given invalid value type @@ -1002,7 +1006,7 @@ public void testSemanticVersionGEMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test SemanticVersionLTMatch returns null if given invalid value type @@ -1011,7 +1015,7 @@ public void testSemanticVersionLTMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test SemanticVersionLEMatch returns null if given invalid value type @@ -1020,7 +1024,7 @@ public void testSemanticVersionLEMatchInvalidInput() { Map testAttributes = new HashMap(); testAttributes.put("version", 2); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.0.0"); - assertNull(testInstanceString.evaluate(null, testAttributes)); + assertNull(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test if not same when targetVersion is only major.minor.patch and version is major.minor @@ -1029,7 +1033,7 @@ public void testIsSemanticNotSameConditionValueMajorMinorPatch() { Map testAttributes = new HashMap(); testAttributes.put("version", "1.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "1.2.0"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test if same when target is only major but user condition checks only major.minor,patch @@ -1038,7 +1042,7 @@ public void testIsSemanticSameSingleDigit() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.0.0"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "3"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test if greater when User value patch is greater even when its beta @@ -1047,7 +1051,7 @@ public void testIsSemanticGreaterWhenUserConditionComparesMajorMinorAndPatchVers Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "3.1.0"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test if greater when preRelease is greater alphabetically @@ -1056,7 +1060,7 @@ public void testIsSemanticGreaterWhenMajorMinorPatchReleaseVersionCharacter() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta.y.1+1.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "3.1.1-beta.x.1+1.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test if greater when preRelease version number is greater @@ -1065,7 +1069,7 @@ public void testIsSemanticGreaterWhenMajorMinorPatchPreReleaseVersionNum() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta.x.2+1.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "3.1.1-beta.x.1+1.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test if equals semantic version even when only same preRelease is passed in user attribute and no build meta @@ -1074,7 +1078,7 @@ public void testIsSemanticEqualWhenMajorMinorPatchPreReleaseVersionNum() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.1.1-beta.x.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "3.1.1-beta.x.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test if not same @@ -1083,7 +1087,7 @@ public void testIsSemanticNotSameReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.2"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.1.1"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test when target is full semantic version major.minor.patch @@ -1092,7 +1096,7 @@ public void testIsSemanticSameFull() { Map testAttributes = new HashMap(); testAttributes.put("version", "3.0.1"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "3.0.1"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare less when user condition checks only major.minor @@ -1101,7 +1105,7 @@ public void testIsSemanticLess() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.6"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.2"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // When user condition checks major.minor but target is major.minor.patch then its equals @@ -1110,7 +1114,7 @@ public void testIsSemanticLessFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.0"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.1"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare less when target is full major.minor.patch @@ -1119,7 +1123,7 @@ public void testIsSemanticFullLess() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.6"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_lt", "2.1.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare greater when user condition checks only major.minor @@ -1128,7 +1132,7 @@ public void testIsSemanticMore() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.3.6"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.2"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare greater when both are major.minor.patch-beta but target is greater than user condition @@ -1137,7 +1141,7 @@ public void testIsSemanticMoreWhenBeta() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.3.6-beta"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.3.5-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare greater when target is major.minor.patch @@ -1146,7 +1150,7 @@ public void testIsSemanticFullMore() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.7"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.6"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare greater when target is major.minor.patch is smaller then it returns false @@ -1155,7 +1159,7 @@ public void testSemanticVersionGTFullMoreReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.10"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare equal when both are exactly same - major.minor.patch-beta @@ -1164,7 +1168,7 @@ public void testIsSemanticFullEqual() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9-beta"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_eq", "2.1.9-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare equal when both major.minor.patch is same, but due to beta user condition is smaller @@ -1173,7 +1177,7 @@ public void testIsSemanticLessWhenBeta() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.9-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare greater when target is major.minor.patch-beta and user condition only compares major.minor.patch @@ -1182,7 +1186,7 @@ public void testIsSemanticGreaterBeta() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_gt", "2.1.9-beta"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare equal when target is major.minor.patch @@ -1191,7 +1195,7 @@ public void testIsSemanticLessEqualsWhenEqualsReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.1.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare less when target is major.minor.patch @@ -1200,7 +1204,7 @@ public void testIsSemanticLessEqualsWhenLessReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.132.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.233.91"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare less when target is major.minor.patch @@ -1209,7 +1213,7 @@ public void testIsSemanticLessEqualsWhenGreaterReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.233.91"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_le", "2.132.009"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare equal when target is major.minor.patch @@ -1218,7 +1222,7 @@ public void testIsSemanticGreaterEqualsWhenEqualsReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.1.9"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.1.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare less when target is major.minor.patch @@ -1227,7 +1231,7 @@ public void testIsSemanticGreaterEqualsWhenLessReturnsTrue() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.233.91"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.132.9"); - assertTrue(testInstanceString.evaluate(null, testAttributes)); + assertTrue(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } // Test compare less when target is major.minor.patch @@ -1236,7 +1240,7 @@ public void testIsSemanticGreaterEqualsWhenLessReturnsFalse() { Map testAttributes = new HashMap(); testAttributes.put("version", "2.132.009"); UserAttribute testInstanceString = new UserAttribute("version", "custom_attribute", "semver_ge", "2.233.91"); - assertFalse(testInstanceString.evaluate(null, testAttributes)); + assertFalse(testInstanceString.evaluate(null, OTUtils.user(testAttributes))); } /** @@ -1245,7 +1249,7 @@ public void testIsSemanticGreaterEqualsWhenLessReturnsFalse() { @Test public void notConditionEvaluateNull() { NotCondition notCondition = new NotCondition(new NullCondition()); - assertNull(notCondition.evaluate(null, testUserAttributes)); + assertNull(notCondition.evaluate(null, OTUtils.user(testUserAttributes))); } /** @@ -1253,12 +1257,13 @@ public void notConditionEvaluateNull() { */ @Test public void notConditionEvaluateTrue() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); UserAttribute userAttribute = mock(UserAttribute.class); - when(userAttribute.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute.evaluate(null, user)).thenReturn(false); NotCondition notCondition = new NotCondition(userAttribute); - assertTrue(notCondition.evaluate(null, testUserAttributes)); - verify(userAttribute, times(1)).evaluate(null, testUserAttributes); + assertTrue(notCondition.evaluate(null, user)); + verify(userAttribute, times(1)).evaluate(null, user); } /** @@ -1266,12 +1271,13 @@ public void notConditionEvaluateTrue() { */ @Test public void notConditionEvaluateFalse() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); UserAttribute userAttribute = mock(UserAttribute.class); - when(userAttribute.evaluate(null, testUserAttributes)).thenReturn(true); + when(userAttribute.evaluate(null, user)).thenReturn(true); NotCondition notCondition = new NotCondition(userAttribute); - assertFalse(notCondition.evaluate(null, testUserAttributes)); - verify(userAttribute, times(1)).evaluate(null, testUserAttributes); + assertFalse(notCondition.evaluate(null, user)); + verify(userAttribute, times(1)).evaluate(null, user); } /** @@ -1279,21 +1285,22 @@ public void notConditionEvaluateFalse() { */ @Test public void orConditionEvaluateTrue() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(true); + when(userAttribute1.evaluate(null, user)).thenReturn(true); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, user)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertTrue(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertTrue(orCondition.evaluate(null, user)); + verify(userAttribute1, times(1)).evaluate(null, user); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(0)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(0)).evaluate(null, user); } /** @@ -1301,21 +1308,22 @@ public void orConditionEvaluateTrue() { */ @Test public void orConditionEvaluateTrueWithNullAndTrue() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(null); + when(userAttribute1.evaluate(null, user)).thenReturn(null); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(true); + when(userAttribute2.evaluate(null, user)).thenReturn(true); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertTrue(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertTrue(orCondition.evaluate(null, user)); + verify(userAttribute1, times(1)).evaluate(null, user); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(1)).evaluate(null, user); } /** @@ -1323,21 +1331,22 @@ public void orConditionEvaluateTrueWithNullAndTrue() { */ @Test public void orConditionEvaluateNullWithNullAndFalse() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(null); + when(userAttribute1.evaluate(null, user)).thenReturn(null); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, user)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertNull(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertNull(orCondition.evaluate(null, user)); + verify(userAttribute1, times(1)).evaluate(null, user); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(1)).evaluate(null, user); } /** @@ -1345,21 +1354,22 @@ public void orConditionEvaluateNullWithNullAndFalse() { */ @Test public void orConditionEvaluateFalseWithFalseAndFalse() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute1.evaluate(null, user)).thenReturn(false); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, user)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertFalse(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); + assertFalse(orCondition.evaluate(null, user)); + verify(userAttribute1, times(1)).evaluate(null, user); // shouldn't be called due to short-circuiting in 'Or' evaluation - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + verify(userAttribute2, times(1)).evaluate(null, user); } /** @@ -1367,20 +1377,21 @@ public void orConditionEvaluateFalseWithFalseAndFalse() { */ @Test public void orConditionEvaluateFalse() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); UserAttribute userAttribute1 = mock(UserAttribute.class); - when(userAttribute1.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute1.evaluate(null, user)).thenReturn(false); UserAttribute userAttribute2 = mock(UserAttribute.class); - when(userAttribute2.evaluate(null, testUserAttributes)).thenReturn(false); + when(userAttribute2.evaluate(null, user)).thenReturn(false); List conditions = new ArrayList(); conditions.add(userAttribute1); conditions.add(userAttribute2); OrCondition orCondition = new OrCondition(conditions); - assertFalse(orCondition.evaluate(null, testUserAttributes)); - verify(userAttribute1, times(1)).evaluate(null, testUserAttributes); - verify(userAttribute2, times(1)).evaluate(null, testUserAttributes); + assertFalse(orCondition.evaluate(null, user)); + verify(userAttribute1, times(1)).evaluate(null, user); + verify(userAttribute2, times(1)).evaluate(null, user); } /** @@ -1388,20 +1399,21 @@ public void orConditionEvaluateFalse() { */ @Test public void andConditionEvaluateTrue() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition1.evaluate(null, user)).thenReturn(true); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition2.evaluate(null, user)).thenReturn(true); List conditions = new ArrayList(); conditions.add(orCondition1); conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertTrue(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); - verify(orCondition2, times(1)).evaluate(null, testUserAttributes); + assertTrue(andCondition.evaluate(null, user)); + verify(orCondition1, times(1)).evaluate(null, user); + verify(orCondition2, times(1)).evaluate(null, user); } /** @@ -1409,20 +1421,21 @@ public void andConditionEvaluateTrue() { */ @Test public void andConditionEvaluateFalseWithNullAndFalse() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(null); + when(orCondition1.evaluate(null, user)).thenReturn(null); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(false); + when(orCondition2.evaluate(null, user)).thenReturn(false); List conditions = new ArrayList(); conditions.add(orCondition1); conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertFalse(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); - verify(orCondition2, times(1)).evaluate(null, testUserAttributes); + assertFalse(andCondition.evaluate(null, user)); + verify(orCondition1, times(1)).evaluate(null, user); + verify(orCondition2, times(1)).evaluate(null, user); } /** @@ -1430,20 +1443,21 @@ public void andConditionEvaluateFalseWithNullAndFalse() { */ @Test public void andConditionEvaluateNullWithNullAndTrue() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(null); + when(orCondition1.evaluate(null, user)).thenReturn(null); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition2.evaluate(null, user)).thenReturn(true); List conditions = new ArrayList(); conditions.add(orCondition1); conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertNull(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); - verify(orCondition2, times(1)).evaluate(null, testUserAttributes); + assertNull(andCondition.evaluate(null, user)); + verify(orCondition1, times(1)).evaluate(null, user); + verify(orCondition2, times(1)).evaluate(null, user); } /** @@ -1451,11 +1465,12 @@ public void andConditionEvaluateNullWithNullAndTrue() { */ @Test public void andConditionEvaluateFalse() { + OptimizelyUserContext user = OTUtils.user(testUserAttributes); OrCondition orCondition1 = mock(OrCondition.class); - when(orCondition1.evaluate(null, testUserAttributes)).thenReturn(false); + when(orCondition1.evaluate(null, user)).thenReturn(false); OrCondition orCondition2 = mock(OrCondition.class); - when(orCondition2.evaluate(null, testUserAttributes)).thenReturn(true); + when(orCondition2.evaluate(null, user)).thenReturn(true); // and[false, true] List conditions = new ArrayList(); @@ -1463,13 +1478,13 @@ public void andConditionEvaluateFalse() { conditions.add(orCondition2); AndCondition andCondition = new AndCondition(conditions); - assertFalse(andCondition.evaluate(null, testUserAttributes)); - verify(orCondition1, times(1)).evaluate(null, testUserAttributes); + assertFalse(andCondition.evaluate(null, user)); + verify(orCondition1, times(1)).evaluate(null, user); // shouldn't be called due to short-circuiting in 'And' evaluation - verify(orCondition2, times(0)).evaluate(null, testUserAttributes); + verify(orCondition2, times(0)).evaluate(null, user); OrCondition orCondition3 = mock(OrCondition.class); - when(orCondition3.evaluate(null, testUserAttributes)).thenReturn(null); + when(orCondition3.evaluate(null, user)).thenReturn(null); // and[null, false] List conditions2 = new ArrayList(); @@ -1477,7 +1492,7 @@ public void andConditionEvaluateFalse() { conditions2.add(orCondition1); AndCondition andCondition2 = new AndCondition(conditions2); - assertFalse(andCondition2.evaluate(null, testUserAttributes)); + assertFalse(andCondition2.evaluate(null, user)); // and[true, false, null] List conditions3 = new ArrayList(); @@ -1486,7 +1501,310 @@ public void andConditionEvaluateFalse() { conditions3.add(orCondition1); AndCondition andCondition3 = new AndCondition(conditions3); - assertFalse(andCondition3.evaluate(null, testUserAttributes)); + assertFalse(andCondition3.evaluate(null, user)); + } + + /** + * Verify that with odp segment evaluator single ODP audience evaluates true + */ + @Test + public void singleODPAudienceEvaluateTrueIfSegmentExist() throws Exception { + + OptimizelyUserContext mockedUser = OTUtils.user(); + + UserAttribute testInstanceSingleAudience = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + List userConditions = new ArrayList<>(); + userConditions.add(testInstanceSingleAudience); + AndCondition andCondition = new AndCondition(userConditions); + + // Should evaluate true if qualified segment exist + Whitebox.setInternalState(mockedUser, "qualifiedSegments", Collections.singletonList("odp-segment-1")); + + assertTrue(andCondition.evaluate(null, mockedUser)); + } + + /** + * Verify that with odp segment evaluator single ODP audience evaluates false + */ + @Test + public void singleODPAudienceEvaluateFalseIfSegmentNotExist() throws Exception { + + OptimizelyUserContext mockedUser = OTUtils.user(); + + UserAttribute testInstanceSingleAudience = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + List userConditions = new ArrayList<>(); + userConditions.add(testInstanceSingleAudience); + AndCondition andCondition = new AndCondition(userConditions); + + // Should evaluate false if qualified segment does not exist + Whitebox.setInternalState(mockedUser, "qualifiedSegments", Collections.singletonList("odp-segment-2")); + + assertFalse(andCondition.evaluate(null, mockedUser)); + } + + /** + * Verify that with odp segment evaluator single ODP audience evaluates false when segments not provided + */ + @Test + public void singleODPAudienceEvaluateFalseIfSegmentNotProvided() throws Exception { + OptimizelyUserContext mockedUser = OTUtils.user(); + + UserAttribute testInstanceSingleAudience = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + List userConditions = new ArrayList<>(); + userConditions.add(testInstanceSingleAudience); + AndCondition andCondition = new AndCondition(userConditions); + + // Should evaluate false if qualified segment does not exist + Whitebox.setInternalState(mockedUser, "qualifiedSegments", Collections.emptyList()); + + assertFalse(andCondition.evaluate(null, mockedUser)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience true when segment provided exist + */ + @Test + public void singleODPAudienceEvaluateMultipleOdpConditions() { + OptimizelyUserContext mockedUser = OTUtils.user(); + + Condition andCondition = createMultipleConditionAudienceAndOrODP(); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(andCondition.evaluate(null, mockedUser)); + + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-4"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(andCondition.evaluate(null, mockedUser)); + + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(andCondition.evaluate(null, mockedUser)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience true when segment provided exist + */ + @Test + public void singleODPAudienceEvaluateMultipleOdpConditionsEvaluateFalse() { + OptimizelyUserContext mockedUser = OTUtils.user(); + + Condition andCondition = createMultipleConditionAudienceAndOrODP(); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertFalse(andCondition.evaluate(null, mockedUser)); + + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertFalse(andCondition.evaluate(null, mockedUser)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience with multiple conditions true or false when segment conditions meet + */ + @Test + public void multipleAudienceEvaluateMultipleOdpConditionsEvaluate() { + OptimizelyUserContext mockedUser = OTUtils.user(); + + // ["and", "1", "2"] + List audience1And2 = new ArrayList<>(); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1")); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-2")); + AndCondition audienceCondition1 = new AndCondition(audience1And2); + + // ["and", "3", "4"] + List audience3And4 = new ArrayList<>(); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-3")); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-4")); + AndCondition audienceCondition2 = new AndCondition(audience3And4); + + // ["or", "5", "6"] + List audience5And6 = new ArrayList<>(); + audience5And6.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-5")); + audience5And6.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-6")); + OrCondition audienceCondition3 = new OrCondition(audience5And6); + + + //Scenario 1- ['or', '1', '2', '3'] + List conditions = new ArrayList<>(); + conditions.add(audienceCondition1); + conditions.add(audienceCondition2); + conditions.add(audienceCondition3); + + OrCondition implicitOr = new OrCondition(conditions); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(implicitOr.evaluate(null, mockedUser)); + + + //Scenario 2- ['and', '1', '2', '3'] + AndCondition implicitAnd = new AndCondition(conditions); + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertFalse(implicitAnd.evaluate(null, mockedUser)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + qualifiedSegments.add("odp-segment-6"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(implicitAnd.evaluate(null, mockedUser)); + + + ////Scenario 3- ['and', '1', '2',['not', '3']] + conditions = new ArrayList<>(); + conditions.add(audienceCondition1); + conditions.add(audienceCondition2); + conditions.add(new NotCondition(audienceCondition3)); + implicitAnd = new AndCondition(conditions); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(implicitAnd.evaluate(null, mockedUser)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + qualifiedSegments.add("odp-segment-5"); + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertFalse(implicitAnd.evaluate(null, mockedUser)); + } + + /** + * Verify that with odp segment evaluator evaluates multiple ODP audience with multiple type of evaluators + */ + @Test + public void multipleAudienceEvaluateMultipleOdpConditionsEvaluateWithMultipleTypeOfEvaluator() { + OptimizelyUserContext mockedUser = OTUtils.user(); + + // ["and", "1", "2"] + List audience1And2 = new ArrayList<>(); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1")); + audience1And2.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-2")); + AndCondition audienceCondition1 = new AndCondition(audience1And2); + + // ["and", "3", "4"] + List audience3And4 = new ArrayList<>(); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-3")); + audience3And4.add(new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-4")); + AndCondition audienceCondition2 = new AndCondition(audience3And4); + + // ["or", "chrome", "safari"] + List chromeUserAudience = new ArrayList<>(); + chromeUserAudience.add(new UserAttribute("browser_type", "custom_attribute", "exact", "chrome")); + chromeUserAudience.add(new UserAttribute("browser_type", "custom_attribute", "exact", "safari")); + OrCondition audienceCondition3 = new OrCondition(chromeUserAudience); + + + //Scenario 1- ['or', '1', '2', '3'] + List conditions = new ArrayList<>(); + conditions.add(audienceCondition1); + conditions.add(audienceCondition2); + conditions.add(audienceCondition3); + + OrCondition implicitOr = new OrCondition(conditions); + // Should evaluate correctly based on the given segments + List qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(implicitOr.evaluate(null, mockedUser)); + + + //Scenario 2- ['and', '1', '2', '3'] + AndCondition implicitAnd = new AndCondition(conditions); + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + + mockedUser = OTUtils.user(Collections.singletonMap("browser_type", "chrome")); + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertFalse(implicitAnd.evaluate(null, mockedUser)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + mockedUser = OTUtils.user(Collections.singletonMap("browser_type", "chrome")); + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertTrue(implicitAnd.evaluate(null, mockedUser)); + + // Should evaluate correctly based on the given segments + qualifiedSegments = new ArrayList<>(); + qualifiedSegments.add("odp-segment-1"); + qualifiedSegments.add("odp-segment-2"); + qualifiedSegments.add("odp-segment-3"); + qualifiedSegments.add("odp-segment-4"); + + mockedUser = OTUtils.user(Collections.singletonMap("browser_type", "not_chrome")); + Whitebox.setInternalState(mockedUser, "qualifiedSegments", qualifiedSegments); + assertFalse(implicitAnd.evaluate(null, mockedUser)); + } + + public Condition createMultipleConditionAudienceAndOrODP() { + UserAttribute testInstanceSingleAudience1 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-1"); + UserAttribute testInstanceSingleAudience2 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-2"); + UserAttribute testInstanceSingleAudience3 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-3"); + UserAttribute testInstanceSingleAudience4 = new UserAttribute("odp.audiences", "third_party_dimension", "qualified", "odp-segment-4"); + + List userConditionsOR = new ArrayList<>(); + userConditionsOR.add(testInstanceSingleAudience3); + userConditionsOR.add(testInstanceSingleAudience4); + OrCondition orCondition = new OrCondition(userConditionsOR); + List userConditionsAnd = new ArrayList<>(); + userConditionsAnd.add(testInstanceSingleAudience1); + userConditionsAnd.add(testInstanceSingleAudience2); + userConditionsAnd.add(orCondition); + AndCondition andCondition = new AndCondition(userConditionsAnd); + + return andCondition; } /** @@ -1498,7 +1816,7 @@ public void andConditionEvaluateFalse() { // } /** - * Verify that {@link Condition#evaluate(com.optimizely.ab.config.ProjectConfig, java.util.Map)} + * Verify that {@link Condition#evaluate(com.optimizely.ab.config.ProjectConfig, com.optimizely.ab.OptimizelyUserContext)} * called when its attribute value is null * returns True when the user's attribute value is also null * True when the attribute is not in the map @@ -1518,8 +1836,8 @@ public void nullValueEvaluate() { attributeValue ); - assertNull(nullValueAttribute.evaluate(null, Collections.emptyMap())); - assertNull(nullValueAttribute.evaluate(null, Collections.singletonMap(attributeName, attributeValue))); - assertNull(nullValueAttribute.evaluate(null, (Collections.singletonMap(attributeName, "")))); + assertNull(nullValueAttribute.evaluate(null, OTUtils.user(Collections.emptyMap()))); + assertNull(nullValueAttribute.evaluate(null, OTUtils.user(Collections.singletonMap(attributeName, attributeValue)))); + assertNull(nullValueAttribute.evaluate(null, OTUtils.user((Collections.singletonMap(attributeName, ""))))); } } diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java index dfc130f21..c43f29599 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/DefaultConfigParserTest.java @@ -20,6 +20,7 @@ import com.optimizely.ab.internal.PropertyUtils; import org.hamcrest.CoreMatchers; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java index 7cf4610ca..ea0d9cac8 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/GsonConfigParserTest.java @@ -29,6 +29,7 @@ import com.optimizely.ab.config.audience.TypedAudience; import com.optimizely.ab.internal.InvalidAudienceCondition; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -312,6 +313,69 @@ public void nullJsonExceptionWrapping() throws Exception { parser.parseProjectConfig(null); } + @Test + public void integrationsArrayAbsent() throws Exception { + GsonConfigParser parser = new GsonConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(nullFeatureEnabledConfigJsonV4()); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasODP() throws Exception { + GsonConfigParser parser = new GsonConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4()); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherIntegration() throws Exception { + GsonConfigParser parser = new GsonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"not-odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasMissingHost() throws Exception { + GsonConfigParser parser = new GsonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getHostForODP(), null); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherKeys() throws Exception { + GsonConfigParser parser = new GsonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\", " + + "\"new-key\": \"new-value\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + @Test public void testToJson() { Map map = new HashMap<>(); diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java index 2e5dcb672..733ae49a5 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JacksonConfigParserTest.java @@ -26,6 +26,7 @@ import com.optimizely.ab.config.audience.TypedAudience; import com.optimizely.ab.internal.InvalidAudienceCondition; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -304,6 +305,69 @@ public void nullJsonExceptionWrapping() throws Exception { parser.parseProjectConfig(null); } + @Test + public void integrationsArrayAbsent() throws Exception { + JacksonConfigParser parser = new JacksonConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(nullFeatureEnabledConfigJsonV4()); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasODP() throws Exception { + JacksonConfigParser parser = new JacksonConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4()); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherIntegration() throws Exception { + JacksonConfigParser parser = new JacksonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"not-odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasMissingHost() throws Exception { + JacksonConfigParser parser = new JacksonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getHostForODP(), null); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherKeys() throws Exception { + JacksonConfigParser parser = new JacksonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\", " + + "\"new-key\": \"new-value\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + @Test public void testToJson() { Map map = new HashMap<>(); diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java index d78f57e75..844d7448b 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonConfigParserTest.java @@ -27,6 +27,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.json.JSONArray; import org.json.JSONObject; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -255,6 +256,68 @@ public void nullJsonExceptionWrapping() throws Exception { parser.parseProjectConfig(null); } + @Test + public void integrationsArrayAbsent() throws Exception { + JsonConfigParser parser = new JsonConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(nullFeatureEnabledConfigJsonV4()); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasODP() throws Exception { + JsonConfigParser parser = new JsonConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4()); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherIntegration() throws Exception { + JsonConfigParser parser = new JsonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"not-odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasMissingHost() throws Exception { + JsonConfigParser parser = new JsonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getHostForODP(), null); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherKeys() throws Exception { + JsonConfigParser parser = new JsonConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\", " + + "\"new-key\": \"new-value\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } @Test public void testToJson() { Map map = new HashMap<>(); diff --git a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java index 6c5dca1eb..1844fa967 100644 --- a/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/config/parser/JsonSimpleConfigParserTest.java @@ -27,6 +27,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.json.JSONArray; import org.json.JSONObject; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -254,6 +255,69 @@ public void nullJsonExceptionWrapping() throws Exception { parser.parseProjectConfig(null); } + @Test + public void integrationsArrayAbsent() throws Exception { + JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(nullFeatureEnabledConfigJsonV4()); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasODP() throws Exception { + JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); + ProjectConfig actual = parser.parseProjectConfig(validConfigJsonV4()); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherIntegration() throws Exception { + JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"not-odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), ""); + assertEquals(actual.getPublicKeyForODP(), ""); + } + + @Test + public void integrationsArrayHasMissingHost() throws Exception { + JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"publicKey\": \"test-key\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getHostForODP(), null); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + + @Test + public void integrationsArrayHasOtherKeys() throws Exception { + JsonSimpleConfigParser parser = new JsonSimpleConfigParser(); + String integrationsObject = ", \"integrations\": [" + + "{ \"key\": \"odp\", " + + "\"host\": \"https://example.com\", " + + "\"publicKey\": \"test-key\", " + + "\"new-key\": \"new-value\" }" + + "]}"; + String datafile = nullFeatureEnabledConfigJsonV4(); + datafile = datafile.substring(0, datafile.lastIndexOf("}")) + integrationsObject; + ProjectConfig actual = parser.parseProjectConfig(datafile); + assertEquals(actual.getIntegrations().size(), 1); + assertEquals(actual.getHostForODP(), "https://example.com"); + assertEquals(actual.getPublicKeyForODP(), "test-key"); + } + @Test public void testToJson() { Map map = new HashMap<>(); diff --git a/core-api/src/test/java/com/optimizely/ab/internal/ExperimentUtilsTest.java b/core-api/src/test/java/com/optimizely/ab/internal/ExperimentUtilsTest.java index fd1529aaf..d7965ccac 100644 --- a/core-api/src/test/java/com/optimizely/ab/internal/ExperimentUtilsTest.java +++ b/core-api/src/test/java/com/optimizely/ab/internal/ExperimentUtilsTest.java @@ -1,12 +1,12 @@ /** * - * Copyright 2017, 2019-2020, Optimizely and contributors + * Copyright 2017, 2019-2020, 2022, Optimizely and contributors * * 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -25,6 +25,7 @@ import com.optimizely.ab.config.audience.Audience; import com.optimizely.ab.config.audience.Condition; import com.optimizely.ab.config.audience.TypedAudience; +import com.optimizely.ab.testutils.OTUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.junit.BeforeClass; import org.junit.Rule; @@ -128,7 +129,7 @@ public void isExperimentActiveReturnsFalseWhenTheExperimentIsNotStarted() { @Test public void doesUserMeetAudienceConditionsReturnsTrueIfExperimentHasNoAudiences() { Experiment experiment = noAudienceProjectConfig.getExperiments().get(0); - assertTrue(doesUserMeetAudienceConditions(noAudienceProjectConfig, experiment, Collections.emptyMap(), RULE, "Everyone Else").getResult()); + assertTrue(doesUserMeetAudienceConditions(noAudienceProjectConfig, experiment, OTUtils.user(Collections.emptyMap()), RULE, "Everyone Else").getResult()); } /** @@ -138,7 +139,7 @@ public void doesUserMeetAudienceConditionsReturnsTrueIfExperimentHasNoAudiences( @Test public void doesUserMeetAudienceConditionsEvaluatesEvenIfExperimentHasAudiencesButUserHasNoAttributes() { Experiment experiment = projectConfig.getExperiments().get(0); - Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, Collections.emptyMap(), EXPERIMENT, experiment.getKey()).getResult(); + Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, OTUtils.user(Collections.emptyMap()), EXPERIMENT, experiment.getKey()).getResult(); assertTrue(result); logbackVerifier.expectMessage(Level.DEBUG, "Evaluating audiences for experiment \"etag1\": [100]."); @@ -154,11 +155,11 @@ public void doesUserMeetAudienceConditionsEvaluatesEvenIfExperimentHasAudiencesB * If the {@link Experiment} contains at least one {@link Audience}, but attributes is empty, * then {@link ExperimentUtils#doesUserMeetAudienceConditions(ProjectConfig, Experiment, Map, String, String)} should return false. */ - @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") + @SuppressFBWarnings("NP_NULL_PARAM_DEREF_NONVIRTUAL") @Test public void doesUserMeetAudienceConditionsEvaluatesEvenIfExperimentHasAudiencesButUserSendNullAttributes() throws Exception { Experiment experiment = projectConfig.getExperiments().get(0); - Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, null, EXPERIMENT, experiment.getKey()).getResult(); + Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, OTUtils.user(null), EXPERIMENT, experiment.getKey()).getResult(); assertTrue(result); logbackVerifier.expectMessage(Level.DEBUG, @@ -179,7 +180,7 @@ public void doesUserMeetAudienceConditionsEvaluatesEvenIfExperimentHasAudiencesB public void doesUserMeetAudienceConditionsEvaluatesExperimentHasTypedAudiences() { Experiment experiment = v4ProjectConfig.getExperiments().get(1); Map attribute = Collections.singletonMap("booleanKey", true); - Boolean result = doesUserMeetAudienceConditions(v4ProjectConfig, experiment, attribute, EXPERIMENT, experiment.getKey()).getResult(); + Boolean result = doesUserMeetAudienceConditions(v4ProjectConfig, experiment, OTUtils.user(attribute), EXPERIMENT, experiment.getKey()).getResult(); assertTrue(result); logbackVerifier.expectMessage(Level.DEBUG, @@ -200,7 +201,7 @@ public void doesUserMeetAudienceConditionsEvaluatesExperimentHasTypedAudiences() public void doesUserMeetAudienceConditionsReturnsTrueIfUserSatisfiesAnAudience() { Experiment experiment = projectConfig.getExperiments().get(0); Map attributes = Collections.singletonMap("browser_type", "chrome"); - Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, attributes, EXPERIMENT, experiment.getKey()).getResult(); + Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, OTUtils.user(attributes), EXPERIMENT, experiment.getKey()).getResult(); assertTrue(result); logbackVerifier.expectMessage(Level.DEBUG, @@ -221,7 +222,7 @@ public void doesUserMeetAudienceConditionsReturnsTrueIfUserSatisfiesAnAudience() public void doesUserMeetAudienceConditionsReturnsTrueIfUserDoesNotSatisfyAnyAudiences() { Experiment experiment = projectConfig.getExperiments().get(0); Map attributes = Collections.singletonMap("browser_type", "firefox"); - Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, attributes, EXPERIMENT, experiment.getKey()).getResult(); + Boolean result = doesUserMeetAudienceConditions(projectConfig, experiment, OTUtils.user(attributes), EXPERIMENT, experiment.getKey()).getResult(); assertFalse(result); logbackVerifier.expectMessage(Level.DEBUG, @@ -246,8 +247,8 @@ public void doesUserMeetAudienceConditionsHandlesNullValue() { AUDIENCE_WITH_MISSING_VALUE_VALUE); Map nonMatchingMap = Collections.singletonMap(ATTRIBUTE_NATIONALITY_KEY, "American"); - assertTrue(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, satisfiesFirstCondition, EXPERIMENT, experiment.getKey()).getResult()); - assertFalse(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, nonMatchingMap, EXPERIMENT, experiment.getKey()).getResult()); + assertTrue(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, OTUtils.user(satisfiesFirstCondition), EXPERIMENT, experiment.getKey()).getResult()); + assertFalse(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, OTUtils.user(nonMatchingMap), EXPERIMENT, experiment.getKey()).getResult()); } /** @@ -258,7 +259,7 @@ public void doesUserMeetAudienceConditionsHandlesNullValueAttributesWithNull() { Experiment experiment = v4ProjectConfig.getExperimentKeyMapping().get(EXPERIMENT_WITH_MALFORMED_AUDIENCE_KEY); Map attributesWithNull = Collections.singletonMap(ATTRIBUTE_NATIONALITY_KEY, null); - assertFalse(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, attributesWithNull, EXPERIMENT, experiment.getKey()).getResult()); + assertFalse(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, OTUtils.user(attributesWithNull), EXPERIMENT, experiment.getKey()).getResult()); logbackVerifier.expectMessage(Level.DEBUG, "Starting to evaluate audience \"2196265320\" with conditions: [and, [or, [or, {name='nationality', type='custom_attribute', match='null', value='English'}, {name='nationality', type='custom_attribute', match='null', value=null}]]]."); @@ -279,7 +280,7 @@ public void doesUserMeetAudienceConditionsHandlesNullConditionValue() { Map attributesEmpty = Collections.emptyMap(); // It should explicitly be set to null otherwise we will return false on empty maps - assertFalse(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, attributesEmpty, EXPERIMENT, experiment.getKey()).getResult()); + assertFalse(doesUserMeetAudienceConditions(v4ProjectConfig, experiment, OTUtils.user(attributesEmpty), EXPERIMENT, experiment.getKey()).getResult()); logbackVerifier.expectMessage(Level.DEBUG, "Starting to evaluate audience \"2196265320\" with conditions: [and, [or, [or, {name='nationality', type='custom_attribute', match='null', value='English'}, {name='nationality', type='custom_attribute', match='null', value=null}]]]."); diff --git a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java index 426422ea3..29cbe3695 100644 --- a/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/optimizelyconfig/OptimizelyConfigServiceTest.java @@ -334,7 +334,8 @@ private ProjectConfig generateOptimizelyConfig() { ) ), Collections.emptyList(), - Collections.emptyList() + Collections.emptyList(), + Collections.emptyList() ); } diff --git a/core-api/src/test/java/com/optimizely/ab/testutils/OTUtils.java b/core-api/src/test/java/com/optimizely/ab/testutils/OTUtils.java new file mode 100644 index 000000000..36c184369 --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/testutils/OTUtils.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2022, Optimizely and contributors + * + * 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 com.optimizely.ab.testutils; + +import com.optimizely.ab.*; +import java.util.Collections; +import java.util.Map; + +public class OTUtils { + public static OptimizelyUserContext user(String userId, Map attributes) { + Optimizely optimizely = new Optimizely.Builder().build(); + return new OptimizelyUserContext(optimizely, userId, attributes); + } + + public static OptimizelyUserContext user(Map attributes) { + return user("any-user", attributes); + } + + public static OptimizelyUserContext user() { + return user("any-user", Collections.emptyMap()); + } +} \ No newline at end of file diff --git a/core-api/src/test/resources/config/valid-project-config-v4.json b/core-api/src/test/resources/config/valid-project-config-v4.json index 01c927a5c..cc0de0908 100644 --- a/core-api/src/test/resources/config/valid-project-config-v4.json +++ b/core-api/src/test/resources/config/valid-project-config-v4.json @@ -945,5 +945,12 @@ } ] } + ], + "integrations": [ + { + "key": "odp", + "host": "https://example.com", + "publicKey": "test-key" + } ] }