MultiProvider: Added strategies, unit tests and documentation
Signed-off-by: suvaidkhan <khansuvaid@yahoo.com>
This commit is contained in:
parent
e67f598357
commit
58c308c25e
6
pom.xml
6
pom.xml
|
|
@ -70,6 +70,12 @@
|
|||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20250517</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>com.tngtech.archunit</groupId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package dev.openfeature.sdk.multiprovider;
|
||||
|
||||
import static dev.openfeature.sdk.ErrorCode.FLAG_NOT_FOUND;
|
||||
|
||||
import dev.openfeature.sdk.EvaluationContext;
|
||||
import dev.openfeature.sdk.FeatureProvider;
|
||||
import dev.openfeature.sdk.ProviderEvaluation;
|
||||
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* First match strategy. Return the first result returned by a provider. Skip providers that
|
||||
* indicate they had no value due to FLAG_NOT_FOUND. In all other cases, use the value returned by
|
||||
* the provider. If any provider returns an error result other than FLAG_NOT_FOUND, the whole
|
||||
* evaluation should error and “bubble up” the individual provider’s error in the result. As soon as
|
||||
* a value is returned by a provider, the rest of the operation should short-circuit and not call
|
||||
* the rest of the providers.
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
public class FirstMatchStrategy implements Strategy {
|
||||
|
||||
/**
|
||||
* Represents a strategy that evaluates providers based on a first-match approach. Provides a
|
||||
* method to evaluate providers using a specified function and return the evaluation result.
|
||||
*
|
||||
* @param providerFunction provider function
|
||||
* @param <T> ProviderEvaluation type
|
||||
* @return the provider evaluation
|
||||
*/
|
||||
@Override
|
||||
public <T> ProviderEvaluation<T> evaluate(
|
||||
Map<String, FeatureProvider> providers,
|
||||
String key,
|
||||
T defaultValue,
|
||||
EvaluationContext ctx,
|
||||
Function<FeatureProvider, ProviderEvaluation<T>> providerFunction) {
|
||||
for (FeatureProvider provider : providers.values()) {
|
||||
try {
|
||||
ProviderEvaluation<T> res = providerFunction.apply(provider);
|
||||
if (!FLAG_NOT_FOUND.equals(res.getErrorCode())) {
|
||||
return res;
|
||||
}
|
||||
} catch (FlagNotFoundError e) {
|
||||
log.debug("flag not found {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
throw new FlagNotFoundError("flag not found");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package dev.openfeature.sdk.multiprovider;
|
||||
|
||||
import dev.openfeature.sdk.EvaluationContext;
|
||||
import dev.openfeature.sdk.FeatureProvider;
|
||||
import dev.openfeature.sdk.ProviderEvaluation;
|
||||
import dev.openfeature.sdk.exceptions.GeneralError;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* First Successful Strategy. Similar to “First Match”, except that errors from evaluated providers
|
||||
* do not halt execution. Instead, it will return the first successful result from a provider. If no
|
||||
* provider successfully responds, it will throw an error result.
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
public class FirstSuccessfulStrategy implements Strategy {
|
||||
|
||||
@Override
|
||||
public <T> ProviderEvaluation<T> evaluate(
|
||||
Map<String, FeatureProvider> providers,
|
||||
String key,
|
||||
T defaultValue,
|
||||
EvaluationContext ctx,
|
||||
Function<FeatureProvider, ProviderEvaluation<T>> providerFunction) {
|
||||
for (FeatureProvider provider : providers.values()) {
|
||||
try {
|
||||
ProviderEvaluation<T> res = providerFunction.apply(provider);
|
||||
if (res.getErrorCode() == null) {
|
||||
return res;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("evaluation exception {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
throw new GeneralError("evaluation error");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package dev.openfeature.sdk.multiprovider;
|
||||
|
||||
import dev.openfeature.sdk.EvaluationContext;
|
||||
import dev.openfeature.sdk.EventProvider;
|
||||
import dev.openfeature.sdk.FeatureProvider;
|
||||
import dev.openfeature.sdk.Metadata;
|
||||
import dev.openfeature.sdk.ProviderEvaluation;
|
||||
import dev.openfeature.sdk.Value;
|
||||
import dev.openfeature.sdk.exceptions.GeneralError;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/** <b>Experimental:</b> Provider implementation for Multi-provider. */
|
||||
@Slf4j
|
||||
public class MultiProvider extends EventProvider {
|
||||
|
||||
@Getter
|
||||
private static final String NAME = "multiprovider";
|
||||
|
||||
public static final int INIT_THREADS_COUNT = 8;
|
||||
private final Map<String, FeatureProvider> providers;
|
||||
private final Strategy strategy;
|
||||
private String metadataName;
|
||||
|
||||
/**
|
||||
* Constructs a MultiProvider with the given list of FeatureProviders, using a default strategy.
|
||||
*
|
||||
* @param providers the list of FeatureProviders to initialize the MultiProvider with
|
||||
*/
|
||||
public MultiProvider(List<FeatureProvider> providers) {
|
||||
this(providers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MultiProvider with the given list of FeatureProviders and a strategy.
|
||||
*
|
||||
* @param providers the list of FeatureProviders to initialize the MultiProvider with
|
||||
* @param strategy the strategy
|
||||
*/
|
||||
public MultiProvider(List<FeatureProvider> providers, Strategy strategy) {
|
||||
this.providers = buildProviders(providers);
|
||||
if (strategy != null) {
|
||||
this.strategy = strategy;
|
||||
} else {
|
||||
this.strategy = new FirstMatchStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
protected static Map<String, FeatureProvider> buildProviders(List<FeatureProvider> providers) {
|
||||
Map<String, FeatureProvider> providersMap = new LinkedHashMap<>(providers.size());
|
||||
for (FeatureProvider provider : providers) {
|
||||
FeatureProvider prevProvider =
|
||||
providersMap.put(provider.getMetadata().getName(), provider);
|
||||
if (prevProvider != null) {
|
||||
log.warn("duplicated provider name: {}", provider.getMetadata().getName());
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(providersMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the provider.
|
||||
*
|
||||
* @param evaluationContext evaluation context
|
||||
* @throws Exception on error
|
||||
*/
|
||||
@Override
|
||||
public void initialize(EvaluationContext evaluationContext) throws Exception {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("name", NAME);
|
||||
JSONObject providersMetadata = new JSONObject();
|
||||
json.put("originalMetadata", providersMetadata);
|
||||
ExecutorService initPool = Executors.newFixedThreadPool(INIT_THREADS_COUNT);
|
||||
Collection<Callable<Boolean>> tasks = new ArrayList<>(providers.size());
|
||||
for (FeatureProvider provider : providers.values()) {
|
||||
tasks.add(() -> {
|
||||
provider.initialize(evaluationContext);
|
||||
return true;
|
||||
});
|
||||
JSONObject providerMetadata = new JSONObject();
|
||||
providerMetadata.put("name", provider.getMetadata().getName());
|
||||
providersMetadata.put(provider.getMetadata().getName(), providerMetadata);
|
||||
}
|
||||
List<Future<Boolean>> results = initPool.invokeAll(tasks);
|
||||
for (Future<Boolean> result : results) {
|
||||
if (!result.get()) {
|
||||
throw new GeneralError("init failed");
|
||||
}
|
||||
}
|
||||
metadataName = json.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metadata getMetadata() {
|
||||
return () -> metadataName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
|
||||
return strategy.evaluate(
|
||||
providers, key, defaultValue, ctx, p -> p.getBooleanEvaluation(key, defaultValue, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderEvaluation<String> getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
|
||||
return strategy.evaluate(providers, key, defaultValue, ctx, p -> p.getStringEvaluation(key, defaultValue, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) {
|
||||
return strategy.evaluate(
|
||||
providers, key, defaultValue, ctx, p -> p.getIntegerEvaluation(key, defaultValue, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) {
|
||||
return strategy.evaluate(providers, key, defaultValue, ctx, p -> p.getDoubleEvaluation(key, defaultValue, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultValue, EvaluationContext ctx) {
|
||||
return strategy.evaluate(providers, key, defaultValue, ctx, p -> p.getObjectEvaluation(key, defaultValue, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
log.debug("shutdown begin");
|
||||
for (FeatureProvider provider : providers.values()) {
|
||||
try {
|
||||
provider.shutdown();
|
||||
} catch (Exception e) {
|
||||
log.error("error shutdown provider {}", provider.getMetadata().getName(), e);
|
||||
}
|
||||
}
|
||||
log.debug("shutdown end");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package dev.openfeature.sdk.multiprovider;
|
||||
|
||||
import dev.openfeature.sdk.EvaluationContext;
|
||||
import dev.openfeature.sdk.FeatureProvider;
|
||||
import dev.openfeature.sdk.ProviderEvaluation;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/** strategy. */
|
||||
public interface Strategy {
|
||||
<T> ProviderEvaluation<T> evaluate(
|
||||
Map<String, FeatureProvider> providers,
|
||||
String key,
|
||||
T defaultValue,
|
||||
EvaluationContext ctx,
|
||||
Function<FeatureProvider, ProviderEvaluation<T>> providerFunction);
|
||||
}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
package dev.openfeature.sdk.multiProvider;
|
||||
|
||||
import static dev.openfeature.sdk.ErrorCode.FLAG_NOT_FOUND;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import dev.openfeature.sdk.EvaluationContext;
|
||||
import dev.openfeature.sdk.FeatureProvider;
|
||||
import dev.openfeature.sdk.Metadata;
|
||||
import dev.openfeature.sdk.MutableContext;
|
||||
import dev.openfeature.sdk.ProviderEvaluation;
|
||||
import dev.openfeature.sdk.Value;
|
||||
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
|
||||
import dev.openfeature.sdk.exceptions.GeneralError;
|
||||
import dev.openfeature.sdk.multiprovider.FirstMatchStrategy;
|
||||
import dev.openfeature.sdk.multiprovider.FirstSuccessfulStrategy;
|
||||
import dev.openfeature.sdk.multiprovider.MultiProvider;
|
||||
import dev.openfeature.sdk.multiprovider.Strategy;
|
||||
import dev.openfeature.sdk.providers.memory.Flag;
|
||||
import dev.openfeature.sdk.providers.memory.InMemoryProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class MultiProviderTest {
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
public void testInit() {
|
||||
FeatureProvider provider1 = mock(FeatureProvider.class);
|
||||
FeatureProvider provider2 = mock(FeatureProvider.class);
|
||||
when(provider1.getMetadata()).thenReturn(() -> "provider1");
|
||||
when(provider2.getMetadata()).thenReturn(() -> "provider2");
|
||||
|
||||
List<FeatureProvider> providers = new ArrayList<>(2);
|
||||
providers.add(provider1);
|
||||
providers.add(provider2);
|
||||
Strategy strategy = mock(Strategy.class);
|
||||
MultiProvider multiProvider = new MultiProvider(providers, strategy);
|
||||
multiProvider.initialize(null);
|
||||
|
||||
assertNotNull(multiProvider);
|
||||
assertEquals(
|
||||
"{\"originalMetadata\":{\"provider1\":{\"name\":\"provider1\"},"
|
||||
+ "\"provider2\":{\"name\":\"provider2\"}},\"name\":\"multiprovider\"}",
|
||||
multiProvider.getMetadata().getName());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
public void testInitOneFails() {
|
||||
FeatureProvider provider1 = mock(FeatureProvider.class);
|
||||
FeatureProvider provider2 = mock(FeatureProvider.class);
|
||||
when(provider1.getMetadata()).thenReturn(() -> "provider1");
|
||||
when(provider2.getMetadata()).thenReturn(() -> "provider2");
|
||||
doThrow(new GeneralError()).when(provider1).initialize(any());
|
||||
doThrow(new GeneralError()).when(provider1).shutdown();
|
||||
|
||||
List<FeatureProvider> providers = new ArrayList<>(2);
|
||||
providers.add(provider1);
|
||||
providers.add(provider2);
|
||||
Strategy strategy = mock(Strategy.class);
|
||||
MultiProvider multiProvider = new MultiProvider(providers, strategy);
|
||||
assertThrows(ExecutionException.class, () -> multiProvider.initialize(null));
|
||||
assertDoesNotThrow(() -> multiProvider.shutdown());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateProviderNames() {
|
||||
FeatureProvider provider1 = mock(FeatureProvider.class);
|
||||
FeatureProvider provider2 = mock(FeatureProvider.class);
|
||||
when(provider1.getMetadata()).thenReturn(() -> "provider");
|
||||
when(provider2.getMetadata()).thenReturn(() -> "provider");
|
||||
|
||||
List<FeatureProvider> providers = new ArrayList<>(2);
|
||||
providers.add(provider1);
|
||||
providers.add(provider2);
|
||||
|
||||
assertDoesNotThrow(() -> new MultiProvider(providers, null).initialize(null));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
public void testRetrieveMetadataName() {
|
||||
List<FeatureProvider> providers = new ArrayList<>();
|
||||
FeatureProvider mockProvider = mock(FeatureProvider.class);
|
||||
when(mockProvider.getMetadata()).thenReturn(() -> "MockProvider");
|
||||
providers.add(mockProvider);
|
||||
Strategy mockStrategy = mock(Strategy.class);
|
||||
MultiProvider multiProvider = new MultiProvider(providers, mockStrategy);
|
||||
multiProvider.initialize(null);
|
||||
|
||||
assertEquals(
|
||||
"{\"originalMetadata\":{\"MockProvider\":{\"name\":\"MockProvider\"}}," + "\"name\":\"multiprovider\"}",
|
||||
multiProvider.getMetadata().getName());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
public void testEvaluations() {
|
||||
Map<String, Flag<?>> flags1 = new HashMap<>();
|
||||
flags1.put(
|
||||
"b1",
|
||||
Flag.builder()
|
||||
.variant("true", true)
|
||||
.variant("false", false)
|
||||
.defaultVariant("true")
|
||||
.build());
|
||||
flags1.put("i1", Flag.builder().variant("v", 1).defaultVariant("v").build());
|
||||
flags1.put("d1", Flag.builder().variant("v", 1.0).defaultVariant("v").build());
|
||||
flags1.put("s1", Flag.builder().variant("v", "str1").defaultVariant("v").build());
|
||||
flags1.put(
|
||||
"o1",
|
||||
Flag.builder().variant("v", new Value("v1")).defaultVariant("v").build());
|
||||
InMemoryProvider provider1 = new InMemoryProvider(flags1) {
|
||||
public Metadata getMetadata() {
|
||||
return () -> "old-provider";
|
||||
}
|
||||
};
|
||||
Map<String, Flag<?>> flags2 = new HashMap<>();
|
||||
flags2.put(
|
||||
"b1",
|
||||
Flag.builder()
|
||||
.variant("true", true)
|
||||
.variant("false", false)
|
||||
.defaultVariant("false")
|
||||
.build());
|
||||
flags2.put("i1", Flag.builder().variant("v", 2).defaultVariant("v").build());
|
||||
flags2.put("d1", Flag.builder().variant("v", 2.0).defaultVariant("v").build());
|
||||
flags2.put("s1", Flag.builder().variant("v", "str2").defaultVariant("v").build());
|
||||
flags2.put(
|
||||
"o1",
|
||||
Flag.builder().variant("v", new Value("v2")).defaultVariant("v").build());
|
||||
|
||||
flags2.put(
|
||||
"s2", Flag.builder().variant("v", "s2str2").defaultVariant("v").build());
|
||||
InMemoryProvider provider2 = new InMemoryProvider(flags2) {
|
||||
public Metadata getMetadata() {
|
||||
return () -> "new-provider";
|
||||
}
|
||||
};
|
||||
List<FeatureProvider> providers = new ArrayList<>(2);
|
||||
providers.add(provider1);
|
||||
providers.add(provider2);
|
||||
MultiProvider multiProvider = new MultiProvider(providers);
|
||||
multiProvider.initialize(null);
|
||||
|
||||
assertEquals(true, multiProvider.getBooleanEvaluation("b1", false, null).getValue());
|
||||
assertEquals(1, multiProvider.getIntegerEvaluation("i1", 0, null).getValue());
|
||||
assertEquals(1.0, multiProvider.getDoubleEvaluation("d1", 0.0, null).getValue());
|
||||
assertEquals("str1", multiProvider.getStringEvaluation("s1", "", null).getValue());
|
||||
assertEquals(
|
||||
"v1",
|
||||
multiProvider.getObjectEvaluation("o1", null, null).getValue().asString());
|
||||
|
||||
assertEquals("s2str2", multiProvider.getStringEvaluation("s2", "", null).getValue());
|
||||
MultiProvider finalMultiProvider1 = multiProvider;
|
||||
assertThrows(FlagNotFoundError.class, () -> finalMultiProvider1.getStringEvaluation("non-existing", "", null));
|
||||
|
||||
multiProvider.shutdown();
|
||||
multiProvider = new MultiProvider(providers, new FirstSuccessfulStrategy());
|
||||
multiProvider.initialize(null);
|
||||
|
||||
assertEquals(true, multiProvider.getBooleanEvaluation("b1", false, null).getValue());
|
||||
assertEquals(1, multiProvider.getIntegerEvaluation("i1", 0, null).getValue());
|
||||
assertEquals(1.0, multiProvider.getDoubleEvaluation("d1", 0.0, null).getValue());
|
||||
assertEquals("str1", multiProvider.getStringEvaluation("s1", "", null).getValue());
|
||||
assertEquals(
|
||||
"v1",
|
||||
multiProvider.getObjectEvaluation("o1", null, null).getValue().asString());
|
||||
|
||||
assertEquals("s2str2", multiProvider.getStringEvaluation("s2", "", null).getValue());
|
||||
MultiProvider finalMultiProvider2 = multiProvider;
|
||||
assertThrows(GeneralError.class, () -> finalMultiProvider2.getStringEvaluation("non-existing", "", null));
|
||||
|
||||
multiProvider.shutdown();
|
||||
Strategy customStrategy = new Strategy() {
|
||||
final FirstMatchStrategy fallbackStrategy = new FirstMatchStrategy();
|
||||
|
||||
@Override
|
||||
public <T> ProviderEvaluation<T> evaluate(
|
||||
Map<String, FeatureProvider> providers,
|
||||
String key,
|
||||
T defaultValue,
|
||||
EvaluationContext ctx,
|
||||
Function<FeatureProvider, ProviderEvaluation<T>> providerFunction) {
|
||||
Value contextProvider = null;
|
||||
if (ctx != null) {
|
||||
contextProvider = ctx.getValue("provider");
|
||||
}
|
||||
if (contextProvider != null && "new-provider".equals(contextProvider.asString())) {
|
||||
return providerFunction.apply(providers.get("new-provider"));
|
||||
}
|
||||
return fallbackStrategy.evaluate(providers, key, defaultValue, ctx, providerFunction);
|
||||
}
|
||||
};
|
||||
multiProvider = new MultiProvider(providers, customStrategy);
|
||||
multiProvider.initialize(null);
|
||||
|
||||
EvaluationContext context = new MutableContext().add("provider", "new-provider");
|
||||
assertEquals(
|
||||
false, multiProvider.getBooleanEvaluation("b1", true, context).getValue());
|
||||
assertEquals(true, multiProvider.getBooleanEvaluation("b1", true, null).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstMatchStrategyErrorCode() {
|
||||
FeatureProvider provider1 = mock(FeatureProvider.class);
|
||||
FeatureProvider provider2 = mock(FeatureProvider.class);
|
||||
FeatureProvider provider3 = mock(FeatureProvider.class);
|
||||
|
||||
when(provider1.getMetadata()).thenReturn(() -> "provider1");
|
||||
when(provider2.getMetadata()).thenReturn(() -> "provider2");
|
||||
when(provider3.getMetadata()).thenReturn(() -> "provider3");
|
||||
|
||||
ProviderEvaluation<String> flagNotFoundResult = mock(ProviderEvaluation.class);
|
||||
when(flagNotFoundResult.getErrorCode()).thenReturn(FLAG_NOT_FOUND);
|
||||
|
||||
ProviderEvaluation<String> successResult = mock(ProviderEvaluation.class);
|
||||
when(successResult.getErrorCode()).thenReturn(null);
|
||||
when(successResult.getValue()).thenReturn("success");
|
||||
|
||||
when(provider1.getStringEvaluation("test", "default", null)).thenReturn(flagNotFoundResult);
|
||||
when(provider2.getStringEvaluation("test", "default", null)).thenReturn(successResult);
|
||||
|
||||
Map<String, FeatureProvider> providers = new LinkedHashMap<>();
|
||||
providers.put("provider1", provider1);
|
||||
providers.put("provider2", provider2);
|
||||
providers.put("provider3", provider3);
|
||||
FirstMatchStrategy strategy = new FirstMatchStrategy();
|
||||
ProviderEvaluation<String> result = strategy.evaluate(
|
||||
providers, "test", "default", null, p -> p.getStringEvaluation("test", "default", null));
|
||||
|
||||
assertEquals("success", result.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstSuccessfulStrategyErrorCode() {
|
||||
FeatureProvider provider1 = mock(FeatureProvider.class);
|
||||
FeatureProvider provider2 = mock(FeatureProvider.class);
|
||||
when(provider1.getMetadata()).thenReturn(() -> "provider1");
|
||||
when(provider2.getMetadata()).thenReturn(() -> "provider2");
|
||||
|
||||
ProviderEvaluation<String> flagNotFoundResult = mock(ProviderEvaluation.class);
|
||||
when(flagNotFoundResult.getErrorCode()).thenReturn(FLAG_NOT_FOUND);
|
||||
|
||||
ProviderEvaluation<String> successResult = mock(ProviderEvaluation.class);
|
||||
when(successResult.getErrorCode()).thenReturn(null);
|
||||
when(successResult.getValue()).thenReturn("success");
|
||||
|
||||
when(provider1.getStringEvaluation("test", "default", null)).thenReturn(flagNotFoundResult);
|
||||
when(provider2.getStringEvaluation("test", "default", null)).thenReturn(successResult);
|
||||
|
||||
Map<String, FeatureProvider> providers = new LinkedHashMap<>();
|
||||
providers.put("provider1", provider1);
|
||||
providers.put("provider2", provider2);
|
||||
FirstSuccessfulStrategy strategy = new FirstSuccessfulStrategy();
|
||||
ProviderEvaluation<String> result = strategy.evaluate(
|
||||
providers, "test", "default", null, p -> p.getStringEvaluation("test", "default", null));
|
||||
|
||||
assertEquals("success", result.getValue());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue