171 lines
9.3 KiB
Java
171 lines
9.3 KiB
Java
package javasdk;
|
|
|
|
import org.junit.jupiter.api.Disabled;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.util.List;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
public class FlagEvaluationSpecTests {
|
|
|
|
Client _client() {
|
|
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
|
|
api.setProvider(new NoOpProvider());
|
|
return api.getClient();
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.1",
|
|
text="The API, and any state it maintains SHOULD exist as a global singleton, " +
|
|
"even in cases wherein multiple versions of the API are present at runtime.")
|
|
@Test void global_singleton() {
|
|
assertSame(OpenFeatureAPI.getInstance(), OpenFeatureAPI.getInstance());
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.2",
|
|
text="The API MUST provide a function to set the global provider singleton, " +
|
|
"which accepts an API-conformant provider implementation.")
|
|
@Specification(spec="flag evaluation", number="1.4",
|
|
text="The API MUST provide a function for retrieving the provider implementation.")
|
|
@Test void provider() {
|
|
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
|
|
FeatureProvider mockProvider = mock(FeatureProvider.class);
|
|
api.setProvider(mockProvider);
|
|
assertEquals(mockProvider, api.getProvider());
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.3", text="The API MUST provide a function to add hooks which " +
|
|
"accepts one or more API-conformant hooks, and appends them to the collection of any previously added hooks." +
|
|
"When new hooks are added, previously added hooks are not removed.")
|
|
@Disabled @Test void hook_addition() {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.5", text="The API MUST provide a function for creating a client " +
|
|
"which accepts the following options: name (optional): A logical string identifier for the client.")
|
|
@Test void namedClient() {
|
|
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
|
|
Client c = api.getClient("Sir Calls-a-lot");
|
|
// TODO: Doesn't say that you can *get* the client name.. which seems useful?
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.6", text="The client MUST provide a method to add hooks which " +
|
|
"accepts one or more API-conformant hooks, and appends them to the collection of any previously added hooks. " +
|
|
"When new hooks are added, previously added hooks are not removed.")
|
|
@Test void hookRegistration() {
|
|
Client c = _client();
|
|
Hook m1 = mock(Hook.class);
|
|
Hook m2 = mock(Hook.class);
|
|
c.registerHooks(m1);
|
|
c.registerHooks(m2);
|
|
List<Hook> hooks = c.getClientHooks();
|
|
assertEquals(2, hooks.size());
|
|
assertTrue(hooks.contains(m1));
|
|
assertTrue(hooks.contains(m2));
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.7", text="The client MUST provide methods for flag evaluation, with" +
|
|
" parameters flag key (string, required), default value (boolean | number | string | structure, required), " +
|
|
"evaluation context (optional), and evaluation options (optional), which returns the flag value.")
|
|
@Specification(spec="flag evaluation", number="1.8.1",text="The client MUST provide methods for typed flag " +
|
|
"evaluation, including boolean, numeric, string, and structure.")
|
|
@Test void value_flags() {
|
|
Client c = _client();
|
|
String key = "key";
|
|
assertFalse(c.getBooleanValue(key, false));
|
|
assertFalse(c.getBooleanValue(key, false, new EvaluationContext()));
|
|
assertFalse(c.getBooleanValue(key, false, new EvaluationContext(), FlagEvaluationOptions.builder().build()));
|
|
|
|
|
|
assertEquals("my-string", c.getStringValue(key, "my-string"));
|
|
assertEquals("my-string", c.getStringValue(key, "my-string", new EvaluationContext()));
|
|
assertEquals("my-string", c.getStringValue(key, "my-string", new EvaluationContext(), FlagEvaluationOptions.builder().build()));
|
|
|
|
assertEquals(4, c.getIntegerValue(key, 4));
|
|
assertEquals(4, c.getIntegerValue(key, 4, new EvaluationContext()));
|
|
assertEquals(4, c.getIntegerValue(key, 4, new EvaluationContext(), FlagEvaluationOptions.builder().build()));
|
|
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.7", text="The client MUST provide methods for flag evaluation, with" +
|
|
" parameters flag key (string, required), default value (boolean | number | string | structure, required), " +
|
|
"evaluation context (optional), and evaluation options (optional), which returns the flag value.")
|
|
@Disabled
|
|
@Test void value_flags__object() {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
|
|
@Specification(spec="flag evaluation", number="1.9", text="The client MUST provide methods for detailed flag value " +
|
|
"evaluation with parameters flag key (string, required), default value (boolean | number | string | " +
|
|
"structure, required), evaluation context (optional), and evaluation options (optional), which returns an " +
|
|
"evaluation details structure.")
|
|
@Specification(spec="flag evaluation", number="1.10", text="The evaluation details structure's value field MUST " +
|
|
"contain the evaluated flag value.")
|
|
@Specification(spec="flag evaluation", number="1.11.1", text="The evaluation details structure SHOULD accept a " +
|
|
"generic argument (or use an equivalent language feature) which indicates the type of the wrapped value " +
|
|
"field.")
|
|
@Specification(spec="flag evaluation", number="1.12", text="The evaluation details structure's flag key field MUST " +
|
|
"contain the flag key argument passed to the detailed flag evaluation method.")
|
|
@Specification(spec="flag evaluation", number="1.13", text="In cases of normal execution, the evaluation details " +
|
|
"structure's variant field MUST contain the value of the variant field in the flag resolution structure " +
|
|
"returned by the configured provider, if the field is set.")
|
|
@Specification(spec="flag evaluation", number="1.14", text="In cases of normal execution, the evaluation details " +
|
|
"structure's reason field MUST contain the value of the reason field in the flag resolution structure " +
|
|
"returned by the configured provider, if the field is set.")
|
|
@Specification(spec="flag evaluation", number="1.15", text="In cases of abnormal execution, the evaluation details " +
|
|
"structure's error code field MUST identify an error occurred during flag evaluation, having possible " +
|
|
"values PROVIDER_NOT_READY, FLAG_NOT_FOUND, PARSE_ERROR, TYPE_MISMATCH, or GENERAL.")
|
|
@Specification(spec="flag evaluation", number="1.16", text="In cases of abnormal execution (network failure, " +
|
|
"unhandled error, etc) the reason field in the evaluation details SHOULD indicate an error.")
|
|
@Disabled
|
|
@Test void detail_flags() {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.17", text="The evaluation options structure's hooks field denotes " +
|
|
"a collection of hooks that the client MUST execute for the respective flag evaluation, in addition to " +
|
|
"those already configured.")
|
|
@Test void hooks() {
|
|
Client c = _client();
|
|
Hook clientHook = mock(Hook.class);
|
|
Hook invocationHook = mock(Hook.class);
|
|
c.registerHooks(clientHook);
|
|
c.getBooleanValue("key", false, null, FlagEvaluationOptions.builder()
|
|
.hook(invocationHook)
|
|
.build());
|
|
verify(clientHook, times(1)).before(any());
|
|
verify(invocationHook, times(1)).before(any());
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.18", text="Methods, functions, or operations on the client MUST " +
|
|
"NOT throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the " +
|
|
"default value in the event of abnormal execution. Exceptions include functions or methods for the " +
|
|
"purposes for configuration or setup.")
|
|
@Test void broken_provider() {
|
|
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
|
|
api.setProvider(new AlwaysBrokenProvider());
|
|
Client c = api.getClient();
|
|
assertFalse(c.getBooleanValue("key", false));
|
|
|
|
}
|
|
@Specification(spec="flag evaluation", number="1.19", text="In the case of abnormal execution, the client SHOULD " +
|
|
"log an informative error message.")
|
|
@Disabled("Not actually sure how to mock out the slf4j logger")
|
|
@Test void log_on_error() throws NotImplementedException {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
@Specification(spec="flag evaluation", number="1.21", text="The client MUST transform the evaluation context using " +
|
|
"the provider's context transformer function, before passing the result of the transformation to the " +
|
|
"provider's flag resolution functions.")
|
|
void todo() {}
|
|
|
|
@Specification(spec="flag evaluation", number="1.20", text="The client SHOULD provide asynchronous or non-blocking " +
|
|
"mechanisms for flag evaluation.")
|
|
@Disabled @Test void explicitly_not_doing() {
|
|
|
|
}
|
|
}
|