From 2cd2fa38b537ea56711a0c3216644e56fdc9f1c7 Mon Sep 17 00:00:00 2001
From: petruki <31597636+petruki@users.noreply.github.com>
Date: Wed, 6 Nov 2024 17:13:29 -0800
Subject: [PATCH] Closes #309 - added Criteria evaluation for the test feature
---
README.md | 17 ++++
pom.xml | 16 ++--
.../client/SwitcherContextBase.java | 36 ++++++--
.../switcherapi/client/SwitcherExecutor.java | 13 ++-
.../client/model/AsyncSwitcher.java | 16 ++--
.../switcherapi/client/model/Switcher.java | 12 +--
.../client/model/SwitcherBuilder.java | 13 ++-
.../model/response/CriteriaResponse.java | 39 ++++++--
.../switcherapi/client/test/SwitcherTest.java | 2 +
.../client/test/SwitcherTestExtension.java | 13 ++-
.../client/test/SwitcherTestTemplate.java | 17 +++-
.../client/test/SwitcherTestValue.java | 2 +
.../client/test/SwitcherTestWhen.java | 11 +++
.../client/utils/SwitcherUtils.java | 50 ----------
.../client/SwitcherBypassTest.java | 91 +++++++++++++++++++
.../playground/ClientPlayground.java | 4 +-
src/test/resources/switcherapi.properties | 2 +-
17 files changed, 252 insertions(+), 102 deletions(-)
create mode 100644 src/main/java/com/github/switcherapi/client/test/SwitcherTestWhen.java
diff --git a/README.md b/README.md
index 189ae63a..a2072109 100644
--- a/README.md
+++ b/README.md
@@ -270,6 +270,15 @@ SwitcherExecutor.forget(FEATURE01);
switcher.isItOn(); // Now, it's going to return the result retrieved from the API or the Snapshot file
```
+For more complex scenarios where you need to test features based on specific inputs, you can use test conditions.
+
+```java
+Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01).checkValue("My value").build();
+
+SwitcherExecutor.assume(FEATURE01, true).when(StrategyValidator.VALUE, "My value");
+switcher.isItOn(); // 'true'
+
+```
## Smoke test
Validate Switcher Keys on your testing pipelines before deploying a change.
Switcher Keys may not be configured correctly and can cause your code to have undesired results.
@@ -314,3 +323,11 @@ void testMyFeature() {
assertTrue(instance.myFeature());
}
```
+
+Using SwitcherTestWhen to define a specific condition for the test:
+```java
+@SwitcherTest(key = MY_SWITCHER, when = @SwitcherTestWhen(value = "My value"))
+void testMyFeature() {
+ assertTrue(instance.myFeature());
+}
+```
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 90e1ab6b..071f2c1a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
com.github.switcherapi
switcher-client
jar
- 2.1.1-SNAPSHOT
+ 2.2.0-SNAPSHOT
Switcher Client
Switcher Client SDK for working with Switcher API
@@ -52,9 +52,9 @@
- 3.1.8
- 3.1.8
- 3.1.8
+ 3.1.9
+ 3.1.9
+ 3.1.9
2.11.0
@@ -64,7 +64,7 @@
5.0.0-alpha.14
- 5.11.1
+ 5.11.3
2.2.0
@@ -173,19 +173,19 @@
com.fasterxml.jackson.core
jackson-annotations
- 2.17.0
+ 2.17.3
com.fasterxml.jackson.core
jackson-databind
- 2.17.0
+ 2.17.3
com.fasterxml.jackson.module
jackson-module-jakarta-xmlbind-annotations
- 2.17.0
+ 2.17.3
diff --git a/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java b/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java
index f8a477fe..055d7f4c 100644
--- a/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java
+++ b/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java
@@ -10,6 +10,7 @@
import com.github.switcherapi.client.service.local.SwitcherLocalService;
import com.github.switcherapi.client.service.remote.SwitcherRemoteService;
import com.github.switcherapi.client.utils.SnapshotEventHandler;
+import com.github.switcherapi.client.utils.SnapshotWatcher;
import com.github.switcherapi.client.utils.SwitcherUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@@ -19,6 +20,7 @@
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -67,6 +69,8 @@ public abstract class SwitcherContextBase {
protected static Map switchers;
protected static SwitcherExecutor instance;
private static ScheduledExecutorService scheduledExecutorService;
+ private static ExecutorService watcherExecutorService;
+ private static SnapshotWatcher watcher;
protected SwitcherContextBase() {
throw new IllegalStateException("Context class cannot be instantiated");
@@ -89,7 +93,7 @@ protected SwitcherContextBase() {
* @param contextFilename to load properties from
*/
public static void loadProperties(String contextFilename) {
- try (InputStream input = SwitcherContext.class
+ try (InputStream input = SwitcherContextBase.class
.getClassLoader().getResourceAsStream(String.format("%s.properties", contextFilename))) {
Properties prop = new Properties();
@@ -190,7 +194,7 @@ public static boolean scheduleSnapshotAutoUpdate(String intervalValue, SnapshotC
}
};
- initExecutorService();
+ initSnapshotExecutorService();
scheduledExecutorService.scheduleAtFixedRate(runnableSnapshotValidate, 0, interval, TimeUnit.MILLISECONDS);
return true;
}
@@ -209,7 +213,7 @@ public static boolean scheduleSnapshotAutoUpdate(String intervalValue) {
/**
* Configure Executor Service for Snapshot Update Worker
*/
- private static void initExecutorService() {
+ private static void initSnapshotExecutorService() {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(r -> {
Thread thread = new Thread(r);
thread.setName(WorkerName.SNAPSHOT_UPDATE_WORKER.toString());
@@ -217,6 +221,18 @@ private static void initExecutorService() {
return thread;
});
}
+
+ /**
+ * Configure Executor Service for Snapshot Watch Worker
+ */
+ private static void initWatcherExecutorService() {
+ watcherExecutorService = Executors.newSingleThreadExecutor(r -> {
+ Thread thread = new Thread(r);
+ thread.setName(WorkerName.SNAPSHOT_WATCH_WORKER.toString());
+ thread.setDaemon(true);
+ return thread;
+ });
+ }
/**
* Return a ready-to-use Switcher that will invoke the criteria configured into the Switcher API or Snapshot
@@ -293,8 +309,12 @@ public static void watchSnapshot(SnapshotEventHandler handler) {
throw new SwitcherException("Cannot watch snapshot when using remote", new UnsupportedOperationException());
}
- SwitcherLocalService executorInstance = (SwitcherLocalService) instance;
- SwitcherUtils.watchSnapshot(executorInstance, handler);
+ if (watcher == null) {
+ watcher = new SnapshotWatcher((SwitcherLocalService) instance, handler);
+ }
+
+ initWatcherExecutorService();
+ watcherExecutorService.submit(watcher);
}
/**
@@ -303,7 +323,11 @@ public static void watchSnapshot(SnapshotEventHandler handler) {
* @throws SwitcherException if watch thread never started
*/
public static void stopWatchingSnapshot() {
- SwitcherUtils.stopWatchingSnapshot();
+ if (watcher != null) {
+ watcherExecutorService.shutdownNow();
+ watcher.terminate();
+ watcher = null;
+ }
}
/**
diff --git a/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java b/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java
index 6675dbad..d79d03fd 100644
--- a/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java
+++ b/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java
@@ -91,15 +91,20 @@ protected Domain initializeSnapshotFromAPI(ClientRemote clientRemote) {
throw e;
}
}
+
+ public boolean isLocalEnabled() {
+ return SwitcherContextBase.contextBol(ContextKey.LOCAL_MODE);
+ }
/**
* It manipulates the result of a given Switcher key.
*
* @param key name of the key that you want to change the result
* @param expectedResult that will be returned when performing isItOn
+ * @return CriteriaResponse with the manipulated result
*/
- public static void assume(final String key, boolean expectedResult) {
- assume(key, expectedResult, null);
+ public static CriteriaResponse assume(final String key, boolean expectedResult) {
+ return assume(key, expectedResult, null);
}
/**
@@ -108,8 +113,9 @@ public static void assume(final String key, boolean expectedResult) {
* @param key name of the key that you want to change the result
* @param metadata additional information about the assumption (JSON)
* @param expectedResult that will be returned when performing isItOn
+ * @return CriteriaResponse with the manipulated result
*/
- public static void assume(final String key, boolean expectedResult, String metadata) {
+ public static CriteriaResponse assume(final String key, boolean expectedResult, String metadata) {
CriteriaResponse criteriaResponse = new CriteriaResponse();
criteriaResponse.setResult(expectedResult);
criteriaResponse.setReason("Switcher bypassed");
@@ -120,6 +126,7 @@ public static void assume(final String key, boolean expectedResult, String metad
}
bypass.put(key, criteriaResponse);
+ return criteriaResponse;
}
/**
diff --git a/src/main/java/com/github/switcherapi/client/model/AsyncSwitcher.java b/src/main/java/com/github/switcherapi/client/model/AsyncSwitcher.java
index 09ed750d..f371c816 100644
--- a/src/main/java/com/github/switcherapi/client/model/AsyncSwitcher.java
+++ b/src/main/java/com/github/switcherapi/client/model/AsyncSwitcher.java
@@ -24,22 +24,20 @@ public class AsyncSwitcher implements Runnable {
private final ExecutorService executorService;
- private Switcher switcher;
+ private final Switcher switcher;
private long nextRun = 0;
- public AsyncSwitcher() {
+ public AsyncSwitcher(final Switcher switcher) {
this.executorService = Executors.newCachedThreadPool();
+ this.switcher = switcher;
}
/**
* Validate if next run is ready to be performed, otherwise it will skip and delegate the
* Switcher result for the Switcher history execution.
- *
- * @param switcher Instance of the current switcher being executed
*/
- public synchronized void execute(final Switcher switcher) {
- this.switcher = switcher;
+ public synchronized void execute() {
SwitcherUtils.debug(logger, "nextRun: {} - currentTimeMillis: {}", nextRun, System.currentTimeMillis());
if (nextRun < System.currentTimeMillis()) {
@@ -53,10 +51,10 @@ public synchronized void execute(final Switcher switcher) {
@Override
public void run() {
try {
- final CriteriaResponse response = switcher.getContext().executeCriteria(this.switcher);
+ final CriteriaResponse response = switcher.getContext().executeCriteria(switcher);
switcher.getHistoryExecution().removeIf(item ->
- this.switcher.getSwitcherKey().equals(item.getSwitcherKey()) &&
- this.switcher.getEntry().equals(item.getEntry()));
+ switcher.getSwitcherKey().equals(item.getSwitcherKey()) &&
+ switcher.getEntry().equals(item.getEntry()));
switcher.getHistoryExecution().add(response);
} catch (SwitcherException e) {
logger.error(e);
diff --git a/src/main/java/com/github/switcherapi/client/model/Switcher.java b/src/main/java/com/github/switcherapi/client/model/Switcher.java
index 1c282e8b..cdfd2d62 100644
--- a/src/main/java/com/github/switcherapi/client/model/Switcher.java
+++ b/src/main/java/com/github/switcherapi/client/model/Switcher.java
@@ -25,8 +25,6 @@ public final class Switcher extends SwitcherBuilder {
public static final String BYPASS_METRIC = "bypassMetric";
- private final SwitcherExecutor context;
-
private final String switcherKey;
private final Set historyExecution;
@@ -40,8 +38,8 @@ public final class Switcher extends SwitcherBuilder {
* @param context client context in which the switcher will be executed (local/remote)
*/
public Switcher(final String switcherKey, final SwitcherExecutor context) {
+ super(context);
this.switcherKey = switcherKey;
- this.context = context;
this.historyExecution = new HashSet<>();
}
@@ -105,10 +103,10 @@ public CriteriaResponse submit() throws SwitcherException {
if (canUseAsync()) {
if (asyncSwitcher == null) {
- asyncSwitcher = new AsyncSwitcher();
+ asyncSwitcher = new AsyncSwitcher(this);
}
- asyncSwitcher.execute(this);
+ asyncSwitcher.execute();
final Optional response = getFromHistory();
if (response.isPresent()) {
return response.get();
@@ -150,10 +148,6 @@ public void resetEntry() {
public synchronized Set getHistoryExecution() {
return this.historyExecution;
}
-
- public SwitcherExecutor getContext() {
- return context;
- }
@Override
public String toString() {
diff --git a/src/main/java/com/github/switcherapi/client/model/SwitcherBuilder.java b/src/main/java/com/github/switcherapi/client/model/SwitcherBuilder.java
index d9b551ae..2819e3b2 100644
--- a/src/main/java/com/github/switcherapi/client/model/SwitcherBuilder.java
+++ b/src/main/java/com/github/switcherapi/client/model/SwitcherBuilder.java
@@ -1,7 +1,7 @@
package com.github.switcherapi.client.model;
import com.github.switcherapi.client.SwitcherContext;
-import com.github.switcherapi.client.SwitcherContextBase;
+import com.github.switcherapi.client.SwitcherExecutor;
import com.github.switcherapi.client.exception.SwitcherContextException;
import com.github.switcherapi.client.exception.SwitcherException;
import com.github.switcherapi.client.model.response.CriteriaResponse;
@@ -17,6 +17,8 @@
* @author Roger Floriano (petruki)
*/
public abstract class SwitcherBuilder {
+
+ protected final SwitcherExecutor context;
protected long delay;
@@ -28,7 +30,8 @@ public abstract class SwitcherBuilder {
protected List entry;
- protected SwitcherBuilder() {
+ protected SwitcherBuilder(final SwitcherExecutor context) {
+ this.context = context;
this.entry = new ArrayList<>();
this.delay = 0;
}
@@ -53,7 +56,7 @@ public SwitcherBuilder throttle(long delay) {
* @throws SwitcherContextException if Switcher is not configured to run locally using local mode
*/
public SwitcherBuilder remote(boolean remote) {
- if (!SwitcherContextBase.contextBol(ContextKey.LOCAL_MODE)) {
+ if (!this.context.isLocalEnabled()) {
throw new SwitcherContextException("Switcher is not configured to run locally");
}
@@ -236,4 +239,8 @@ public boolean isRemote() {
public String getDefaultResult() {
return defaultResult;
}
+
+ public SwitcherExecutor getContext() {
+ return context;
+ }
}
diff --git a/src/main/java/com/github/switcherapi/client/model/response/CriteriaResponse.java b/src/main/java/com/github/switcherapi/client/model/response/CriteriaResponse.java
index b55a58e3..92059b08 100644
--- a/src/main/java/com/github/switcherapi/client/model/response/CriteriaResponse.java
+++ b/src/main/java/com/github/switcherapi/client/model/response/CriteriaResponse.java
@@ -1,9 +1,12 @@
package com.github.switcherapi.client.model.response;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import com.github.switcherapi.client.model.Entry;
+import com.github.switcherapi.client.model.StrategyValidator;
import com.github.switcherapi.client.model.Switcher;
import com.google.gson.Gson;
@@ -27,6 +30,8 @@ public class CriteriaResponse {
protected List entry;
+ protected Map> entryWhen;
+
public CriteriaResponse() {
}
@@ -40,6 +45,15 @@ public CriteriaResponse(final boolean result, final String reason, final Switche
public CriteriaResponse buildFromSwitcher(Switcher switcher) {
this.switcherKey = switcher.getSwitcherKey();
this.entry = switcher.getEntry();
+
+ if (Objects.nonNull(entry)) {
+ for (Entry entry : entry) {
+ if (entryWhen.containsKey(entry.getStrategy()) && !entryWhen.get(entry.getStrategy()).contains(entry.getInput())) {
+ return new CriteriaResponse(!this.result, this.reason, switcher);
+ }
+ }
+ }
+
return this;
}
@@ -96,6 +110,19 @@ public void setEntry(List entry) {
this.entry = entry;
}
+ public CriteriaResponse when(StrategyValidator strategy, String input) {
+ return when(strategy, List.of(input));
+ }
+
+ public CriteriaResponse when(StrategyValidator strategy, List inputs) {
+ if (entryWhen == null) {
+ entryWhen = new HashMap<>();
+ }
+
+ entryWhen.put(strategy.toString(), inputs);
+ return this;
+ }
+
@Override
public int hashCode() {
return Objects.hash(entry, result, switcherKey);
@@ -118,13 +145,11 @@ public boolean equals(Object obj) {
@Override
public String toString() {
- final StringBuilder toString = new StringBuilder();
- toString.append("CriteriaResponse [result=").append(result)
- .append(", reason=").append(reason)
- .append(", metadata=").append(metadata)
- .append(", switcherKey=").append(switcherKey)
- .append(", entry=").append(entry).append("]");
- return toString.toString();
+ return "CriteriaResponse [result=" + result +
+ ", reason=" + reason +
+ ", metadata=" + metadata +
+ ", switcherKey=" + switcherKey +
+ ", entry=" + entry + "]";
}
}
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTest.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTest.java
index 07a6d9b9..4ef86d04 100644
--- a/src/main/java/com/github/switcherapi/client/test/SwitcherTest.java
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTest.java
@@ -30,6 +30,8 @@
boolean abTest() default false;
+ SwitcherTestWhen[] when() default {};
+
SwitcherTestValue[] switchers() default {};
}
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java
index 8d49b4bb..d1e06a74 100644
--- a/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java
@@ -1,6 +1,7 @@
package com.github.switcherapi.client.test;
import com.github.switcherapi.client.SwitcherExecutor;
+import com.github.switcherapi.client.model.response.CriteriaResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.jupiter.api.extension.*;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
@@ -78,16 +79,24 @@ private void mockMultipleSwitchers(ExtensionContext context, SwitcherTest switch
.toArray(String[]::new);
for (SwitcherTestValue value : switcherTest.switchers()) {
- SwitcherExecutor.assume(value.key(), inverted != value.result(), value.metadata());
+ CriteriaResponse criteriaResponse = SwitcherExecutor.assume(value.key(), inverted != value.result(), value.metadata());
+ applySwitcherTestWhen(criteriaResponse, value.when());
}
getStore(context).put(STORE_KEYS, keys);
}
private void mockSingleSwitcher(ExtensionContext context, SwitcherTest switcherTest, boolean inverted) {
- SwitcherExecutor.assume(switcherTest.key(), inverted != switcherTest.result(), switcherTest.metadata());
+ CriteriaResponse criteriaResponse = SwitcherExecutor.assume(switcherTest.key(), inverted != switcherTest.result(), switcherTest.metadata());
+ applySwitcherTestWhen(criteriaResponse, switcherTest.when());
getStore(context).put(STORE_KEY, switcherTest.key());
}
+
+ private void applySwitcherTestWhen(CriteriaResponse criteriaResponse, SwitcherTestWhen[] whens) {
+ for (SwitcherTestWhen when : whens) {
+ criteriaResponse.when(when.strategy(), Arrays.asList(when.input()));
+ }
+ }
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass(), context));
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestTemplate.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestTemplate.java
index b8e9ddfa..ab9d695d 100644
--- a/src/main/java/com/github/switcherapi/client/test/SwitcherTestTemplate.java
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestTemplate.java
@@ -9,6 +9,8 @@ class SwitcherTestTemplate implements TestTemplateInvocationContext {
private static final String DISPLAY_NAME_TEMPLATE = "With %s as %s";
+ private static final String WHEN_TEMPLATE = "when %s = %s";
+
private final SwitcherTest switcherTest;
private boolean inverted;
@@ -29,10 +31,21 @@ public String getDisplayName(int invocationIndex) {
if (ArrayUtils.isNotEmpty(switcherTestValues)) {
return String.join(", ", Arrays.toString(
Arrays.stream(switcherTestValues)
- .map(value -> String.format(DISPLAY_NAME_TEMPLATE, value.key(), inverted != value.result()))
+ .map(value -> concatWhens(String.format(DISPLAY_NAME_TEMPLATE, value.key(), inverted != value.result()), value.when()))
.toArray()));
}
- return String.format(DISPLAY_NAME_TEMPLATE, switcherTest.key(), inverted != switcherTest.result());
+ String displayName = String.format(DISPLAY_NAME_TEMPLATE, switcherTest.key(), inverted != switcherTest.result());
+ return concatWhens(displayName, switcherTest.when());
+ }
+
+ private String concatWhens(String displayName, SwitcherTestWhen[] switcherTestWhens) {
+ if (ArrayUtils.isNotEmpty(switcherTestWhens)) {
+ return displayName + " - " + Arrays.toString(
+ Arrays.stream(switcherTestWhens)
+ .map(when -> String.format(WHEN_TEMPLATE, when.strategy(), Arrays.toString(when.input())))
+ .toArray());
+ }
+ return displayName;
}
}
\ No newline at end of file
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestValue.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestValue.java
index 9cec2a66..4727424c 100644
--- a/src/main/java/com/github/switcherapi/client/test/SwitcherTestValue.java
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestValue.java
@@ -8,4 +8,6 @@
String metadata() default "";
+ SwitcherTestWhen[] when() default {};
+
}
diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestWhen.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestWhen.java
new file mode 100644
index 00000000..e8e9a730
--- /dev/null
+++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestWhen.java
@@ -0,0 +1,11 @@
+package com.github.switcherapi.client.test;
+
+import com.github.switcherapi.client.model.StrategyValidator;
+
+public @interface SwitcherTestWhen {
+
+ StrategyValidator strategy();
+
+ String[] input();
+
+}
diff --git a/src/main/java/com/github/switcherapi/client/utils/SwitcherUtils.java b/src/main/java/com/github/switcherapi/client/utils/SwitcherUtils.java
index 61a6527a..d5e1e3b4 100644
--- a/src/main/java/com/github/switcherapi/client/utils/SwitcherUtils.java
+++ b/src/main/java/com/github/switcherapi/client/utils/SwitcherUtils.java
@@ -3,8 +3,6 @@
import com.github.switcherapi.client.exception.SwitcherContextException;
import com.github.switcherapi.client.exception.SwitcherInvalidDateTimeArgumentException;
import com.github.switcherapi.client.model.ContextKey;
-import com.github.switcherapi.client.service.WorkerName;
-import com.github.switcherapi.client.service.local.SwitcherLocalService;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@@ -19,8 +17,6 @@
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -51,10 +47,6 @@ public class SwitcherUtils {
private static final String PAYLOAD_PATTERN = "%s.%s";
- private static SnapshotWatcher watcher;
-
- private static ExecutorService executorService;
-
private SwitcherUtils() {}
public static Date addTimeDuration(final String addValue, final Date date)
@@ -135,36 +127,6 @@ public static Set payloadReader(String jsonStr, String prevKey) {
return keys;
}
- /**
- * Initialize instance of SnapshotWatcher to run in the background.
- *
- * @param executorInstance of a Remote or Local Switcher
- * @param handler to notify snapshot change events
- */
- public static void watchSnapshot(final SwitcherLocalService executorInstance, SnapshotEventHandler handler) {
- if (watcher == null) {
- watcher = new SnapshotWatcher(executorInstance, handler);
- }
-
- initExecutorService();
- executorService.submit(watcher);
- }
-
- /**
- * If an instance of SnapshotWatcher is available, this operation will force it to terminate
- * and indicates to GC that the instance should be wiped from the memory.
- */
- public static void stopWatchingSnapshot() {
- if (executorService != null) {
- executorService.shutdownNow();
- }
-
- if (watcher != null) {
- watcher.terminate();
- watcher = null;
- }
- }
-
/**
* Resolve properties from switcherapi.properties file.
* It reads environment values when using the following notation: ${VALUE} or
@@ -262,16 +224,4 @@ private static boolean setWithSystemEnv(Matcher matcher, StringBuilder sBuilder)
return StringUtils.isEmpty(sBuilder.toString());
}
- /**
- * Configure Executor Service for Snapshot Watch Worker
- */
- private static void initExecutorService() {
- executorService = Executors.newSingleThreadExecutor(r -> {
- Thread thread = new Thread(r);
- thread.setName(WorkerName.SNAPSHOT_WATCH_WORKER.toString());
- thread.setDaemon(true);
- return thread;
- });
- }
-
}
diff --git a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java
index d3d1309d..9d509705 100644
--- a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java
+++ b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java
@@ -1,9 +1,11 @@
package com.github.switcherapi.client;
+import com.github.switcherapi.client.model.StrategyValidator;
import com.github.switcherapi.client.model.Switcher;
import com.github.switcherapi.client.model.response.CriteriaResponse;
import com.github.switcherapi.client.test.SwitcherTest;
import com.github.switcherapi.client.test.SwitcherTestValue;
+import com.github.switcherapi.client.test.SwitcherTestWhen;
import com.github.switcherapi.fixture.MetadataErrorSample;
import com.github.switcherapi.fixture.MetadataSample;
import org.apache.commons.lang3.StringUtils;
@@ -106,6 +108,63 @@ void shouldReturnFalse_usingAnnotationAsFalse() {
assertFalse(switcher.isItOn());
}
+ @SwitcherTest(key = USECASE41, when = {
+ @SwitcherTestWhen(strategy = StrategyValidator.VALUE, input = "Value1")
+ })
+ void shouldReturnTrue_usingAnnotationAsTrueWhenValueMatches() {
+ //given
+ SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2));
+ SwitcherContext.initializeClient();
+
+ //test
+ Switcher switcher = getSwitcher(USECASE41).checkValue("Value1").build();
+ assertTrue(switcher.isItOn());
+ }
+
+ @SwitcherTest(key = USECASE41, when = {
+ @SwitcherTestWhen(strategy = StrategyValidator.VALUE, input = { "Value1", "Value2" })
+ })
+ void shouldReturnTrue_usingAnnotationAsTrueWhenValueSetMatches() {
+ //given
+ SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2));
+ SwitcherContext.initializeClient();
+
+ //test
+ Switcher switcher = getSwitcher(USECASE41).checkValue("Value1").build();
+ assertTrue(switcher.isItOn());
+
+ switcher = getSwitcher(USECASE41).checkValue("Value2").build();
+ assertTrue(switcher.isItOn());
+ }
+
+ @SwitcherTest(key = USECASE41, switchers =
+ @SwitcherTestValue(key = USECASE41, when = {
+ @SwitcherTestWhen(strategy = StrategyValidator.VALUE, input = "Value1")
+ })
+ )
+ void shouldReturnTrue_usingMultipleSwitchersAnnotationWhenValueMatches() {
+ //given
+ SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2));
+ SwitcherContext.initializeClient();
+
+ //test
+ Switcher switcher = getSwitcher(USECASE41).checkValue("Value1").build();
+ assertTrue(switcher.isItOn());
+ }
+
+ @SwitcherTest(key = USECASE41, when = {
+ @SwitcherTestWhen(strategy = StrategyValidator.VALUE, input = "Value2")
+ })
+ void shouldReturnFalse_usingAnnotationAsTrueWhenValueNotMatches() {
+ //given
+ SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE2));
+ SwitcherContext.initializeClient();
+
+ //test
+ Switcher switcher = getSwitcher(USECASE41).checkValue("Value1").build();
+ assertFalse(switcher.isItOn());
+ }
+
@SwitcherTest(key = USECASE111)
void shouldReturnTrue_usingAnnotationAsTrue() {
//given
@@ -183,6 +242,38 @@ void shouldReturnWithMetadata_usingMultipleSwitchersAnnotation() {
assertEquals("321", criteriaResponse.getMetadata(MetadataErrorSample.class).getErrorId());
}
+ @Test
+ void shouldReturnTrue_afterAssumingItsTrueWhenValueMatches() {
+ //given
+ SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE1));
+ SwitcherContext.initializeClient();
+
+ //test
+ Switcher switcher = getSwitcher(USECASE41).checkValue("Value1").build();
+
+ SwitcherExecutor.assume(USECASE41, true)
+ .when(StrategyValidator.VALUE, "Value1");
+
+ assertTrue(switcher.isItOn());
+ }
+
+ @Test
+ void shouldReturnFalse_afterAssumingItsTrueWhenValueNotMatches() {
+ //given
+ SwitcherContext.configure(ContextBuilder.builder().snapshotLocation(SNAPSHOTS_LOCAL).environment(FIXTURE1));
+ SwitcherContext.initializeClient();
+
+ //test
+ Switcher switcher = getSwitcher(USECASE41).checkValue("Value2").build();
+
+ SwitcherExecutor.assume(USECASE41, true)
+ .when(StrategyValidator.VALUE, "Value1");
+
+ assertFalse(switcher.isItOn());
+ // check if no override was made during criteria submission
+ assertFalse(switcher.isItOn());
+ }
+
/**
* Fake scenario to test both ways of building a String.
* It is used to AB Test behavior when the same result is expected.
diff --git a/src/test/java/com/github/switcherapi/playground/ClientPlayground.java b/src/test/java/com/github/switcherapi/playground/ClientPlayground.java
index e59292dd..e46d62fb 100644
--- a/src/test/java/com/github/switcherapi/playground/ClientPlayground.java
+++ b/src/test/java/com/github/switcherapi/playground/ClientPlayground.java
@@ -22,7 +22,7 @@ public static void test() {
configure(ContextBuilder.builder()
.contextLocation(Features.class.getCanonicalName())
.url("https://api.switcherapi.com")
- .apiKey("JDJiJDA4JEFweTZjSTR2bE9pUjNJOUYvRy9raC4vRS80Q2tzUnk1d3o1aXFmS2o5eWJmVW11cjR0ODNT")
+ .apiKey("[API_KEY]")
.domain("Playground")
.local(true)
.snapshotLocation("src/test/resources/snapshot/playground")
@@ -35,7 +35,7 @@ public static void test() {
long time = System.currentTimeMillis();
logger.info("Switcher is on: {}", switcher.isItOn());
logger.info("Time elapsed: {}", System.currentTimeMillis() - time);
- }, 0, 10, TimeUnit.SECONDS);
+ }, 0, 5, TimeUnit.SECONDS);
}
public static void main(String[] args) {
diff --git a/src/test/resources/switcherapi.properties b/src/test/resources/switcherapi.properties
index dc5de1ab..0ebf340e 100644
--- a/src/test/resources/switcherapi.properties
+++ b/src/test/resources/switcherapi.properties
@@ -1,6 +1,6 @@
switcher.context=com.github.switcherapi.Switchers
switcher.url=http://localhost:3000
-switcher.apikey=JDJiJDA4JGdQSVZ0UW1qWUVCSUhYdEg1RC92ZHVCTnRNYjdRYm4yRXEuM3g4UTkxUkouc29sNWhtTnN1
+switcher.apikey=[API_KEY]
switcher.component=switcher-client
switcher.environment=test
switcher.domain=switcher-domain