evaluateFeature(
// String key = ruleKey;
String attributeValue = context.getAttributes().get(ruleKey) == null ? null : context.getAttributes().get(ruleKey).getAsString();
if (attributeValue == null || attributeValue.isEmpty()) {
- Float hashFNV = GrowthBookUtils.hash(attributeValue, 1, key);
- if (hashFNV == null) {
- hashFNV = 0f;
- }
- if (hashFNV > rule.getCoverage()) {
- continue;
- }
+ continue;
+ }
+ Float hashFNV = GrowthBookUtils.hash(attributeValue, 1, key);
+ if (hashFNV == null) {
+ hashFNV = 0f;
+ }
+ if (hashFNV > rule.getCoverage()) {
+ continue;
}
}
}
diff --git a/lib/src/main/java/growthbook/sdk/java/FeatureFetchException.java b/lib/src/main/java/growthbook/sdk/java/FeatureFetchException.java
index 030e78b0..d7f41da1 100644
--- a/lib/src/main/java/growthbook/sdk/java/FeatureFetchException.java
+++ b/lib/src/main/java/growthbook/sdk/java/FeatureFetchException.java
@@ -5,14 +5,14 @@
/**
* This error is thrown by {@link GBFeaturesRepository}
* You can call getErrorCode() to get an enum of various error types you can handle.
- *
+ *
* CONFIGURATION_ERROR:
- * - an encryptionKey was provided but the endpoint does not support encryption so decryption fails
- * - no features were found for an unencrypted endpoint
+ * - an encryptionKey was provided but the endpoint does not support encryption so decryption fails
+ * - no features were found for an unencrypted endpoint
* NO_RESPONSE_ERROR:
- * - there was no response body
+ * - there was no response body
* UNKNOWN:
- * - there was an unknown error that occurred when attempting to make the request.
+ * - there was an unknown error that occurred when attempting to make the request.
*/
public class FeatureFetchException extends Exception {
@@ -28,13 +28,13 @@ public class FeatureFetchException extends Exception {
*/
public enum FeatureFetchErrorCode {
/**
- * - an encryptionKey was provided but the endpoint does not support encryption so decryption fails
- * - no features were found for an unencrypted endpoint
+ * - an encryptionKey was provided but the endpoint does not support encryption so decryption fails
+ * - no features were found for an unencrypted endpoint
*/
CONFIGURATION_ERROR,
/**
- * - there was no response body
+ * - there was no response body
*/
NO_RESPONSE_ERROR,
@@ -44,7 +44,7 @@ public enum FeatureFetchErrorCode {
SSE_CONNECTION_ERROR,
/**
- * - there was an unknown error that occurred when attempting to make the request.
+ * - there was an unknown error that occurred when attempting to make the request.
*/
UNKNOWN,
}
@@ -52,7 +52,8 @@ public enum FeatureFetchErrorCode {
/**
* Create an exception with error code and custom message
- * @param errorCode {@link FeatureFetchErrorCode}
+ *
+ * @param errorCode {@link FeatureFetchErrorCode}
* @param errorMessage Custom error message string
*/
public FeatureFetchException(FeatureFetchErrorCode errorCode, String errorMessage) {
@@ -62,6 +63,7 @@ public FeatureFetchException(FeatureFetchErrorCode errorCode, String errorMessag
/**
* Create an exception with error code
+ *
* @param errorCode {@link FeatureFetchErrorCode}
*/
public FeatureFetchException(FeatureFetchErrorCode errorCode) {
diff --git a/lib/src/main/java/growthbook/sdk/java/FeatureRefreshCallback.java b/lib/src/main/java/growthbook/sdk/java/FeatureRefreshCallback.java
index 81f5f653..c5101e76 100644
--- a/lib/src/main/java/growthbook/sdk/java/FeatureRefreshCallback.java
+++ b/lib/src/main/java/growthbook/sdk/java/FeatureRefreshCallback.java
@@ -7,12 +7,14 @@ public interface FeatureRefreshCallback {
/**
* See {@link GBFeaturesRepository#onFeaturesRefresh(FeatureRefreshCallback)}
- * @param featuresJson Features as JSON string
+ *
+ * @param featuresJson Features as JSON string
*/
void onRefresh(String featuresJson);
/**
* See {@link GBFeaturesRepository#onFeaturesRefresh(FeatureRefreshCallback)}
+ *
* @param throwable Exception on refreshCallback
*/
void onError(Throwable throwable);
diff --git a/lib/src/main/java/growthbook/sdk/java/FeatureResult.java b/lib/src/main/java/growthbook/sdk/java/FeatureResult.java
index ff4ee9d1..f65383c2 100644
--- a/lib/src/main/java/growthbook/sdk/java/FeatureResult.java
+++ b/lib/src/main/java/growthbook/sdk/java/FeatureResult.java
@@ -1,13 +1,14 @@
package growthbook.sdk.java;
-import com.google.gson.*;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializer;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
-
import javax.annotation.Nullable;
-import java.lang.reflect.Type;
/**
* Results for a {@link FeatureEvaluator#evaluateFeature(String, GBContext, Class, JsonObject)}
@@ -46,6 +47,7 @@ public class FeatureResult {
/**
* Get a Gson JsonElement of the {@link FeatureResult}
+ *
* @return a Gson JsonElement
*/
public String toJson() {
@@ -54,6 +56,7 @@ public String toJson() {
/**
* Evaluates to true when the feature is on
+ *
* @return Boolean
*/
public Boolean isOn() {
@@ -84,6 +87,7 @@ public Boolean isOn() {
/**
* Evaluates to true when the feature is off
+ *
* @return Boolean
*/
public Boolean isOff() {
@@ -92,9 +96,10 @@ public Boolean isOff() {
/**
* Get a Gson JsonElement of the {@link FeatureResult}
- * @param object {@link FeatureResult}
- * @return a Gson JsonElement
+ *
+ * @param object {@link FeatureResult}
* @param value type for the feature
+ * @return a Gson JsonElement
*/
public static JsonElement getJson(FeatureResult object) {
JsonObject jsonObject = new JsonObject();
@@ -126,15 +131,11 @@ public static JsonElement getJson(FeatureResult object) {
/**
* a Gson serializer for {@link FeatureResult}
- * @return Gson serializer
+ *
* @param {@link FeatureResult}
+ * @return Gson serializer
*/
public static JsonSerializer> getSerializer() {
- return new JsonSerializer>() {
- @Override
- public JsonElement serialize(FeatureResult src, Type typeOfSrc, JsonSerializationContext context) {
- return FeatureResult.getJson(src);
- }
- };
+ return (src, typeOfSrc, context) -> FeatureResult.getJson(src);
}
}
diff --git a/lib/src/main/java/growthbook/sdk/java/FeatureResultSource.java b/lib/src/main/java/growthbook/sdk/java/FeatureResultSource.java
index c1e96b01..c7ab0264 100644
--- a/lib/src/main/java/growthbook/sdk/java/FeatureResultSource.java
+++ b/lib/src/main/java/growthbook/sdk/java/FeatureResultSource.java
@@ -32,7 +32,17 @@ public enum FeatureResultSource {
* When the value is assigned due to an experiment condition
*/
@SerializedName("experiment") EXPERIMENT("experiment"),
- ;
+
+ /**
+ * CyclicPrerequisite Value for the Feature is being processed
+ */
+ @SerializedName("cyclicPrerequisite") CYCLIC_PREREQUISITE("cyclicPrerequisite"),
+
+ /**
+ * Prerequisite Value for the Feature is being processed
+ */
+ @SerializedName("prerequisite") PREREQUISITE("prerequisite");
+
private final String rawValue;
FeatureResultSource(String rawValue) {
@@ -47,6 +57,7 @@ public String toString() {
/**
* Get a nullable enum Operator from the string value. Use this instead of valueOf()
+ *
* @param stringValue string to try to parse as an operator
* @return nullable Operator
*/
diff --git a/lib/src/main/java/growthbook/sdk/java/FeatureRule.java b/lib/src/main/java/growthbook/sdk/java/FeatureRule.java
index a268281e..2599ac77 100644
--- a/lib/src/main/java/growthbook/sdk/java/FeatureRule.java
+++ b/lib/src/main/java/growthbook/sdk/java/FeatureRule.java
@@ -21,12 +21,16 @@
* namespace (Namespace) - Adds the experiment to a namespace
* hashAttribute (string) - What user attribute should be used to assign variations (defaults to id)
*
+ *
* @param generic type for the value type for this experiment's variations.
*/
@Data
@Builder
@AllArgsConstructor
public class FeatureRule {
+ @Nullable
+ String id;
+
@Nullable
String key;
@@ -51,6 +55,9 @@ public class FeatureRule {
@Nullable
JsonElement condition;
+ @Nullable
+ ArrayList parentConditions;
+
@Nullable
Integer hashVersion;
diff --git a/lib/src/main/java/growthbook/sdk/java/Filter.java b/lib/src/main/java/growthbook/sdk/java/Filter.java
index 6f87f43c..4696984a 100644
--- a/lib/src/main/java/growthbook/sdk/java/Filter.java
+++ b/lib/src/main/java/growthbook/sdk/java/Filter.java
@@ -2,7 +2,6 @@
import lombok.Builder;
import lombok.Getter;
-
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -22,9 +21,10 @@ public class Filter {
/**
* Object used for mutual exclusion and filtering users out of experiments based on random hashes.
- * @param seed The seed used in the hash
- * @param ranges Array of ranges that are included
- * @param attribute The attribute to use (default: "id")
+ *
+ * @param seed The seed used in the hash
+ * @param ranges Array of ranges that are included
+ * @param attribute The attribute to use (default: "id")
* @param hashVersion The hash version to use (default: 2)
*/
@Builder
diff --git a/lib/src/main/java/growthbook/sdk/java/GBContext.java b/lib/src/main/java/growthbook/sdk/java/GBContext.java
index ce3bc6a9..235fd346 100644
--- a/lib/src/main/java/growthbook/sdk/java/GBContext.java
+++ b/lib/src/main/java/growthbook/sdk/java/GBContext.java
@@ -24,15 +24,16 @@ public class GBContext {
/**
* The {@link GBContextBuilder} is recommended for constructing a Context.
* Alternatively, you can use this static method instead of the builder.
- * @param attributesJson User attributes as JSON string
- * @param featuresJson Features response as JSON string, or the encrypted payload. Encrypted payload requires `encryptionKey`
- * @param encryptionKey Optional encryption key. If this is not null, featuresJson should be an encrypted payload.
- * @param enabled Whether globally all experiments are enabled (default: true)
- * @param isQaMode If true, random assignment is disabled and only explicitly forced variations are used.
- * @param url A URL string that is used for experiment evaluation, as well as forcing feature values.
- * @param allowUrlOverrides Boolean flag to allow URL overrides (default: false)
+ *
+ * @param attributesJson User attributes as JSON string
+ * @param featuresJson Features response as JSON string, or the encrypted payload. Encrypted payload requires `encryptionKey`
+ * @param encryptionKey Optional encryption key. If this is not null, featuresJson should be an encrypted payload.
+ * @param enabled Whether globally all experiments are enabled (default: true)
+ * @param isQaMode If true, random assignment is disabled and only explicitly forced variations are used.
+ * @param url A URL string that is used for experiment evaluation, as well as forcing feature values.
+ * @param allowUrlOverrides Boolean flag to allow URL overrides (default: false)
* @param forcedVariationsMap Force specific experiments to always assign a specific variation (used for QA)
- * @param trackingCallback A function that takes {@link Experiment} and {@link ExperimentResult} as arguments.
+ * @param trackingCallback A function that takes {@link Experiment} and {@link ExperimentResult} as arguments.
*/
@Builder
public GBContext(
@@ -114,6 +115,7 @@ private void setFeatures(@Nullable JsonObject features) {
/**
* You can update the attributes JSON with new user attributes to evaluate against.
+ *
* @param attributesJson updated user attributes
*/
public void setAttributesJson(String attributesJson) {
@@ -139,6 +141,7 @@ private void setAttributes(@Nullable JsonObject attributes) {
/**
* You can update the features JSON with new features to evaluate against.
+ *
* @param featuresJson updated features
*/
@@ -164,10 +167,12 @@ public void setFeaturesJson(String featuresJson) {
/**
* The builder class to help create a context. You can use {@link #builder()} or the {@link GBContext} constructor
*/
- public static class GBContextBuilder {} // This stub is required for JavaDoc and is filled by Lombuk
+ public static class GBContextBuilder {
+ } // This stub is required for JavaDoc and is filled by Lombuk
/**
* The builder class to help create a context. You can use this builder or the constructor
+ *
* @return {@link CustomGBContextBuilder}
*/
public static GBContextBuilder builder() {
diff --git a/lib/src/main/java/growthbook/sdk/java/GBFeaturesRepository.java b/lib/src/main/java/growthbook/sdk/java/GBFeaturesRepository.java
index 0770e6cf..1ddde88d 100644
--- a/lib/src/main/java/growthbook/sdk/java/GBFeaturesRepository.java
+++ b/lib/src/main/java/growthbook/sdk/java/GBFeaturesRepository.java
@@ -4,12 +4,16 @@
import com.google.gson.JsonObject;
import lombok.Builder;
import lombok.Getter;
-import okhttp3.*;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import okhttp3.sse.EventSources;
import org.jetbrains.annotations.NotNull;
-
import javax.annotation.Nullable;
import java.io.IOException;
import java.time.Instant;
@@ -36,7 +40,8 @@ public class GBFeaturesRepository implements IGBFeaturesRepository {
@Getter
private FeatureRefreshStrategy refreshStrategy;
- @Nullable @Getter
+ @Nullable
+ @Getter
private final String encryptionKey;
@Getter
@@ -55,8 +60,10 @@ public class GBFeaturesRepository implements IGBFeaturesRepository {
private Boolean initialized = false;
private Boolean sseAllowed = false;
- @Nullable private Request sseRequest = null;
- @Nullable private EventSource sseEventSource = null;
+ @Nullable
+ private Request sseRequest = null;
+ @Nullable
+ private EventSource sseEventSource = null;
/**
* Allows you to get the features JSON from the provided {@link GBFeaturesRepository#getFeaturesEndpoint()}.
@@ -67,37 +74,39 @@ public class GBFeaturesRepository implements IGBFeaturesRepository {
/**
* Create a new GBFeaturesRepository
- * @param apiHost The GrowthBook API host (default: https://cdn.growthbook.io)
- * @param clientKey Your client ID, e.g. sdk-abc123
+ *
+ * @param apiHost The GrowthBook API host (default: https://cdn.growthbook.io)
+ * @param clientKey Your client ID, e.g. sdk-abc123
* @param encryptionKey optional key for decrypting encrypted payload
* @param swrTtlSeconds How often the cache should be invalidated when using {@link FeatureRefreshStrategy#STALE_WHILE_REVALIDATE} (default: 60)
*/
@Builder
public GBFeaturesRepository(
- @Nullable String apiHost,
- String clientKey,
- @Nullable String encryptionKey,
- @Nullable FeatureRefreshStrategy refreshStrategy,
- @Nullable Integer swrTtlSeconds
+ @Nullable String apiHost,
+ String clientKey,
+ @Nullable String encryptionKey,
+ @Nullable FeatureRefreshStrategy refreshStrategy,
+ @Nullable Integer swrTtlSeconds
) {
this(apiHost, clientKey, encryptionKey, refreshStrategy, swrTtlSeconds, null);
}
/**
* Create a new GBFeaturesRepository
- * @param apiHost The GrowthBook API host (default: https://cdn.growthbook.io)
- * @param clientKey Your client ID, e.g. sdk-abc123
+ *
+ * @param apiHost The GrowthBook API host (default: https://cdn.growthbook.io)
+ * @param clientKey Your client ID, e.g. sdk-abc123
* @param encryptionKey optional key for decrypting encrypted payload
* @param swrTtlSeconds How often the cache should be invalidated when using {@link FeatureRefreshStrategy#STALE_WHILE_REVALIDATE} (default: 60)
- * @param okHttpClient HTTP client (optional)
+ * @param okHttpClient HTTP client (optional)
*/
public GBFeaturesRepository(
- @Nullable String apiHost,
- String clientKey,
- @Nullable String encryptionKey,
- @Nullable FeatureRefreshStrategy refreshStrategy,
- @Nullable Integer swrTtlSeconds,
- @Nullable OkHttpClient okHttpClient
+ @Nullable String apiHost,
+ String clientKey,
+ @Nullable String encryptionKey,
+ @Nullable FeatureRefreshStrategy refreshStrategy,
+ @Nullable Integer swrTtlSeconds,
+ @Nullable OkHttpClient okHttpClient
) {
if (clientKey == null) throw new IllegalArgumentException("clientKey cannot be null");
@@ -144,7 +153,8 @@ public String getFeaturesJson() {
* Subscribe to feature refresh events
* This callback is called when the features are successfully refreshed or there is an error when refreshing.
* This is called even if the features have not changed.
- * @param callback This callback will be called when features are refreshed
+ *
+ * @param callback This callback will be called when features are refreshed
*/
@Override
public void onFeaturesRefresh(FeatureRefreshCallback callback) {
@@ -160,8 +170,8 @@ private void enqueueFeatureRefreshRequest() {
GBFeaturesRepository self = this;
Request request = new Request.Builder()
- .url(this.featuresEndpoint)
- .build();
+ .url(this.featuresEndpoint)
+ .build();
this.okHttpClient.newCall(request).enqueue(new Callback() {
@Override
@@ -224,19 +234,19 @@ private void createEventSourceListenerAndStartListening(Boolean retryOnFailure)
if (this.sseHttpClient == null) {
this.sseHttpClient = new OkHttpClient.Builder()
- .addInterceptor(new GBFeaturesRepositoryRequestInterceptor())
- .retryOnConnectionFailure(true)
- .connectTimeout(0, TimeUnit.SECONDS)
- .readTimeout(0, TimeUnit.SECONDS)
- .writeTimeout(0, TimeUnit.SECONDS)
- .build();
+ .addInterceptor(new GBFeaturesRepositoryRequestInterceptor())
+ .retryOnConnectionFailure(true)
+ .connectTimeout(0, TimeUnit.SECONDS)
+ .readTimeout(0, TimeUnit.SECONDS)
+ .writeTimeout(0, TimeUnit.SECONDS)
+ .build();
}
this.sseRequest = new Request.Builder()
- .url(this.eventsEndpoint)
- .header("Accept", "application/json; q=0.5")
- .addHeader("Accept", "text/event-stream")
- .build();
+ .url(this.eventsEndpoint)
+ .header("Accept", "application/json; q=0.5")
+ .addHeader("Accept", "text/event-stream")
+ .build();
GBEventSourceListener gbEventSourceListener =
new GBEventSourceListener(
@@ -274,8 +284,8 @@ public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable t, @
};
this.sseEventSource = EventSources
- .createFactory(this.sseHttpClient)
- .newEventSource(sseRequest, gbEventSourceListener);
+ .createFactory(this.sseHttpClient)
+ .newEventSource(sseRequest, gbEventSourceListener);
this.sseHttpClient.newCall(sseRequest).enqueue(new Callback() {
@Override
@@ -296,9 +306,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO
*/
private OkHttpClient initializeHttpClient() {
OkHttpClient client = new OkHttpClient.Builder()
- .addInterceptor(new GBFeaturesRepositoryRequestInterceptor())
- .retryOnConnectionFailure(true)
- .build();
+ .addInterceptor(new GBFeaturesRepositoryRequestInterceptor())
+ .retryOnConnectionFailure(true)
+ .build();
return client;
}
@@ -324,8 +334,8 @@ private void fetchFeatures() throws FeatureFetchException {
}
Request request = new Request.Builder()
- .url(this.featuresEndpoint)
- .build();
+ .url(this.featuresEndpoint)
+ .build();
try (Response response = this.okHttpClient.newCall(request).execute()) {
String sseSupportHeader = response.header("x-sse-support");
@@ -336,20 +346,21 @@ private void fetchFeatures() throws FeatureFetchException {
e.printStackTrace();
throw new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
- e.getMessage()
+ FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
+ e.getMessage()
);
}
}
/**
* Reads the response JSON properties `features` or `encryptedFeatures`, and decrypts if necessary
+ *
* @param responseJsonString JSON response object
*/
private void onResponseJson(String responseJsonString) throws FeatureFetchException {
try {
JsonObject jsonObject = GrowthBookJsonUtils.getInstance()
- .gson.fromJson(responseJsonString, JsonObject.class);
+ .gson.fromJson(responseJsonString, JsonObject.class);
// Features will be refreshed as either an encrypted or un-encrypted JSON string
String refreshedFeatures;
@@ -359,8 +370,8 @@ private void onResponseJson(String responseJsonString) throws FeatureFetchExcept
JsonElement encryptedFeaturesJsonElement = jsonObject.get("encryptedFeatures");
if (encryptedFeaturesJsonElement == null) {
throw new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
- "encryptionKey provided but endpoint not encrypted"
+ FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
+ "encryptionKey provided but endpoint not encrypted"
);
}
@@ -371,8 +382,8 @@ private void onResponseJson(String responseJsonString) throws FeatureFetchExcept
JsonElement featuresJsonElement = jsonObject.get("features");
if (featuresJsonElement == null) {
throw new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
- "No features found"
+ FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
+ "No features found"
);
}
@@ -386,8 +397,8 @@ private void onResponseJson(String responseJsonString) throws FeatureFetchExcept
e.printStackTrace();
throw new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
- e.getMessage()
+ FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
+ e.getMessage()
);
}
}
@@ -406,6 +417,7 @@ private void onRefreshFailed(Throwable throwable) {
/**
* Handles the successful features fetching response
+ *
* @param response Successful response
*/
private void onSuccess(Response response) throws FeatureFetchException {
@@ -413,7 +425,7 @@ private void onSuccess(Response response) throws FeatureFetchException {
ResponseBody responseBody = response.body();
if (responseBody == null) {
throw new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR
+ FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR
);
}
@@ -422,14 +434,15 @@ private void onSuccess(Response response) throws FeatureFetchException {
e.printStackTrace();
throw new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
- e.getMessage()
+ FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
+ e.getMessage()
);
}
}
private interface GBEventSourceHandler {
void onClose(EventSource eventSource);
+
void onFeaturesResponse(String featuresJsonResponse) throws FeatureFetchException;
}
diff --git a/lib/src/main/java/growthbook/sdk/java/GrowthBook.java b/lib/src/main/java/growthbook/sdk/java/GrowthBook.java
index 468d28af..10d92734 100644
--- a/lib/src/main/java/growthbook/sdk/java/GrowthBook.java
+++ b/lib/src/main/java/growthbook/sdk/java/GrowthBook.java
@@ -3,7 +3,6 @@
import com.google.gson.JsonObject;
import growthbook.sdk.java.stickyBucketing.InMemoryStickyBucketServiceImpl;
import growthbook.sdk.java.stickyBucketing.StickyBucketService;
-
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
@@ -29,6 +28,7 @@ public class GrowthBook implements IGrowthBook {
/**
* Initialize the GrowthBook SDK with a provided {@link GBContext}
+ *
* @param context {@link GBContext}
*/
public GrowthBook(GBContext context) {
@@ -57,9 +57,9 @@ public GrowthBook() {
/**
* INTERNAL: Constructor with injected dependencies. Useful for testing but not intended to be used
*
- * @param context Context
- * @param featureEvaluator FeatureEvaluator
- * @param conditionEvaluator ConditionEvaluator
+ * @param context Context
+ * @param featureEvaluator FeatureEvaluator
+ * @param conditionEvaluator ConditionEvaluator
* @param experimentEvaluator ExperimentEvaluator
*/
GrowthBook(GBContext context, FeatureEvaluator featureEvaluator, ConditionEvaluator conditionEvaluator, ExperimentEvaluator experimentEvaluator) {
@@ -86,10 +86,10 @@ public void setAttributes(String attributesJsonString) {
}
@Override
- public ExperimentResult run(Experiment experiment) {
+ public ExperimentResult run(Experiment experiment) {
ExperimentResult result = experimentEvaluatorEvaluator.evaluateExperiment(experiment, this.context, null, attributeOverrides);
- this.callbacks.forEach( callback -> {
+ this.callbacks.forEach(callback -> {
callback.onRun(result);
});
@@ -162,17 +162,18 @@ public Float getFeatureValue(String featureKey, Float defaultValue) {
@Override
public Integer getFeatureValue(String featureKey, Integer defaultValue) {
try {
- // Type erasure occurs so a Double ends up being returned
- Double maybeValue = (Double) this.featureEvaluator.evaluateFeature(featureKey, context, Double.class, attributeOverrides).getValue();
+ Object maybeValue = this.featureEvaluator.evaluateFeature(featureKey, context, Object.class, attributeOverrides).getValue();
if (maybeValue == null) {
return defaultValue;
}
- try {
- return maybeValue.intValue();
- } catch (NumberFormatException e) {
- return defaultValue;
+ if (maybeValue instanceof Double) {
+ return ((Double) maybeValue).intValue();
+ } else if (maybeValue instanceof Long) {
+ return ((Long) maybeValue).intValue();
+ } else {
+ return defaultValue; // или можно бросить исключение, если требуется строгое соответствие типу Integer
}
} catch (Exception e) {
e.printStackTrace();
@@ -216,8 +217,19 @@ public Boolean evaluateCondition(String attributesJsonString, String conditionJs
@Override
public Double getFeatureValue(String featureKey, Double defaultValue) {
try {
- Double maybeValue = (Double) this.featureEvaluator.evaluateFeature(featureKey, context, Double.class, attributeOverrides).getValue();
- return maybeValue == null ? defaultValue : maybeValue;
+ Object maybeValue = this.featureEvaluator.evaluateFeature(featureKey, context, Object.class, attributeOverrides).getValue();
+
+ if (maybeValue == null) {
+ return defaultValue;
+ }
+
+ if (maybeValue instanceof Double) {
+ return (Double) maybeValue;
+ } else if (maybeValue instanceof Long) {
+ return ((Long) maybeValue).doubleValue();
+ } else {
+ return defaultValue;
+ }
} catch (Exception e) {
e.printStackTrace();
return defaultValue;
diff --git a/lib/src/main/java/growthbook/sdk/java/GrowthBookJsonUtils.java b/lib/src/main/java/growthbook/sdk/java/GrowthBookJsonUtils.java
index 9ee580cc..b9230931 100644
--- a/lib/src/main/java/growthbook/sdk/java/GrowthBookJsonUtils.java
+++ b/lib/src/main/java/growthbook/sdk/java/GrowthBookJsonUtils.java
@@ -4,7 +4,7 @@
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
-
+import com.google.gson.ToNumberPolicy;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -38,11 +38,14 @@ private GrowthBookJsonUtils() {
// FeatureResult
gsonBuilder.registerTypeAdapter(FeatureResult.class, FeatureResult.getSerializer());
+ gsonBuilder.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE);
+
gson = gsonBuilder.create();
}
/**
* The JSON utils singleton
+ *
* @return an instance of {@link GrowthBookJsonUtils}
*/
public static GrowthBookJsonUtils getInstance() {
@@ -58,6 +61,7 @@ public static GrowthBookJsonUtils getInstance() {
/**
* Unwrap an object. If it's not a JsonElement, you'll get the object right back
+ *
* @param o the JSON element to unwrap.
* @return unwrapped or original object
*/
@@ -101,7 +105,7 @@ private static Number unwrapNumber(final Number n) {
if (bigDecimal.scale() <= 0) {
if (bigDecimal.abs().compareTo(new BigDecimal(Integer.MAX_VALUE)) <= 0) {
unwrapped = bigDecimal.intValue();
- } else if (bigDecimal.abs().compareTo(new BigDecimal(Long.MAX_VALUE)) <= 0){
+ } else if (bigDecimal.abs().compareTo(new BigDecimal(Long.MAX_VALUE)) <= 0) {
unwrapped = bigDecimal.longValue();
} else {
unwrapped = bigDecimal;
@@ -122,6 +126,7 @@ private static Number unwrapNumber(final Number n) {
/**
* A convenience method to help work with types of JSON elements
+ *
* @param element unknown JsonElement
* @return {@link DataType}
*/
diff --git a/lib/src/main/java/growthbook/sdk/java/GrowthBookUtils.java b/lib/src/main/java/growthbook/sdk/java/GrowthBookUtils.java
index ad2b2cc0..713fb546 100644
--- a/lib/src/main/java/growthbook/sdk/java/GrowthBookUtils.java
+++ b/lib/src/main/java/growthbook/sdk/java/GrowthBookUtils.java
@@ -1,18 +1,23 @@
package growthbook.sdk.java;
-import com.google.gson.*;
-
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
import growthbook.sdk.java.stickyBucketing.StickyAssignmentsDocument;
import growthbook.sdk.java.stickyBucketing.StickyBucketService;
-import lombok.val;
import org.jetbrains.annotations.NotNull;
-
import javax.annotation.Nullable;
import java.net.MalformedURLException;
import java.net.URL;
-
-// 8 references from java.util package are used in this file
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
/**
* INTERNAL: Implementation of for internal utility methods to support {@link growthbook.sdk.java.GrowthBook}
@@ -550,7 +555,7 @@ public static Map getStickyBucketAttributes(GBContext context,
JsonElement features = featuresJsonElement != null ? featuresJsonElement : context.getFeatures();
if (features != null) {
- for (val id : features.getAsJsonObject().keySet()) {
+ for (String id : features.getAsJsonObject().keySet()) {
Feature feature = GrowthBookJsonUtils
.getInstance()
.gson
@@ -638,9 +643,9 @@ public static Map getStickyBucketAssignments(
}
}
- if (stickyAssignmentsDocuments.get(hashKey) != null) {
- mergedAssignments.putAll(stickyAssignmentsDocuments.get(hashKey).getAssignments());
- }
+ if (stickyAssignmentsDocuments.get(hashKey) != null) {
+ mergedAssignments.putAll(stickyAssignmentsDocuments.get(hashKey).getAssignments());
+ }
return mergedAssignments;
}
diff --git a/lib/src/main/java/growthbook/sdk/java/IGrowthBook.java b/lib/src/main/java/growthbook/sdk/java/IGrowthBook.java
index e502114d..8620a012 100644
--- a/lib/src/main/java/growthbook/sdk/java/IGrowthBook.java
+++ b/lib/src/main/java/growthbook/sdk/java/IGrowthBook.java
@@ -1,12 +1,11 @@
package growthbook.sdk.java;
import growthbook.sdk.java.stickyBucketing.StickyBucketService;
-
import javax.annotation.Nullable;
interface IGrowthBook {
- ExperimentResult run(Experiment experiment);
+ ExperimentResult run(Experiment experiment);
void subscribe(ExperimentRunCallback callback);
@@ -19,12 +18,14 @@ interface IGrowthBook {
/**
* Call this with the JSON string returned from API.
+ *
* @param featuresJsonString features JSON from the GrowthBook API
*/
void setFeatures(String featuresJsonString);
/**
* Update the user's attributes
+ *
* @param attributesJsonString user attributes JSON
*/
void setAttributes(String attributesJsonString);
@@ -34,11 +35,13 @@ interface IGrowthBook {
void setInMemoryStickyBucketService();
Boolean isOn(String featureKey);
+
Boolean isOff(String featureKey);
/**
* Get the feature value as a boolean
- * @param featureKey name of the feature
+ *
+ * @param featureKey name of the feature
* @param defaultValue boolean value to return
* @return the found value or defaultValue
*/
@@ -46,7 +49,8 @@ interface IGrowthBook {
/**
* Get the feature value as a string
- * @param featureKey name of the feature
+ *
+ * @param featureKey name of the feature
* @param defaultValue string value to return
* @return the found value or defaultValue
*/
@@ -54,7 +58,8 @@ interface IGrowthBook {
/**
* Get the feature value as a float
- * @param featureKey name of the feature
+ *
+ * @param featureKey name of the feature
* @param defaultValue float value to return
* @return the found value or defaultValue
*/
@@ -62,7 +67,8 @@ interface IGrowthBook {
/**
* Get the feature value as an integer
- * @param featureKey name of the feature
+ *
+ * @param featureKey name of the feature
* @param defaultValue integer value to return
* @return the found value or defaultValue
*/
@@ -70,7 +76,8 @@ interface IGrowthBook {
/**
* Get the feature value as a double
- * @param featureKey name of the feature
+ *
+ * @param featureKey name of the feature
* @param defaultValue integer value to return
* @return the found value or defaultValue
*/
@@ -78,7 +85,8 @@ interface IGrowthBook {
/**
* Get the feature value as an Object. This may be useful for implementations that do not use Gson.
- * @param featureKey feature identifier
+ *
+ * @param featureKey feature identifier
* @param defaultValue default object value
* @return Object
*/
@@ -87,11 +95,12 @@ interface IGrowthBook {
/**
* Get the feature value as a Gson-deserializable.
* If your class requires a custom deserializer, use {@link #getFeatureValue(String, Object)} instead and deserialize it with your own Gson instance.
- * @param featureKey feature identifier
- * @param defaultValue default generic class
+ *
+ * @param featureKey feature identifier
+ * @param defaultValue default generic class
* @param gsonDeserializableClass the class of the generic, e.g. MyFeature.class
+ * @param Gson deserializable type
* @return ValueType instance
- * @param Gson deserializable type
*/
ValueType getFeatureValue(String featureKey, ValueType defaultValue, Class gsonDeserializableClass);
diff --git a/lib/src/main/java/growthbook/sdk/java/Namespace.java b/lib/src/main/java/growthbook/sdk/java/Namespace.java
index 4691ca00..27a7bb60 100644
--- a/lib/src/main/java/growthbook/sdk/java/Namespace.java
+++ b/lib/src/main/java/growthbook/sdk/java/Namespace.java
@@ -1,13 +1,14 @@
package growthbook.sdk.java;
-import com.google.gson.*;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonSerializer;
import com.google.gson.annotations.Expose;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
-import java.lang.reflect.Type;
-
/**
* A tuple that specifies what part of a namespace an experiment includes. If two experiments are in the same namespace and their ranges don't overlap, they wil be mutually exclusive.
*
@@ -59,6 +60,7 @@ static Namespace fromJson(JsonElement jsonElement) {
/**
* A JSON string for the namespace, resulting in a triple value [id, rangeStart, rangeEnd]
+ *
* @return JSON string
*/
public String toJson() {
@@ -72,27 +74,19 @@ public String toString() {
/**
* a Gson deserializer for {@link Namespace}
+ *
* @return a deserializer for {@link Namespace}
*/
public static JsonDeserializer getDeserializer() {
- return new JsonDeserializer() {
- @Override
- public Namespace deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
- return Namespace.fromJson(json);
- }
- };
+ return (json, typeOfT, context) -> Namespace.fromJson(json);
}
/**
* a Gson serializer for {@link Namespace}
+ *
* @return a serializer for {@link Namespace}
*/
public static JsonSerializer getSerializer() {
- return new JsonSerializer() {
- @Override
- public JsonElement serialize(Namespace src, Type typeOfSrc, JsonSerializationContext context) {
- return Namespace.getJson(src);
- }
- };
+ return (src, typeOfSrc, context) -> Namespace.getJson(src);
}
}
diff --git a/lib/src/main/java/growthbook/sdk/java/ParentCondition.java b/lib/src/main/java/growthbook/sdk/java/ParentCondition.java
new file mode 100644
index 00000000..b265bc84
--- /dev/null
+++ b/lib/src/main/java/growthbook/sdk/java/ParentCondition.java
@@ -0,0 +1,15 @@
+package growthbook.sdk.java;
+
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@RequiredArgsConstructor
+public class ParentCondition {
+ private String id;
+ private JsonObject condition;
+ private Boolean gate;
+}
diff --git a/lib/src/main/java/growthbook/sdk/java/VariationMeta.java b/lib/src/main/java/growthbook/sdk/java/VariationMeta.java
index 6c68895c..9a2a8b12 100644
--- a/lib/src/main/java/growthbook/sdk/java/VariationMeta.java
+++ b/lib/src/main/java/growthbook/sdk/java/VariationMeta.java
@@ -4,7 +4,6 @@
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
-
import javax.annotation.Nullable;
@@ -15,10 +14,13 @@
@Builder
@AllArgsConstructor
public class VariationMeta {
- @Nullable String key;
+ @Nullable
+ String key;
- @Nullable String name;
+ @Nullable
+ String name;
@SerializedName("passthrough")
- @Nullable Boolean passThrough;
+ @Nullable
+ Boolean passThrough;
}
diff --git a/lib/src/test/java/growthbook/sdk/java/ConditionEvaluatorTest.java b/lib/src/test/java/growthbook/sdk/java/ConditionEvaluatorTest.java
index f20a220a..0b04e860 100644
--- a/lib/src/test/java/growthbook/sdk/java/ConditionEvaluatorTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/ConditionEvaluatorTest.java
@@ -1,5 +1,10 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -9,11 +14,8 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-
import java.util.ArrayList;
-import static org.junit.jupiter.api.Assertions.*;
-
class ConditionEvaluatorTest {
final TestCasesJsonHelper helper = TestCasesJsonHelper.getInstance();
@@ -21,8 +23,8 @@ class ConditionEvaluatorTest {
final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
static final String[] expectedExceptionStrings = {
- "Expected BEGIN_ARRAY but was NUMBER at path $",
- "java.util.regex.PatternSyntaxException: Dangling meta character '?' near index 3"
+ "Expected BEGIN_ARRAY but was NUMBER at path $",
+ "java.util.regex.PatternSyntaxException: Dangling meta character '?' near index 3"
};
@BeforeEach
@@ -116,61 +118,6 @@ void test_getPath() {
assertNull(evaluator.getPath(attributes, "job.company"));
}
- @Test
- void test_paddedVersionString_eq() {
- JsonArray testCases = helper.versionCompareTestCases_eq();
-
- for (int i = 0; i < testCases.size(); i++) {
- JsonArray test = (JsonArray) testCases.get(i);
- String version = test.get(0).getAsString();
- String otherVersion = test.get(1).getAsString();
- Boolean equals = test.get(2).getAsBoolean();
-
- String paddedVersion = StringUtils.paddedVersionString(version);
- String paddedOther = StringUtils.paddedVersionString(otherVersion);
-
- assertEquals(paddedVersion.compareTo(paddedOther) == 0, equals);
- }
- }
-
- @Test
- void test_paddedVersionString_lt() {
- JsonArray testCases = helper.versionCompareTestCases_lt();
-
- for (int i = 0; i < testCases.size(); i++) {
- JsonArray test = (JsonArray) testCases.get(i);
- String version = test.get(0).getAsString();
- String otherVersion = test.get(1).getAsString();
- Boolean equals = test.get(2).getAsBoolean();
-
- String paddedVersion = StringUtils.paddedVersionString(version);
- String paddedOther = StringUtils.paddedVersionString(otherVersion);
-
-// System.out.printf("%s < %s = %s - actual: %s\n", paddedVersion, paddedOther, equals, paddedVersion.compareTo(paddedOther) < 0);
-
- assertEquals(paddedVersion.compareTo(paddedOther) < 0, equals);
- }
- }
-
- @Test
- void test_paddedVersionString_gt() {
- JsonArray testCases = helper.versionCompareTestCases_gt();
-
- for (int i = 0; i < testCases.size(); i++) {
- JsonArray test = (JsonArray) testCases.get(i);
- String version = test.get(0).getAsString();
- String otherVersion = test.get(1).getAsString();
- Boolean equals = test.get(2).getAsBoolean();
-
- String paddedVersion = StringUtils.paddedVersionString(version);
- String paddedOther = StringUtils.paddedVersionString(otherVersion);
-
-// System.out.printf("%s > %s = %s - actual: %s\n", paddedVersion, paddedOther, equals, paddedVersion.compareTo(paddedOther) > 0);
-
- assertEquals(paddedVersion.compareTo(paddedOther) > 0, equals);
- }
- }
-
private boolean unexpectedExceptionOccurred(String stacktrace) {
if (stacktrace.isEmpty()) {
return false;
@@ -180,7 +127,7 @@ private boolean unexpectedExceptionOccurred(String stacktrace) {
return false;
}
}
- System.out.println(stacktrace.toString());
+ System.out.println(stacktrace);
return true;
}
diff --git a/lib/src/test/java/growthbook/sdk/java/DecryptionUtilsTest.java b/lib/src/test/java/growthbook/sdk/java/DecryptionUtilsTest.java
index 4d336bd2..64a3d334 100644
--- a/lib/src/test/java/growthbook/sdk/java/DecryptionUtilsTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/DecryptionUtilsTest.java
@@ -1,12 +1,14 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import growthbook.sdk.java.testhelpers.TestCasesJsonHelper;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.*;
-
class DecryptionUtilsTest {
final TestCasesJsonHelper helper = TestCasesJsonHelper.getInstance();
diff --git a/lib/src/test/java/growthbook/sdk/java/EvaluateFeatureWithStickyBucketingFeatureTest.java b/lib/src/test/java/growthbook/sdk/java/EvaluateFeatureWithStickyBucketingFeatureTest.java
index 40c0f590..703154d7 100644
--- a/lib/src/test/java/growthbook/sdk/java/EvaluateFeatureWithStickyBucketingFeatureTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/EvaluateFeatureWithStickyBucketingFeatureTest.java
@@ -1,5 +1,7 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import com.google.common.reflect.TypeToken;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@@ -11,10 +13,11 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-
-import java.util.*;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
public class EvaluateFeatureWithStickyBucketingFeatureTest {
private static TestCasesJsonHelper helper;
@@ -36,9 +39,9 @@ void setUp() {
@Test
void testsStickyBucketingFeature() {
- ArrayList passedTests = new ArrayList<>();
- ArrayList failedTests = new ArrayList<>();
- ArrayList failingIndexes = new ArrayList<>();
+ List passedTests = new ArrayList<>();
+ List failedTests = new ArrayList<>();
+ List failingIndexes = new ArrayList<>();
JsonArray stickyBucketTestCases = helper.getStickyBucketTestCases();
for (int i = 0; i < stickyBucketTestCases.size(); i++) {
JsonArray testCase = stickyBucketTestCases.get(i).getAsJsonArray();
@@ -99,8 +102,8 @@ void testsStickyBucketingFeature() {
}.getType()
);
- String status = "\n" + testCase.get(0).getAsString() + expectedExperimentResult + "&" + expectedStickyAssignmentsDocument + "\n\n"
- + "\n" + actualExperimentResult + "&" + context.getStickyBucketAssignmentDocs();
+ String status = "\n" + description + expectedExperimentResult + "&" + expectedStickyAssignmentsDocument + "\n\n"
+ + "\n" + actualExperimentResult + "&" + actualStickyBucketAssignmentDocs;
if (Objects.equals(actualExperimentResult, expectedExperimentResult) && expectedStickyAssignmentsDocument.equals(context.getStickyBucketAssignmentDocs())) {
passedTests.add(status);
@@ -114,6 +117,5 @@ void testsStickyBucketingFeature() {
}
assertEquals(0, failedTests.size());
-
}
}
diff --git a/lib/src/test/java/growthbook/sdk/java/ExperimentResultTest.java b/lib/src/test/java/growthbook/sdk/java/ExperimentResultTest.java
index 60626868..27690a5b 100644
--- a/lib/src/test/java/growthbook/sdk/java/ExperimentResultTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/ExperimentResultTest.java
@@ -1,12 +1,13 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
import com.google.gson.reflect.TypeToken;
import org.junit.jupiter.api.Test;
-
import java.lang.reflect.Type;
-import static org.junit.jupiter.api.Assertions.*;
-
class ExperimentResultTest {
final GrowthBookJsonUtils jsonUtils = GrowthBookJsonUtils.getInstance();
diff --git a/lib/src/test/java/growthbook/sdk/java/ExperimentTest.java b/lib/src/test/java/growthbook/sdk/java/ExperimentTest.java
index 62fb2883..618f859f 100644
--- a/lib/src/test/java/growthbook/sdk/java/ExperimentTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/ExperimentTest.java
@@ -1,13 +1,14 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import org.junit.jupiter.api.Test;
-
import java.lang.reflect.Type;
import java.util.ArrayList;
-import static org.junit.jupiter.api.Assertions.*;
-
class ExperimentTest {
final GrowthBookJsonUtils jsonUtils = GrowthBookJsonUtils.getInstance();
@@ -21,7 +22,6 @@ void canBeConstructed() {
ArrayList variations = new ArrayList<>();
Namespace namespace = Namespace.builder().build();
- String conditionJson = "{}";
Experiment experiment = new Experiment(
"my_experiment",
@@ -29,7 +29,8 @@ void canBeConstructed() {
weights,
true,
0.5f,
- conditionJson,
+ new JsonObject(),
+ null,
namespace,
1,
"_id",
@@ -53,10 +54,11 @@ void canBeConstructed() {
assertEquals("my_experiment", experiment.getKey());
assertEquals("_id", experiment.hashAttribute);
assertEquals("_id", experiment.getHashAttribute());
+ assert experiment.weights != null;
assertEquals(0.3f, experiment.weights.get(0));
assertEquals(0.7f, experiment.weights.get(1));
- assertTrue(experiment.isActive);
- assertTrue(experiment.getIsActive());
+ assertEquals(Boolean.TRUE, experiment.isActive);
+ assertEquals(Boolean.TRUE, experiment.getIsActive());
}
@Test
@@ -85,8 +87,8 @@ void canBeBuilt() {
assertEquals("my_experiment", experiment.getKey());
assertEquals("_id", experiment.hashAttribute);
assertEquals("_id", experiment.getHashAttribute());
- assertTrue(experiment.isActive);
- assertTrue(experiment.getIsActive());
+ assertEquals(Boolean.TRUE, experiment.isActive);
+ assertEquals(Boolean.TRUE, experiment.getIsActive());
}
@Test
@@ -127,13 +129,13 @@ void test_canBeSerialized() {
.weights(weights)
.isActive(true)
.coverage(0.5f)
- .conditionJson("{}")
+ .conditionJson(new JsonObject())
.namespace(namespace)
.force(1)
.hashAttribute("_id")
.build();
- assertEquals("{\"key\":\"my_experiment\",\"variations\":[100,200],\"weights\":[0.3,0.7],\"active\":true,\"coverage\":0.5,\"conditionJson\":\"{}\",\"namespace\":[\"pricing\",0.0,1.0],\"force\":1,\"hashAttribute\":\"_id\"}", subject.toJson());
+ assertEquals("{\"key\":\"my_experiment\",\"variations\":[100,200],\"weights\":[0.3,0.7],\"active\":true,\"coverage\":0.5,\"conditionJson\":{},\"namespace\":[\"pricing\",0.0,1.0],\"force\":1,\"hashAttribute\":\"_id\"}", subject.toJson());
}
@Test
@@ -142,8 +144,9 @@ void test_canBeDeserialized() {
Experiment subject = jsonUtils.gson.fromJson("{\"key\":\"my_experiment\",\"variations\":[100,200],\"weights\":[0.3,0.7],\"active\":true,\"coverage\":0.5,\"namespace\":[\"pricing\",0.0,1.0],\"force\":1,\"hashAttribute\":\"_id\"}", experimentType);
+ assert subject.getNamespace() != null;
assertEquals("pricing", subject.getNamespace().getId());
- assertTrue(subject.getIsActive());
+ assertEquals(Boolean.TRUE, subject.getIsActive());
assertEquals(100, subject.getVariations().get(0));
assertEquals(200, subject.getVariations().get(1));
}
diff --git a/lib/src/test/java/growthbook/sdk/java/FeatureFetchExceptionTest.java b/lib/src/test/java/growthbook/sdk/java/FeatureFetchExceptionTest.java
index 4ac704b8..073cd377 100644
--- a/lib/src/test/java/growthbook/sdk/java/FeatureFetchExceptionTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/FeatureFetchExceptionTest.java
@@ -1,8 +1,8 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
class FeatureFetchExceptionTest {
@@ -10,65 +10,65 @@ class FeatureFetchExceptionTest {
void exceptionsHaveEnumErrorCodesAndMessages() {
// CONFIGURATION_ERROR
FeatureFetchException configExc = new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR
+ FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR
);
assertEquals(
- FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
- configExc.getErrorCode()
+ FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
+ configExc.getErrorCode()
);
FeatureFetchException configExcWithError = new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
- "config with message"
+ FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
+ "config with message"
);
assertEquals(
- FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
- configExcWithError.getErrorCode()
+ FeatureFetchException.FeatureFetchErrorCode.CONFIGURATION_ERROR,
+ configExcWithError.getErrorCode()
);
assertEquals(
- "CONFIGURATION_ERROR : config with message",
- configExcWithError.getMessage()
+ "CONFIGURATION_ERROR : config with message",
+ configExcWithError.getMessage()
);
// NO_RESPONSE_ERROR
FeatureFetchException noResponseExc = new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR
+ FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR
);
assertEquals(
- FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR,
- noResponseExc.getErrorCode()
+ FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR,
+ noResponseExc.getErrorCode()
);
FeatureFetchException noResponseExcWithMessage = new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR,
- "no response with message"
+ FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR,
+ "no response with message"
);
assertEquals(
- FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR,
- noResponseExcWithMessage.getErrorCode()
+ FeatureFetchException.FeatureFetchErrorCode.NO_RESPONSE_ERROR,
+ noResponseExcWithMessage.getErrorCode()
);
assertEquals(
- "NO_RESPONSE_ERROR : no response with message",
- noResponseExcWithMessage.getMessage()
+ "NO_RESPONSE_ERROR : no response with message",
+ noResponseExcWithMessage.getMessage()
);
// UNKNOWN
FeatureFetchException unknownExc = new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.UNKNOWN
+ FeatureFetchException.FeatureFetchErrorCode.UNKNOWN
);
assertEquals(
- FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
- unknownExc.getErrorCode()
+ FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
+ unknownExc.getErrorCode()
);
FeatureFetchException unknownExcWithMessage = new FeatureFetchException(
- FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
- "unknown with message"
+ FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
+ "unknown with message"
);
assertEquals(
- FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
- unknownExcWithMessage.getErrorCode()
+ FeatureFetchException.FeatureFetchErrorCode.UNKNOWN,
+ unknownExcWithMessage.getErrorCode()
);
assertEquals(
- "UNKNOWN : unknown with message",
- unknownExcWithMessage.getMessage()
+ "UNKNOWN : unknown with message",
+ unknownExcWithMessage.getMessage()
);
}
}
diff --git a/lib/src/test/java/growthbook/sdk/java/FeatureResultTest.java b/lib/src/test/java/growthbook/sdk/java/FeatureResultTest.java
index 5dfd6176..a96fab23 100644
--- a/lib/src/test/java/growthbook/sdk/java/FeatureResultTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/FeatureResultTest.java
@@ -1,8 +1,11 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
class FeatureResultTest {
@Test
@@ -21,7 +24,7 @@ void canBeConstructed() {
@Test
void canBeBuilt() {
- FeatureResult subject = FeatureResult
+ FeatureResult subject = FeatureResult
.builder()
.value("hello")
.experiment(null)
@@ -67,10 +70,10 @@ void featureResultSourceOutputsCorrectlyToJson() {
@Test
void featureResult_isOn_withNonZeroValue_returnsTrue_forIntegers() {
FeatureResult subject = FeatureResult
- .builder()
- .value(1)
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value(1)
+ .source(FeatureResultSource.FORCE)
+ .build();
assertTrue(subject.isOn());
assertFalse(subject.isOff());
@@ -79,10 +82,10 @@ void featureResult_isOn_withNonZeroValue_returnsTrue_forIntegers() {
@Test
void featureResult_isOn_withZeroValue_returnsFalse_forIntegers() {
FeatureResult subject = FeatureResult
- .builder()
- .value(0)
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value(0)
+ .source(FeatureResultSource.FORCE)
+ .build();
assertFalse(subject.isOn());
assertTrue(subject.isOff());
@@ -93,10 +96,10 @@ void featureResult_isOn_withZeroValue_returnsFalse_forIntegers() {
@Test
void featureResult_isOn_withNonZeroValue_returnsTrue_forFloats() {
FeatureResult subject = FeatureResult
- .builder()
- .value(1.0f)
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value(1.0f)
+ .source(FeatureResultSource.FORCE)
+ .build();
assertTrue(subject.isOn());
assertFalse(subject.isOff());
@@ -105,10 +108,10 @@ void featureResult_isOn_withNonZeroValue_returnsTrue_forFloats() {
@Test
void featureResult_isOn_withZeroValue_returnsFalse_forFloats() {
FeatureResult subject = FeatureResult
- .builder()
- .value(0.0f)
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value(0.0f)
+ .source(FeatureResultSource.FORCE)
+ .build();
assertFalse(subject.isOn());
assertTrue(subject.isOff());
@@ -119,10 +122,10 @@ void featureResult_isOn_withZeroValue_returnsFalse_forFloats() {
@Test
void featureResult_isOn_withNonZeroValue_returnsTrue_forDoubles() {
FeatureResult subject = FeatureResult
- .builder()
- .value(1.0)
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value(1.0)
+ .source(FeatureResultSource.FORCE)
+ .build();
assertTrue(subject.isOn());
assertFalse(subject.isOff());
@@ -131,10 +134,10 @@ void featureResult_isOn_withNonZeroValue_returnsTrue_forDoubles() {
@Test
void featureResult_isOn_withZeroValue_returnsFalse_forDoubles() {
FeatureResult subject = FeatureResult
- .builder()
- .value(0)
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value(0)
+ .source(FeatureResultSource.FORCE)
+ .build();
assertFalse(subject.isOn());
assertTrue(subject.isOff());
@@ -145,10 +148,10 @@ void featureResult_isOn_withZeroValue_returnsFalse_forDoubles() {
@Test
void featureResult_isOn_withNonEmptyValue_returnsTrue_forStrings() {
FeatureResult subject = FeatureResult
- .builder()
- .value("hello, world!")
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value("hello, world!")
+ .source(FeatureResultSource.FORCE)
+ .build();
assertTrue(subject.isOn());
assertFalse(subject.isOff());
@@ -157,10 +160,10 @@ void featureResult_isOn_withNonEmptyValue_returnsTrue_forStrings() {
@Test
void featureResult_isOn_withEmptyValue_returnsFalse_forStrings() {
FeatureResult subject = FeatureResult
- .builder()
- .value("")
- .source(FeatureResultSource.FORCE)
- .build();
+ .builder()
+ .value("")
+ .source(FeatureResultSource.FORCE)
+ .build();
assertFalse(subject.isOn());
assertTrue(subject.isOff());
diff --git a/lib/src/test/java/growthbook/sdk/java/FeatureRuleTest.java b/lib/src/test/java/growthbook/sdk/java/FeatureRuleTest.java
index 888eabf5..13d981b7 100644
--- a/lib/src/test/java/growthbook/sdk/java/FeatureRuleTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/FeatureRuleTest.java
@@ -1,12 +1,11 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
+import org.junit.jupiter.api.Test;
+import java.util.ArrayList;
+
class FeatureRuleTest {
@Test
@@ -25,6 +24,7 @@ void canBeConstructed() {
.build();
FeatureRule subject = new FeatureRule(
+ null,
"my-key",
0.5f,
100,
@@ -45,6 +45,7 @@ void canBeConstructed() {
null,
null,
null,
+ null,
null
);
diff --git a/lib/src/test/java/growthbook/sdk/java/FilterTest.java b/lib/src/test/java/growthbook/sdk/java/FilterTest.java
index 5ff1f9e1..f2b3bdb7 100644
--- a/lib/src/test/java/growthbook/sdk/java/FilterTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/FilterTest.java
@@ -1,11 +1,10 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
import java.util.ArrayList;
-import static org.junit.jupiter.api.Assertions.*;
-
class FilterTest {
@Test
void test_canBeBuilt() {
diff --git a/lib/src/test/java/growthbook/sdk/java/GBContextTest.java b/lib/src/test/java/growthbook/sdk/java/GBContextTest.java
index 43ab919d..d48faeae 100644
--- a/lib/src/test/java/growthbook/sdk/java/GBContextTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/GBContextTest.java
@@ -1,16 +1,19 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.verify;
+
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-
import java.util.HashMap;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.verify;
-
class GBContextTest {
private AutoCloseable closeable;
@Mock
@@ -76,7 +79,7 @@ void hasGetterSetterForInitialState() {
.build();
// Initial state OK
- assertTrue(subject.getEnabled());
+ assertEquals(Boolean.TRUE, subject.getEnabled());
assertFalse(subject.getIsQaMode());
assertEquals("http://localhost:3000", subject.getUrl());
@@ -85,7 +88,7 @@ void hasGetterSetterForInitialState() {
subject.setIsQaMode(true);
subject.setUrl("https://docs.growthbook.io/lib/build-your-own");
- assertFalse(subject.getEnabled());
+ assertNotEquals(Boolean.TRUE, subject.getEnabled());
assertTrue(subject.getIsQaMode());
assertEquals("https://docs.growthbook.io/lib/build-your-own", subject.getUrl());
}
@@ -168,6 +171,7 @@ void supportsEncryptedFeaturesUsingBuilder() {
String expectedFeaturesJson = "{\"greeting\":{\"defaultValue\":\"hello\",\"rules\":[{\"condition\":{\"country\":\"france\"},\"force\":\"bonjour\"},{\"condition\":{\"country\":\"mexico\"},\"force\":\"hola\"}]}}";
assertNotNull(subject);
+ assert subject.getFeaturesJson() != null;
assertEquals(expectedFeaturesJson.trim(), subject.getFeaturesJson().trim());
}
diff --git a/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryRefreshingTest.java b/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryRefreshingTest.java
index 9b223fbc..37dcf686 100644
--- a/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryRefreshingTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryRefreshingTest.java
@@ -1,16 +1,23 @@
package growthbook.sdk.java;
-import okhttp3.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import okhttp3.Call;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-
import java.io.IOException;
import java.util.concurrent.TimeUnit;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
-
-
/**
* // TODO: Fix concurrency issue with these tests and re-enable
* These tests use Thread.sleep() to pass time to test the callback logic.
@@ -25,20 +32,20 @@ public class GBFeaturesRepositoryRefreshingTest {
void refreshesFeaturesWhenGetFeaturesCalledAfterCacheExpired() throws IOException, FeatureFetchException, InterruptedException {
Integer ttlSeconds = 5; // cache invalidates every 5 seconds
String fakeResponseJson = "{\n" +
- " \"status\": 200,\n" +
- " \"features\": {},\n" +
- " \"dateUpdated\": \"2023-01-25T00:51:26.772Z\",\n" +
- " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
- "}";
+ " \"status\": 200,\n" +
+ " \"features\": {},\n" +
+ " \"dateUpdated\": \"2023-01-25T00:51:26.772Z\",\n" +
+ " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
+ "}";
String encryptionKey = "o0maZL/O7AphxcbRvaJIzw==";
OkHttpClient mockOkHttpClient = mockHttpClient(fakeResponseJson);
GBFeaturesRepository subject = new GBFeaturesRepository(
- "http://localhost:80",
- "sdk-abc123",
- encryptionKey,
- FeatureRefreshStrategy.STALE_WHILE_REVALIDATE,
- ttlSeconds,
- mockOkHttpClient
+ "http://localhost:80",
+ "sdk-abc123",
+ encryptionKey,
+ FeatureRefreshStrategy.STALE_WHILE_REVALIDATE,
+ ttlSeconds,
+ mockOkHttpClient
);
subject.initialize();
@@ -60,21 +67,21 @@ void refreshesFeaturesWhenGetFeaturesCalledAfterCacheExpired() throws IOExceptio
void doesNotRefreshFeaturesWhenGetFeaturesCalledWithinCacheTime() throws IOException, FeatureFetchException, InterruptedException {
Integer ttlSeconds = 5; // cache invalidates every 5 seconds
String fakeResponseJson = "{\n" +
- " \"status\": 200,\n" +
- " \"features\": {},\n" +
- " \"dateUpdated\": \"2024-01-25T00:51:26.772Z\",\n" +
- " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
- "}";
+ " \"status\": 200,\n" +
+ " \"features\": {},\n" +
+ " \"dateUpdated\": \"2024-01-25T00:51:26.772Z\",\n" +
+ " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
+ "}";
String encryptionKey = "o0maZL/O7AphxcbRvaJIzw==";
OkHttpClient mockOkHttpClient = mockHttpClient(fakeResponseJson);
GBFeaturesRepository subject = new GBFeaturesRepository(
- "http://localhost:80",
- "sdk-abc123",
- encryptionKey,
- FeatureRefreshStrategy.STALE_WHILE_REVALIDATE,
- ttlSeconds,
- mockOkHttpClient
+ "http://localhost:80",
+ "sdk-abc123",
+ encryptionKey,
+ FeatureRefreshStrategy.STALE_WHILE_REVALIDATE,
+ ttlSeconds,
+ mockOkHttpClient
);
subject.initialize();
@@ -95,20 +102,20 @@ void doesNotRefreshFeaturesWhenGetFeaturesCalledWithinCacheTime() throws IOExcep
void refreshesFeaturesWhenGetFeaturesCalledAfterCacheExpired_multipleTimes() throws IOException, FeatureFetchException, InterruptedException {
Integer ttlSeconds = 5; // cache invalidates every 5 seconds
String fakeResponseJson = "{\n" +
- " \"status\": 200,\n" +
- " \"features\": {},\n" +
- " \"dateUpdated\": \"2023-01-25T00:51:26.772Z\",\n" +
- " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
- "}";
+ " \"status\": 200,\n" +
+ " \"features\": {},\n" +
+ " \"dateUpdated\": \"2023-01-25T00:51:26.772Z\",\n" +
+ " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
+ "}";
String encryptionKey = "o0maZL/O7AphxcbRvaJIzw==";
OkHttpClient mockOkHttpClient = mockHttpClient(fakeResponseJson);
GBFeaturesRepository subject = new GBFeaturesRepository(
- "http://localhost:80",
- "sdk-abc123",
- encryptionKey,
- FeatureRefreshStrategy.STALE_WHILE_REVALIDATE,
- ttlSeconds,
- mockOkHttpClient
+ "http://localhost:80",
+ "sdk-abc123",
+ encryptionKey,
+ FeatureRefreshStrategy.STALE_WHILE_REVALIDATE,
+ ttlSeconds,
+ mockOkHttpClient
);
subject.initialize();
@@ -138,6 +145,7 @@ void refreshesFeaturesWhenGetFeaturesCalledAfterCacheExpired_multipleTimes() thr
/**
* Create a mock instance of {@link OkHttpClient}
+ *
* @param serializedBody JSON string response
* @return mock {@link OkHttpClient}
*/
@@ -147,14 +155,14 @@ private static OkHttpClient mockHttpClient(final String serializedBody) throws I
Call remoteCall = mock(Call.class);
Response response = new Response.Builder()
- .request(new Request.Builder().url("http://url.com").build())
- .protocol(Protocol.HTTP_1_1)
- .code(200).message("").body(
- ResponseBody.create(
- serializedBody,
- MediaType.parse("application/json")
- ))
- .build();
+ .request(new Request.Builder().url("http://url.com").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200).message("").body(
+ ResponseBody.create(
+ serializedBody,
+ MediaType.parse("application/json")
+ ))
+ .build();
when(remoteCall.execute()).thenReturn(response);
when(okHttpClient.newCall(any())).thenReturn(remoteCall);
diff --git a/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryTest.java b/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryTest.java
index d06663a4..594c64a0 100644
--- a/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/GBFeaturesRepositoryTest.java
@@ -1,11 +1,7 @@
package growthbook.sdk.java;
-import okhttp3.*;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@@ -15,17 +11,28 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+
class GBFeaturesRepositoryTest {
@Test
void canBeConstructed_withNullEncryptionKey() {
GBFeaturesRepository subject = new GBFeaturesRepository(
- "https://cdn.growthbook.io",
- "java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8",
- null,
- null,
- null,
- null
+ "https://cdn.growthbook.io",
+ "java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8",
+ null,
+ null,
+ null,
+ null
);
assertNotNull(subject);
@@ -36,12 +43,12 @@ void canBeConstructed_withNullEncryptionKey() {
@Test
void canBeConstructed_withEncryptionKey() {
GBFeaturesRepository subject = new GBFeaturesRepository(
- "https://cdn.growthbook.io",
- "sdk-862b5mHcP9XPugqD",
- "BhB1wORFmZLTDjbvstvS8w==",
- null,
- null,
- null
+ "https://cdn.growthbook.io",
+ "sdk-862b5mHcP9XPugqD",
+ "BhB1wORFmZLTDjbvstvS8w==",
+ null,
+ null,
+ null
);
assertNotNull(subject);
@@ -52,10 +59,10 @@ void canBeConstructed_withEncryptionKey() {
@Test
void canBeBuilt_withNullEncryptionKey() {
GBFeaturesRepository subject = GBFeaturesRepository
- .builder()
- .apiHost("https://cdn.growthbook.io")
- .clientKey("java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8")
- .build();
+ .builder()
+ .apiHost("https://cdn.growthbook.io")
+ .clientKey("java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8")
+ .build();
assertNotNull(subject);
assertEquals("https://cdn.growthbook.io/api/features/java_NsrWldWd5bxQJZftGsWKl7R2yD2LtAK8C8EUYh9L8", subject.getFeaturesEndpoint());
@@ -64,11 +71,11 @@ void canBeBuilt_withNullEncryptionKey() {
@Test
void canBeBuilt_withEncryptionKey() {
GBFeaturesRepository subject = GBFeaturesRepository
- .builder()
- .apiHost("https://cdn.growthbook.io")
- .clientKey("sdk-862b5mHcP9XPugqD")
- .encryptionKey("BhB1wORFmZLTDjbvstvS8w==")
- .build();
+ .builder()
+ .apiHost("https://cdn.growthbook.io")
+ .clientKey("sdk-862b5mHcP9XPugqD")
+ .encryptionKey("BhB1wORFmZLTDjbvstvS8w==")
+ .build();
assertNotNull(subject);
assertEquals("https://cdn.growthbook.io/api/features/sdk-862b5mHcP9XPugqD", subject.getFeaturesEndpoint());
@@ -95,12 +102,12 @@ void canFetchUnencryptedFeatures_mockedResponse() throws FeatureFetchException,
OkHttpClient mockOkHttpClient = mockHttpClient(fakeResponseJson);
GBFeaturesRepository subject = new GBFeaturesRepository(
- "http://localhost:80",
- "sdk-abc123",
- null,
- null,
- null,
- mockOkHttpClient
+ "http://localhost:80",
+ "sdk-abc123",
+ null,
+ null,
+ null,
+ mockOkHttpClient
);
subject.initialize();
@@ -131,21 +138,21 @@ void canFetchEncryptedFeatures_real() throws FeatureFetchException {
@Test
void canFetchEncryptedFeatures_mockedResponse() throws IOException, FeatureFetchException {
String fakeResponseJson = "{\n" +
- " \"status\": 200,\n" +
- " \"features\": {},\n" +
- " \"dateUpdated\": \"2023-01-25T00:51:26.772Z\",\n" +
- " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
- "}";
+ " \"status\": 200,\n" +
+ " \"features\": {},\n" +
+ " \"dateUpdated\": \"2023-01-25T00:51:26.772Z\",\n" +
+ " \"encryptedFeatures\": \"jfLnSxjChWcbyHaIF30RNw==.iz8DywkSk4+WhNqnIwvr/PdvAwaRNjN3RE30JeOezGAQ/zZ2yoVyVo4w0nLHYqOje5MbhmL0ssvlH0ojk/BxqdSzXD4Wzo3DXfKV81Nzi1aSdiCMnVAIYEzjPl1IKZC3fl88YDBNV3F6YnR9Lemy9yzT03cvMZ0NZ9t5LZO2xS2MhpPYNcAfAlfxXhBGXj6UFDoNKGAtGKdc/zmJsUVQGLtHmqLspVynnJlPPo9nXG+87bt6SjSfQfySUgHm28hb4VmDhVmCx0N37buolVr3pzjZ1QK+tyMKIV7x4/Gu06k8sm0eU4HjG5DFsPgTR7qDu/N5Nk5UTRpG7aSXTUErxhHSJ7MQaxH/Dp/71zVEicaJ0qZE3oPRnU187QVBfdVLLRbqq2QU7Yu0GyJ1jjuf6TA+759OgifHdm17SX43L94Qe62CMU7JQyAqt7h7XmTTQBG664HYwgHJ0ju/9jySC4KUlRxNsixH1tJfznnEXqxgSozn4J61UprTqcmlxLZ1hZPCcRew3mm9DMAG9+YEiL8MhaIwsw8oVq9GirN1S8G3m/6UxQHxZVraPvMRXpGt5VpzEDJ0Po+phrIAhPuIbNpgb08b6Ej4Xh9XXeOLtIcpuj+gNpc4pR4tqF2IOwET\"\n" +
+ "}";
String encryptionKey = "o0maZL/O7AphxcbRvaJIzw==";
OkHttpClient mockOkHttpClient = mockHttpClient(fakeResponseJson);
GBFeaturesRepository subject = new GBFeaturesRepository(
- "http://localhost:80",
- "abc-123",
- encryptionKey,
- null,
- null,
- mockOkHttpClient
+ "http://localhost:80",
+ "abc-123",
+ encryptionKey,
+ null,
+ null,
+ mockOkHttpClient
);
subject.initialize();
@@ -242,6 +249,7 @@ void testUserAgentHeaders() throws FeatureFetchException {
/**
* Create a mock instance of {@link OkHttpClient}
+ *
* @param serializedBody JSON string response
* @return mock {@link OkHttpClient}
*/
@@ -251,14 +259,14 @@ private static OkHttpClient mockHttpClient(final String serializedBody) throws I
Call remoteCall = mock(Call.class);
Response response = new Response.Builder()
- .request(new Request.Builder().url("http://url.com").build())
- .protocol(Protocol.HTTP_1_1)
- .code(200).message("").body(
- ResponseBody.create(
- serializedBody,
- MediaType.parse("application/json")
- ))
- .build();
+ .request(new Request.Builder().url("http://url.com").build())
+ .protocol(Protocol.HTTP_1_1)
+ .code(200).message("").body(
+ ResponseBody.create(
+ serializedBody,
+ MediaType.parse("application/json")
+ ))
+ .build();
when(remoteCall.execute()).thenReturn(response);
when(okHttpClient.newCall(any())).thenReturn(remoteCall);
diff --git a/lib/src/test/java/growthbook/sdk/java/GrowthBookJsonUtilsTest.java b/lib/src/test/java/growthbook/sdk/java/GrowthBookJsonUtilsTest.java
index c2d49fb0..b5c7f885 100644
--- a/lib/src/test/java/growthbook/sdk/java/GrowthBookJsonUtilsTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/GrowthBookJsonUtilsTest.java
@@ -1,11 +1,11 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
class GrowthBookJsonUtilsTest {
final GrowthBookJsonUtils subject = GrowthBookJsonUtils.getInstance();
diff --git a/lib/src/test/java/growthbook/sdk/java/GrowthBookTest.java b/lib/src/test/java/growthbook/sdk/java/GrowthBookTest.java
index c8153a8b..e3fc485c 100644
--- a/lib/src/test/java/growthbook/sdk/java/GrowthBookTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/GrowthBookTest.java
@@ -3,6 +3,16 @@
*/
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -13,15 +23,11 @@
import growthbook.sdk.java.testhelpers.TestContext;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
-
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
class GrowthBookTest {
final TestCasesJsonHelper helper = TestCasesJsonHelper.getInstance();
@@ -129,8 +135,8 @@ void test_evalFeature() {
System.out.printf("\n Actual result = %s", result);
System.out.printf(
- "\n\n valuePasses = %s, onPasses = %s, offPasses = %s, sourcePasses = %s, hashValuePasses = %s, keyPasses = %s, bucketPasses = %s",
- valuePasses, onPasses, offPasses, sourcePasses, hashValuePasses, keyPasses, bucketPasses
+ "\n\n valuePasses = %s, onPasses = %s, offPasses = %s, sourcePasses = %s, hashValuePasses = %s, keyPasses = %s, bucketPasses = %s",
+ valuePasses, onPasses, offPasses, sourcePasses, hashValuePasses, keyPasses, bucketPasses
);
// System.out.println("\n\n-------------------------------");
@@ -152,10 +158,10 @@ void test_evalFeature_callsFeatureUsageCallback() {
FeatureUsageCallback featureUsageCallback = mock(FeatureUsageCallback.class);
String features = TestCasesJsonHelper.getInstance().getDemoFeaturesJson();
GBContext context = GBContext
- .builder()
- .featuresJson(features)
- .featureUsageCallback(featureUsageCallback)
- .build();
+ .builder()
+ .featuresJson(features)
+ .featureUsageCallback(featureUsageCallback)
+ .build();
GrowthBook subject = new GrowthBook(context);
String value = subject.getFeatureValue("h1-title", "unknown feature key");
@@ -209,7 +215,7 @@ void test_runExperiment() {
for (int i = 0; i < testCases.size(); i++) {
JsonArray itemArray = (JsonArray) testCases.get(i);
- if (itemArray instanceof JsonArray) {
+ if (itemArray != null) {
String testDescription = itemArray.get(0).getAsString();
JsonElement featuresJson = itemArray.get(1).getAsJsonObject().get("features");
@@ -242,8 +248,7 @@ void test_runExperiment() {
JsonObject experimentObject = jsonUtils.gson.fromJson(experimentElement.getAsJsonObject(), JsonObject.class);
JsonElement conditionElement = experimentObject.get("condition");
if (conditionElement != null) {
- String conditionJson = conditionElement.toString();
- experiment.setConditionJson(conditionJson);
+ experiment.setConditionJson(conditionElement);
}
}
@@ -297,8 +302,10 @@ void test_isOn_returns_true() {
.build();
GrowthBook subject = new GrowthBook(context);
- assertTrue(subject.isOn(featureKey));
- assertFalse(subject.isOff(featureKey));
+ FeatureResult feature = subject.evalFeature(featureKey, Object.class);
+
+ assertTrue(feature.isOn());
+ assertFalse(feature.isOff());
}
@Test
@@ -642,12 +649,12 @@ void test_withUrl_getFeatureValue_forcesBooleanValue_true() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Boolean result = subject.getFeatureValue("dark_mode", false);
@@ -662,12 +669,12 @@ void test_withUrl_getFeatureValue_forcesBooleanValue_on() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=on&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Boolean result = subject.getFeatureValue("dark_mode", false);
@@ -682,12 +689,12 @@ void test_withUrl_getFeatureValue_forcesBooleanValue_1() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=1&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Boolean result = subject.getFeatureValue("dark_mode", false);
@@ -702,12 +709,12 @@ void test_withUrl_getFeatureValue_forcesBooleanValue_false() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=false&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Boolean result = subject.getFeatureValue("dark_mode", true);
@@ -722,12 +729,12 @@ void test_withUrl_getFeatureValue_forcesBooleanValue_off() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=off&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Boolean result = subject.getFeatureValue("dark_mode", true);
@@ -742,12 +749,12 @@ void test_withUrl_getFeatureValue_forcesBooleanValue_0() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=0&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Boolean result = subject.getFeatureValue("dark_mode", true);
@@ -762,12 +769,12 @@ void test_withUrl_getFeatureValue_forcesIntegerValue() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=2&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Integer result = subject.getFeatureValue("donut_price", 999);
@@ -781,12 +788,12 @@ void test_withUrl_getFeatureValue_forcesFloatValue() {
String attributes = "{}";
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
Float result = subject.getFeatureValue("donut_price", 9999f);
@@ -800,12 +807,12 @@ void test_withUrl_getFeatureValue_forcesStringValue() {
String attributes = "{}";
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
String result = subject.getFeatureValue("banner_text", "???");
@@ -820,12 +827,12 @@ void test_withUrl_getFeatureValue_forcesJsonValue_Gson() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
MealOrder emptyMealOrder = new MealOrder(MealType.STANDARD, "Donut");
@@ -844,12 +851,12 @@ void test_withUrl_getFeatureValue_jsonValueFromString() {
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
String resultAsString = (String) subject.getFeatureValue("meal_overrides_gluten_free", "{\"meal_type\": \"standard\", \"dessert\": \"Donut\"}");
@@ -867,12 +874,12 @@ void test_withUrl_getFeatureValue_withForcedJsonValue_returnsDefaultValueWhenInv
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
// We try to deserialize an unsupported class from the URL but it cannot deserialize properly, so we get the default value
@@ -924,7 +931,8 @@ public MealOrder(MealType mealType, String dessert) {
}
static class GBTestingFoo {
- @SerializedName("foo") String foo = "FOO!";
+ @SerializedName("foo")
+ String foo = "FOO!";
@Override
public boolean equals(Object obj) {
@@ -945,12 +953,12 @@ void test_withUrl_evaluateFeature_forcesBooleanValue() {
String attributes = "{}";
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
FeatureResult result = subject.evalFeature("dark_mode", Boolean.class);
@@ -965,12 +973,12 @@ void test_withUrl_evaluateFeature_forcesStringValue() {
String attributes = "{}";
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
FeatureResult result = subject.evalFeature("banner_text", String.class);
@@ -985,12 +993,12 @@ void test_withUrl_evaluateFeature_forcesFloatValue() {
String attributes = "{}";
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=3.33&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
FeatureResult result = subject.evalFeature("donut_price", Float.class);
@@ -1005,12 +1013,12 @@ void test_withUrl_evaluateFeature_forcesIntegerValue() {
String attributes = "{}";
String url = "http://localhost:8080/url-feature-force?gb~meal_overrides_gluten_free=%7B%22meal_type%22%3A%20%22gf%22%2C%20%22dessert%22%3A%20%22French%20Vanilla%20Ice%20Cream%22%7D&gb~dark_mode=true&gb~donut_price=4&gb~banner_text=Hello%2C%20everyone!%20I%20hope%20you%20are%20all%20doing%20well!";
GBContext context = GBContext
- .builder()
- .featuresJson(featuresJson)
- .attributesJson(attributes)
- .url(url)
- .allowUrlOverrides(true)
- .build();
+ .builder()
+ .featuresJson(featuresJson)
+ .attributesJson(attributes)
+ .url(url)
+ .allowUrlOverrides(true)
+ .build();
GrowthBook subject = new GrowthBook(context);
FeatureResult result = subject.evalFeature("donut_price", Integer.class);
diff --git a/lib/src/test/java/growthbook/sdk/java/GrowthBookUtilsTest.java b/lib/src/test/java/growthbook/sdk/java/GrowthBookUtilsTest.java
index 6dc0507c..79af4c57 100644
--- a/lib/src/test/java/growthbook/sdk/java/GrowthBookUtilsTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/GrowthBookUtilsTest.java
@@ -1,17 +1,17 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
import com.google.gson.JsonArray;
import com.google.gson.reflect.TypeToken;
import growthbook.sdk.java.testhelpers.TestCasesJsonHelper;
import org.junit.jupiter.api.Test;
-
import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
class GrowthBookUtilsTest {
final TestCasesJsonHelper helper = TestCasesJsonHelper.getInstance();
@@ -29,15 +29,15 @@ void test_hashFowlerNollVoAlgo() {
hnvCases.forEach(jsonElement -> {
JsonArray kv = (JsonArray) jsonElement;
- String seed = kv.get(3).getAsString();
- String input = kv.get(0).getAsString();
+ String seed = kv.get(0).getAsString();
+ String input = kv.get(1).getAsString();
Integer hashVersion = kv.get(2).getAsInt();
Float expected = null;
- if (!kv.get(1).isJsonNull()) {
+ if (!kv.get(3).isJsonNull()) {
// In the case of unsupported hash versions, this method returns null
- expected = kv.get(1).getAsFloat();
+ expected = kv.get(3).getAsFloat();
}
assertEquals(expected, GrowthBookUtils.hash(input, hashVersion, seed));
diff --git a/lib/src/test/java/growthbook/sdk/java/MathUtilsTest.java b/lib/src/test/java/growthbook/sdk/java/MathUtilsTest.java
index 2753071b..0ffb3de7 100644
--- a/lib/src/test/java/growthbook/sdk/java/MathUtilsTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/MathUtilsTest.java
@@ -1,9 +1,8 @@
package growthbook.sdk.java;
-import growthbook.sdk.java.MathUtils;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
class MathUtilsTest {
diff --git a/lib/src/test/java/growthbook/sdk/java/NamespaceTest.java b/lib/src/test/java/growthbook/sdk/java/NamespaceTest.java
index e2487b76..bfebf873 100644
--- a/lib/src/test/java/growthbook/sdk/java/NamespaceTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/NamespaceTest.java
@@ -1,12 +1,11 @@
package growthbook.sdk.java;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import growthbook.sdk.java.Namespace;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
class NamespaceTest {
@Test
void isGsonSerializable() {
diff --git a/lib/src/test/java/growthbook/sdk/java/OperatorTest.java b/lib/src/test/java/growthbook/sdk/java/OperatorTest.java
index bf5f226d..30c26ed8 100644
--- a/lib/src/test/java/growthbook/sdk/java/OperatorTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/OperatorTest.java
@@ -1,8 +1,9 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
class OperatorTest {
@Test
diff --git a/lib/src/test/java/growthbook/sdk/java/StringUtilsTest.java b/lib/src/test/java/growthbook/sdk/java/StringUtilsTest.java
index 085597f5..0dca0d76 100644
--- a/lib/src/test/java/growthbook/sdk/java/StringUtilsTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/StringUtilsTest.java
@@ -1,8 +1,8 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
class StringUtilsTest {
diff --git a/lib/src/test/java/growthbook/sdk/java/TrackDataTest.java b/lib/src/test/java/growthbook/sdk/java/TrackDataTest.java
index 9cd39dd6..728b6539 100644
--- a/lib/src/test/java/growthbook/sdk/java/TrackDataTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/TrackDataTest.java
@@ -1,20 +1,20 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
class TrackDataTest {
@Test
void canBeConstructed() {
Experiment experiment = Experiment.builder()
- .key("my_experiment")
- .force(100)
- .build();
+ .key("my_experiment")
+ .force(100)
+ .build();
ExperimentResult experimentResult = ExperimentResult.builder()
- .inExperiment(true)
- .key("my_experiment")
- .build();
+ .inExperiment(true)
+ .key("my_experiment")
+ .build();
TrackData subject = new TrackData<>(experiment, experimentResult);
diff --git a/lib/src/test/java/growthbook/sdk/java/UrlUtilsTest.java b/lib/src/test/java/growthbook/sdk/java/UrlUtilsTest.java
index ffbbac97..e6d430eb 100644
--- a/lib/src/test/java/growthbook/sdk/java/UrlUtilsTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/UrlUtilsTest.java
@@ -1,15 +1,12 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;
-import static org.junit.jupiter.api.Assertions.*;
-
class UrlUtilsTest {
@Test
diff --git a/lib/src/test/java/growthbook/sdk/java/VariationMetaTest.java b/lib/src/test/java/growthbook/sdk/java/VariationMetaTest.java
index bfc8e321..913f4e5c 100644
--- a/lib/src/test/java/growthbook/sdk/java/VariationMetaTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/VariationMetaTest.java
@@ -1,18 +1,18 @@
package growthbook.sdk.java;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
class VariationMetaTest {
@Test
void canBeBuilt() {
VariationMeta subject = VariationMeta
- .builder()
- .key("my-key")
- .name("my-name")
- .passThrough(true)
- .build();
+ .builder()
+ .key("my-key")
+ .name("my-name")
+ .passThrough(true)
+ .build();
assertEquals("my-key", subject.getKey());
assertEquals("my-name", subject.getName());
diff --git a/lib/src/test/java/growthbook/sdk/java/testhelpers/ITestCasesJsonHelper.java b/lib/src/test/java/growthbook/sdk/java/testhelpers/ITestCasesJsonHelper.java
index f5cf7146..9ed3b4ec 100644
--- a/lib/src/test/java/growthbook/sdk/java/testhelpers/ITestCasesJsonHelper.java
+++ b/lib/src/test/java/growthbook/sdk/java/testhelpers/ITestCasesJsonHelper.java
@@ -5,18 +5,26 @@
interface ITestCasesJsonHelper {
JsonObject getTestCases();
+
JsonArray evalConditionTestCases();
+
JsonArray getHNVTestCases();
+
JsonArray getBucketRangeTestCases();
+
JsonArray featureTestCases();
+
JsonArray runTestCases();
+
JsonArray getChooseVariationTestCases();
+
JsonArray getQueryStringOverrideTestCases();
+
JsonArray getInNamespaceTestCases();
+
JsonArray getEqualWeightsTestCases();
+
JsonArray decryptionTestCases();
- JsonArray versionCompareTestCases_eq();
- JsonArray versionCompareTestCases_lt();
- JsonArray versionCompareTestCases_gt();
+
JsonArray getStickyBucketTestCases();
}
diff --git a/lib/src/test/java/growthbook/sdk/java/testhelpers/SSETestServer.java b/lib/src/test/java/growthbook/sdk/java/testhelpers/SSETestServer.java
index 79845c4e..4e1c8ced 100644
--- a/lib/src/test/java/growthbook/sdk/java/testhelpers/SSETestServer.java
+++ b/lib/src/test/java/growthbook/sdk/java/testhelpers/SSETestServer.java
@@ -3,8 +3,11 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
-import growthbook.sdk.java.*;
-
+import growthbook.sdk.java.FeatureFetchException;
+import growthbook.sdk.java.FeatureRefreshStrategy;
+import growthbook.sdk.java.GBContext;
+import growthbook.sdk.java.GBFeaturesRepository;
+import growthbook.sdk.java.GrowthBook;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
@@ -16,10 +19,10 @@ public class SSETestServer {
public static void main(String[] args) throws IOException, FeatureFetchException {
// Unencrypted
GBFeaturesRepository featuresRepository = GBFeaturesRepository.builder()
- .apiHost("https://cdn.growthbook.io")
- .clientKey("sdk-pGmC6LrsiUoEUcpZ")
- .refreshStrategy(FeatureRefreshStrategy.SERVER_SENT_EVENTS)
- .build();
+ .apiHost("https://cdn.growthbook.io")
+ .clientKey("sdk-pGmC6LrsiUoEUcpZ")
+ .refreshStrategy(FeatureRefreshStrategy.SERVER_SENT_EVENTS)
+ .build();
// Encrypted
// GBFeaturesRepository featuresRepository = GBFeaturesRepository.builder()
@@ -50,8 +53,8 @@ private static class TestServerHandler implements HttpHandler {
public void handle(HttpExchange exchange) throws IOException {
// Setup GrowthBook SDK
GBContext context = GBContext.builder()
- .featuresJson(featuresRepository.getFeaturesJson())
- .build();
+ .featuresJson(featuresRepository.getFeaturesJson())
+ .build();
GrowthBook growthBook = new GrowthBook(context);
// Get a feature value
diff --git a/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelper.java b/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelper.java
index 9e99ddd4..b3950e2c 100644
--- a/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelper.java
+++ b/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelper.java
@@ -4,7 +4,6 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.nio.file.Path;
@@ -65,21 +64,6 @@ public JsonArray decryptionTestCases() {
return this.testCases.get("decrypt").getAsJsonArray();
}
- @Override
- public JsonArray versionCompareTestCases_eq() {
- return this.testCases.get("versionCompare").getAsJsonObject().get("eq").getAsJsonArray();
- }
-
- @Override
- public JsonArray versionCompareTestCases_lt() {
- return this.testCases.get("versionCompare").getAsJsonObject().get("lt").getAsJsonArray();
- }
-
- @Override
- public JsonArray versionCompareTestCases_gt() {
- return this.testCases.get("versionCompare").getAsJsonObject().get("gt").getAsJsonArray();
- }
-
@Override
public JsonArray getQueryStringOverrideTestCases() {
return this.testCases.get("getQueryStringOverride").getAsJsonArray();
@@ -140,7 +124,7 @@ private String initializeDemoFeaturesFromFile() {
}
private String getResourceDirectoryPath() {
- Path resourceDirectory = Paths.get("src","test","resources");
+ Path resourceDirectory = Paths.get("src", "test", "resources");
String absolutePath = resourceDirectory.toFile().getAbsolutePath();
System.out.println(absolutePath);
return absolutePath;
diff --git a/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelperTest.java b/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelperTest.java
index 60d7b97f..f9cfd18b 100644
--- a/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelperTest.java
+++ b/lib/src/test/java/growthbook/sdk/java/testhelpers/TestCasesJsonHelperTest.java
@@ -1,11 +1,11 @@
package growthbook.sdk.java.testhelpers;
-import com.google.gson.JsonObject;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import com.google.gson.JsonObject;
+import org.junit.jupiter.api.Test;
+
class TestCasesJsonHelperTest {
@Test
@@ -21,6 +21,6 @@ void getTestCases_returnsTestCasesAsJson() {
JsonObject testCases = TestCasesJsonHelper.getInstance().getTestCases();
assertNotNull(testCases);
- assertEquals("0.5.4", testCases.get("specVersion").getAsString());
+ assertEquals("0.6.0", testCases.get("specVersion").getAsString());
}
}
diff --git a/lib/src/test/resources/test-cases.json b/lib/src/test/resources/test-cases.json
index 98ef1231..92bab9b8 100644
--- a/lib/src/test/resources/test-cases.json
+++ b/lib/src/test/resources/test-cases.json
@@ -1,5 +1,5 @@
{
- "specVersion": "0.5.4",
+ "specVersion": "0.6.0",
"evalCondition": [
[
"$not - pass",
@@ -2019,135 +2019,781 @@
"v": "1.2.3-alpha"
},
true
- ]
- ],
- "versionCompare": {
- "lt": [
- ["0.9.99", "1.0.0", true],
- ["0.9.0", "0.10.0", true],
- ["1.0.0-0.0", "1.0.0-0.0.0", true],
- ["1.0.0-9999", "1.0.0--", true],
- ["1.0.0-99", "1.0.0-100", true],
- ["1.0.0-alpha", "1.0.0-alpha.1", true],
- ["1.0.0-alpha.1", "1.0.0-alpha.beta", true],
- ["1.0.0-alpha.beta", "1.0.0-beta", true],
- ["1.0.0-beta", "1.0.0-beta.2", true],
- ["1.0.0-beta.2", "1.0.0-beta.11", true],
- ["1.0.0-beta.11", "1.0.0-rc.1", true],
- ["1.0.0-rc.1", "1.0.0", true],
- ["1.0.0-0", "1.0.0--1", true],
- ["1.0.0-0", "1.0.0-1", true],
- ["1.0.0-1.0", "1.0.0-1.-1", true]
- ],
- "gt": [
- ["0.0.0", "0.0.0-foo", true],
- ["0.0.1", "0.0.0", true],
- ["1.0.0", "0.9.9", true],
- ["0.10.0", "0.9.0", true],
- ["0.99.0", "0.10.0", true],
- ["2.0.0", "1.2.3", true],
- ["v0.0.0", "0.0.0-foo", true],
- ["v0.0.1", "0.0.0", true],
- ["v1.0.0", "0.9.9", true],
- ["v0.10.0", "0.9.0", true],
- ["v0.99.0", "0.10.0", true],
- ["v2.0.0", "1.2.3", true],
- ["0.0.0", "v0.0.0-foo", true],
- ["0.0.1", "v0.0.0", true],
- ["1.0.0", "v0.9.9", true],
- ["0.10.0", "v0.9.0", true],
- ["0.99.0", "v0.10.0", true],
- ["2.0.0", "v1.2.3", true],
- ["1.2.3", "1.2.3-asdf", true],
- ["1.2.3", "1.2.3-4", true],
- ["1.2.3", "1.2.3-4-foo", true],
- ["1.2.3-5-foo", "1.2.3-5", true],
- ["1.2.3-5", "1.2.3-4", true],
- ["1.2.3-5-foo", "1.2.3-5-Foo", true],
- ["3.0.0", "2.7.2+asdf", true],
- ["1.2.3-a.10", "1.2.3-a.5", true],
- ["1.2.3-a.b", "1.2.3-a.5", true],
- ["1.2.3-a.b", "1.2.3-a", true],
- ["1.2.3-a.b.c", "1.2.3-a.b.c.d", false],
- ["1.2.3-a.b.c.10.d.5", "1.2.3-a.b.c.5.d.100", true],
- ["1.2.3-r2", "1.2.3-r100", true],
- ["1.2.3-r100", "1.2.3-R2", true],
- ["a.b.c.d.e.f", "1.2.3", true],
- ["10.0.0", "9.0.0", true],
- ["10000.0.0", "9999.0.0", true]
- ],
- "eq": [
- ["1.2.3", "1.2.3", true],
- ["1.2.3", "v1.2.3", true],
- ["1.2.3-0", "v1.2.3-0", true],
- ["1.2.3-1", "1.2.3-1", true],
- ["1.2.3-1", "v1.2.3-1", true],
- ["1.2.3-beta", "1.2.3-beta", true],
- ["1.2.3-beta", "v1.2.3-beta", true],
- ["1.2.3-beta+build", "1.2.3-beta+otherbuild", true],
- ["1.2.3-beta+build", "v1.2.3-beta+otherbuild", true],
- ["1-2-3", "1.2.3", true],
- ["1-2-3", "1-2.3+build99", true],
- ["1-2-3", "v1.2.3", true],
- ["1.2.3.4", "1.2.3-4", true]
- ]
- },
- "hash": [
+ ],
[
- "a",
- 0.22,
- 1,
- ""
+ "version 0.9.99 < 1.0.0",
+ {
+ "version": {
+ "$vlt": "1.0.0"
+ }
+ },
+ {
+ "version": "0.9.99"
+ },
+ true
],
[
- "b",
- 0.077,
- 1,
- ""
+ "version 0.9.0 < 0.10.0",
+ {
+ "version": {
+ "$vlt": "0.10.0"
+ }
+ },
+ {
+ "version": "0.9.0"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-0.0 < 1.0.0-0.0.0",
+ {
+ "version": {
+ "$vlt": "1.0.0-0.0.0"
+ }
+ },
+ {
+ "version": "1.0.0-0.0"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-9999 < 1.0.0--",
+ {
+ "version": {
+ "$vlt": "1.0.0--"
+ }
+ },
+ {
+ "version": "1.0.0-9999"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-99 < 1.0.0-100",
+ {
+ "version": {
+ "$vlt": "1.0.0-100"
+ }
+ },
+ {
+ "version": "1.0.0-99"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-alpha < 1.0.0-alpha.1",
+ {
+ "version": {
+ "$vlt": "1.0.0-alpha.1"
+ }
+ },
+ {
+ "version": "1.0.0-alpha"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-alpha.1 < 1.0.0-alpha.beta",
+ {
+ "version": {
+ "$vlt": "1.0.0-alpha.beta"
+ }
+ },
+ {
+ "version": "1.0.0-alpha.1"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-alpha.beta < 1.0.0-beta",
+ {
+ "version": {
+ "$vlt": "1.0.0-beta"
+ }
+ },
+ {
+ "version": "1.0.0-alpha.beta"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-beta < 1.0.0-beta.2",
+ {
+ "version": {
+ "$vlt": "1.0.0-beta.2"
+ }
+ },
+ {
+ "version": "1.0.0-beta"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-beta.2 < 1.0.0-beta.11",
+ {
+ "version": {
+ "$vlt": "1.0.0-beta.11"
+ }
+ },
+ {
+ "version": "1.0.0-beta.2"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-beta.11 < 1.0.0-rc.1",
+ {
+ "version": {
+ "$vlt": "1.0.0-rc.1"
+ }
+ },
+ {
+ "version": "1.0.0-beta.11"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-rc.1 < 1.0.0",
+ {
+ "version": {
+ "$vlt": "1.0.0"
+ }
+ },
+ {
+ "version": "1.0.0-rc.1"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-0 < 1.0.0--1",
+ {
+ "version": {
+ "$vlt": "1.0.0--1"
+ }
+ },
+ {
+ "version": "1.0.0-0"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-0 < 1.0.0-1",
+ {
+ "version": {
+ "$vlt": "1.0.0-1"
+ }
+ },
+ {
+ "version": "1.0.0-0"
+ },
+ true
+ ],
+ [
+ "version 1.0.0-1.0 < 1.0.0-1.-1",
+ {
+ "version": {
+ "$vlt": "1.0.0-1.-1"
+ }
+ },
+ {
+ "version": "1.0.0-1.0"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-a.b.c < 1.2.3-a.b.c.d",
+ {
+ "version": {
+ "$vlt": "1.2.3-a.b.c.d"
+ }
+ },
+ {
+ "version": "1.2.3-a.b.c"
+ },
+ true
+ ],
+ [
+ "version 0.0.0 > 0.0.0-foo",
+ {
+ "version": {
+ "$vgt": "0.0.0-foo"
+ }
+ },
+ {
+ "version": "0.0.0"
+ },
+ true
+ ],
+ [
+ "version 0.0.1 > 0.0.0",
+ {
+ "version": {
+ "$vgt": "0.0.0"
+ }
+ },
+ {
+ "version": "0.0.1"
+ },
+ true
+ ],
+ [
+ "version 1.0.0 > 0.9.9",
+ {
+ "version": {
+ "$vgt": "0.9.9"
+ }
+ },
+ {
+ "version": "1.0.0"
+ },
+ true
+ ],
+ [
+ "version 0.10.0 > 0.9.0",
+ {
+ "version": {
+ "$vgt": "0.9.0"
+ }
+ },
+ {
+ "version": "0.10.0"
+ },
+ true
+ ],
+ [
+ "version 0.99.0 > 0.10.0",
+ {
+ "version": {
+ "$vgt": "0.10.0"
+ }
+ },
+ {
+ "version": "0.99.0"
+ },
+ true
+ ],
+ [
+ "version 2.0.0 > 1.2.3",
+ {
+ "version": {
+ "$vgt": "1.2.3"
+ }
+ },
+ {
+ "version": "2.0.0"
+ },
+ true
+ ],
+ [
+ "version v0.0.0 > 0.0.0-foo",
+ {
+ "version": {
+ "$vgt": "0.0.0-foo"
+ }
+ },
+ {
+ "version": "v0.0.0"
+ },
+ true
+ ],
+ [
+ "version v0.0.1 > 0.0.0",
+ {
+ "version": {
+ "$vgt": "0.0.0"
+ }
+ },
+ {
+ "version": "v0.0.1"
+ },
+ true
+ ],
+ [
+ "version v1.0.0 > 0.9.9",
+ {
+ "version": {
+ "$vgt": "0.9.9"
+ }
+ },
+ {
+ "version": "v1.0.0"
+ },
+ true
+ ],
+ [
+ "version v0.10.0 > 0.9.0",
+ {
+ "version": {
+ "$vgt": "0.9.0"
+ }
+ },
+ {
+ "version": "v0.10.0"
+ },
+ true
+ ],
+ [
+ "version v0.99.0 > 0.10.0",
+ {
+ "version": {
+ "$vgt": "0.10.0"
+ }
+ },
+ {
+ "version": "v0.99.0"
+ },
+ true
+ ],
+ [
+ "version v2.0.0 > 1.2.3",
+ {
+ "version": {
+ "$vgt": "1.2.3"
+ }
+ },
+ {
+ "version": "v2.0.0"
+ },
+ true
+ ],
+ [
+ "version 0.0.0 > v0.0.0-foo",
+ {
+ "version": {
+ "$vgt": "v0.0.0-foo"
+ }
+ },
+ {
+ "version": "0.0.0"
+ },
+ true
+ ],
+ [
+ "version 0.0.1 > v0.0.0",
+ {
+ "version": {
+ "$vgt": "v0.0.0"
+ }
+ },
+ {
+ "version": "0.0.1"
+ },
+ true
+ ],
+ [
+ "version 1.0.0 > v0.9.9",
+ {
+ "version": {
+ "$vgt": "v0.9.9"
+ }
+ },
+ {
+ "version": "1.0.0"
+ },
+ true
+ ],
+ [
+ "version 0.10.0 > v0.9.0",
+ {
+ "version": {
+ "$vgt": "v0.9.0"
+ }
+ },
+ {
+ "version": "0.10.0"
+ },
+ true
+ ],
+ [
+ "version 0.99.0 > v0.10.0",
+ {
+ "version": {
+ "$vgt": "v0.10.0"
+ }
+ },
+ {
+ "version": "0.99.0"
+ },
+ true
+ ],
+ [
+ "version 2.0.0 > v1.2.3",
+ {
+ "version": {
+ "$vgt": "v1.2.3"
+ }
+ },
+ {
+ "version": "2.0.0"
+ },
+ true
+ ],
+ [
+ "version 1.2.3 > 1.2.3-asdf",
+ {
+ "version": {
+ "$vgt": "1.2.3-asdf"
+ }
+ },
+ {
+ "version": "1.2.3"
+ },
+ true
+ ],
+ [
+ "version 1.2.3 > 1.2.3-4",
+ {
+ "version": {
+ "$vgt": "1.2.3-4"
+ }
+ },
+ {
+ "version": "1.2.3"
+ },
+ true
+ ],
+ [
+ "version 1.2.3 > 1.2.3-4-foo",
+ {
+ "version": {
+ "$vgt": "1.2.3-4-foo"
+ }
+ },
+ {
+ "version": "1.2.3"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-5-foo > 1.2.3-5",
+ {
+ "version": {
+ "$vgt": "1.2.3-5"
+ }
+ },
+ {
+ "version": "1.2.3-5-foo"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-5 > 1.2.3-4",
+ {
+ "version": {
+ "$vgt": "1.2.3-4"
+ }
+ },
+ {
+ "version": "1.2.3-5"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-5-foo > 1.2.3-5-Foo",
+ {
+ "version": {
+ "$vgt": "1.2.3-5-Foo"
+ }
+ },
+ {
+ "version": "1.2.3-5-foo"
+ },
+ true
+ ],
+ [
+ "version 3.0.0 > 2.7.2+asdf",
+ {
+ "version": {
+ "$vgt": "2.7.2+asdf"
+ }
+ },
+ {
+ "version": "3.0.0"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-a.10 > 1.2.3-a.5",
+ {
+ "version": {
+ "$vgt": "1.2.3-a.5"
+ }
+ },
+ {
+ "version": "1.2.3-a.10"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-a.b > 1.2.3-a.5",
+ {
+ "version": {
+ "$vgt": "1.2.3-a.5"
+ }
+ },
+ {
+ "version": "1.2.3-a.b"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-a.b > 1.2.3-a",
+ {
+ "version": {
+ "$vgt": "1.2.3-a"
+ }
+ },
+ {
+ "version": "1.2.3-a.b"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-a.b.c.10.d.5 > 1.2.3-a.b.c.5.d.100",
+ {
+ "version": {
+ "$vgt": "1.2.3-a.b.c.5.d.100"
+ }
+ },
+ {
+ "version": "1.2.3-a.b.c.10.d.5"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-r2 > 1.2.3-r100",
+ {
+ "version": {
+ "$vgt": "1.2.3-r100"
+ }
+ },
+ {
+ "version": "1.2.3-r2"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-r100 > 1.2.3-R2",
+ {
+ "version": {
+ "$vgt": "1.2.3-R2"
+ }
+ },
+ {
+ "version": "1.2.3-r100"
+ },
+ true
+ ],
+ [
+ "version a.b.c.d.e.f > 1.2.3",
+ {
+ "version": {
+ "$vgt": "1.2.3"
+ }
+ },
+ {
+ "version": "a.b.c.d.e.f"
+ },
+ true
+ ],
+ [
+ "version 10.0.0 > 9.0.0",
+ {
+ "version": {
+ "$vgt": "9.0.0"
+ }
+ },
+ {
+ "version": "10.0.0"
+ },
+ true
+ ],
+ [
+ "version 10000.0.0 > 9999.0.0",
+ {
+ "version": {
+ "$vgt": "9999.0.0"
+ }
+ },
+ {
+ "version": "10000.0.0"
+ },
+ true
+ ],
+ [
+ "version 1.2.3 == 1.2.3",
+ {
+ "version": {
+ "$veq": "1.2.3"
+ }
+ },
+ {
+ "version": "1.2.3"
+ },
+ true
+ ],
+ [
+ "version 1.2.3 == v1.2.3",
+ {
+ "version": {
+ "$veq": "v1.2.3"
+ }
+ },
+ {
+ "version": "1.2.3"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-0 == v1.2.3-0",
+ {
+ "version": {
+ "$veq": "v1.2.3-0"
+ }
+ },
+ {
+ "version": "1.2.3-0"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-1 == 1.2.3-1",
+ {
+ "version": {
+ "$veq": "1.2.3-1"
+ }
+ },
+ {
+ "version": "1.2.3-1"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-1 == v1.2.3-1",
+ {
+ "version": {
+ "$veq": "v1.2.3-1"
+ }
+ },
+ {
+ "version": "1.2.3-1"
+ },
+ true
+ ],
+ [
+ "version 1.2.3-beta == 1.2.3-beta",
+ {
+ "version": {
+ "$veq": "1.2.3-beta"
+ }
+ },
+ {
+ "version": "1.2.3-beta"
+ },
+ true
],
[
- "ab",
- 0.946,
- 1,
- ""
+ "version 1.2.3-beta == v1.2.3-beta",
+ {
+ "version": {
+ "$veq": "v1.2.3-beta"
+ }
+ },
+ {
+ "version": "1.2.3-beta"
+ },
+ true
],
[
- "def",
- 0.652,
- 1,
- ""
+ "version 1.2.3-beta+build == 1.2.3-beta+otherbuild",
+ {
+ "version": {
+ "$veq": "1.2.3-beta+otherbuild"
+ }
+ },
+ {
+ "version": "1.2.3-beta+build"
+ },
+ true
],
[
- "8952klfjas09ujkasdf",
- 0.549,
- 1,
- ""
+ "version 1.2.3-beta+build == v1.2.3-beta+otherbuild",
+ {
+ "version": {
+ "$veq": "v1.2.3-beta+otherbuild"
+ }
+ },
+ {
+ "version": "1.2.3-beta+build"
+ },
+ true
],
[
- "123",
- 0.011,
- 1,
- ""
+ "version 1-2-3 == 1.2.3",
+ {
+ "version": {
+ "$veq": "1.2.3"
+ }
+ },
+ {
+ "version": "1-2-3"
+ },
+ true
],
[
- "___)((*\":&",
- 0.563,
- 1,
- ""
+ "version 1-2-3 == 1-2.3+build99",
+ {
+ "version": {
+ "$veq": "1-2.3+build99"
+ }
+ },
+ {
+ "version": "1-2-3"
+ },
+ true
],
[
- "a",
- 0.0505,
- 2,
- "seed"
+ "version 1-2-3 == v1.2.3",
+ {
+ "version": {
+ "$veq": "v1.2.3"
+ }
+ },
+ {
+ "version": "1-2-3"
+ },
+ true
],
[
- "def",
- null,
- 99,
- "abc"
+ "version 1.2.3.4 == 1.2.3-4",
+ {
+ "version": {
+ "$veq": "1.2.3-4"
+ }
+ },
+ {
+ "version": "1.2.3.4"
+ },
+ true
]
],
+ "hash": [
+ ["", "a", 1, 0.22],
+ ["", "b", 1, 0.077],
+ ["b", "a", 1, 0.946],
+ ["ef", "d", 1, 0.652],
+ ["asdf", "8952klfjas09ujk", 1, 0.549],
+ ["", "123", 1, 0.011],
+ ["", "___)((*\":&", 1, 0.563],
+ ["seed", "a", 2, 0.0505],
+ ["seed", "b", 2, 0.2696],
+ ["foo", "ab", 2, 0.2575],
+ ["foo", "def", 2, 0.2019],
+ ["89123klj", "8952klfjas09ujkasdf", 2, 0.124],
+ ["90850943850283058242805", "123", 2, 0.7516],
+ ["()**(%$##$%#$#", "___)((*\":&", 2, 0.0128],
+ ["abc", "def", 99, null]
+ ],
"getBucketRange": [
[
"normal 50/50",
@@ -2451,6 +3097,33 @@
"source": "defaultValue"
}
],
+ [
+ "force rules - coverage 0",
+ {
+ "attributes": {
+ "id": "d0bc0a5a"
+ },
+ "features": {
+ "8d156": {
+ "defaultValue": 0,
+ "rules": [
+ {
+ "force": 1,
+ "coverage": 0,
+ "hashVersion": 2
+ }
+ ]
+ }
+ }
+ },
+ "8d156",
+ {
+ "value": 0,
+ "on": false,
+ "off": true,
+ "source": "defaultValue"
+ }
+ ],
[
"force rules - condition pass",
{
@@ -3323,8 +3996,93 @@
}
}
],
-
-
+ [
+ "Prerequisite flag off, block dependent flag",
+ {
+ "attributes": {
+ "id": "123",
+ "memberType": "basic",
+ "country": "Canada"
+ },
+ "features": {
+ "parentFlag": {
+ "defaultValue": "silver",
+ "rules": [
+ {
+ "condition": { "country": "Canada" },
+ "force": "red"
+ },
+ {
+ "condition": { "country": { "$in": ["USA", "Mexico"] } },
+ "force": "green"
+ }
+ ]
+ },
+ "childFlag": {
+ "defaultValue": "default",
+ "rules": [
+ {
+ "parentConditions": [
+ {
+ "id": "parentFlag",
+ "condition": { "value": "green" },
+ "gate": true
+ }
+ ]
+ },
+ {
+ "condition": { "memberType": "basic" },
+ "force": "success"
+ }
+ ]
+ }
+ }
+ },
+ "childFlag",
+ {
+ "value": null,
+ "on": false,
+ "off": true,
+ "source": "prerequisite"
+ }
+ ],
+ [
+ "Prerequisite flag missing, block dependent flag",
+ {
+ "attributes": {
+ "id": "123",
+ "memberType": "basic",
+ "country": "Canada"
+ },
+ "features": {
+ "childFlag": {
+ "defaultValue": "default",
+ "rules": [
+ {
+ "parentConditions": [
+ {
+ "id": "parentFlag",
+ "condition": { "value": "green" },
+ "gate": true
+ }
+ ]
+ },
+ {
+ "condition": { "memberType": "basic" },
+ "force": "success"
+ }
+ ]
+ }
+ }
+ },
+ "childFlag",
+ {
+ "value": null,
+ "on": false,
+ "off": true,
+ "source": "prerequisite"
+ }
+ ],
[
"Prerequisite flag on, evaluate dependent flag",
{
@@ -3562,6 +4320,51 @@
"off": false,
"source": "force"
}
+ ],
+ [
+ "Prerequisite cycle detected, break",
+ {
+ "attributes": {
+ "id": "123"
+ },
+ "features": {
+ "flag1": {
+ "defaultValue": true,
+ "rules": [
+ {
+ "parentConditions": [
+ {
+ "id": "flag2",
+ "condition": { "value": true },
+ "gate": true
+ }
+ ]
+ }
+ ]
+ },
+ "flag2": {
+ "defaultValue": true,
+ "rules": [
+ {
+ "parentConditions": [
+ {
+ "id": "flag1",
+ "condition": { "value": true },
+ "gate": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "flag1",
+ {
+ "value": null,
+ "on": false,
+ "off": true,
+ "source": "cyclicPrerequisite"
+ }
]
],
"run": [
@@ -3996,6 +4799,22 @@
false,
false
],
+ [
+ "querystring force",
+ {
+ "attributes": {
+ "id": "1"
+ },
+ "url": "http://example.com?forced-test-qs=1#someanchor"
+ },
+ {
+ "key": "forced-test-qs",
+ "variations": [0, 1]
+ },
+ 1,
+ true,
+ false
+ ],
[
"run active experiments",
{
@@ -4028,6 +4847,23 @@
false,
false
],
+ [
+ "querystring force with inactive",
+ {
+ "attributes": {
+ "id": "1"
+ },
+ "url": "http://example.com/?my-test=1"
+ },
+ {
+ "key": "my-test",
+ "active": false,
+ "variations": [0, 1]
+ },
+ 1,
+ true,
+ false
+ ],
[
"coverage take precendence over forced",
{
@@ -4385,6 +5221,32 @@
1,
true,
true
+ ],
+ [
+ "Prerequisite condition fails",
+ {
+ "attributes": { "id": "1" },
+ "features": {
+ "parentFlag": {
+ "defaultValue": false
+ }
+ }
+ },
+ {
+ "key": "my-test",
+ "variations": [0, 1],
+ "parentConditions": [
+ {
+ "id": "parentFlag",
+ "condition": {
+ "value": true
+ }
+ }
+ ]
+ },
+ 0,
+ false,
+ false
]
],
"chooseVariation": [