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