Skip to content

Commit 30c5ac4

Browse files
committed
Support for spec validation
1 parent 01bba2d commit 30c5ac4

File tree

6 files changed

+171
-104
lines changed

6 files changed

+171
-104
lines changed

lib/src/test/java/dev/openfeature/javasdk/EvalContextTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import static org.junit.jupiter.api.Assertions.assertEquals;
88

99
public class EvalContextTests {
10-
@Specification(spec="Evaluation Context", number="3.1",
10+
@Specification(number="3.1",
1111
text="The `evaluation context` structure **MUST** define an optional `targeting key` field of " +
1212
"type string, identifying the subject of the flag evaluation.")
1313
@Test void requires_targeting_key() {
@@ -16,7 +16,7 @@ public class EvalContextTests {
1616
assertEquals("targeting-key", ec.getTargetingKey());
1717
}
1818

19-
@Specification(spec="Evaluation Context", number="3.2", text="The evaluation context MUST support the inclusion of " +
19+
@Specification(number="3.2", text="The evaluation context MUST support the inclusion of " +
2020
"custom fields, having keys of type `string`, and " +
2121
"values of type `boolean | string | number | datetime | structure`.")
2222
@Test void eval_context() {
@@ -36,7 +36,7 @@ public class EvalContextTests {
3636
assertEquals(dt, ec.getDatetimeAttribute("dt"));
3737
}
3838

39-
@Specification(spec="Evaluation Context", number="3.2", text="The evaluation context MUST support the inclusion of " +
39+
@Specification(number="3.2", text="The evaluation context MUST support the inclusion of " +
4040
"custom fields, having keys of type `string`, and " +
4141
"values of type `boolean | string | number | datetime | structure`.")
4242
@Test void eval_context__structure() {

lib/src/test/java/dev/openfeature/javasdk/FlagEvaluationSpecTests.java

Lines changed: 25 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,17 @@ Client _client() {
1616
return api.getClient();
1717
}
1818

19-
@Specification(spec="flag evaluation", number="1.1",
20-
text="The API, and any state it maintains SHOULD exist as a global singleton, " +
21-
"even in cases wherein multiple versions of the API are present at runtime.")
2219
@Test void global_singleton() {
2320
assertSame(OpenFeatureAPI.getInstance(), OpenFeatureAPI.getInstance());
2421
}
2522

26-
@Specification(spec="flag evaluation", number="1.2",
27-
text="The API MUST provide a function to set the global provider singleton, " +
28-
"which accepts an API-conformant provider implementation.")
29-
@Specification(spec="flag evaluation", number="1.4",
30-
text="The API MUST provide a function for retrieving the provider implementation.")
3123
@Test void provider() {
3224
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
3325
FeatureProvider mockProvider = mock(FeatureProvider.class);
3426
api.setProvider(mockProvider);
3527
assertEquals(mockProvider, api.getProvider());
3628
}
3729

38-
@Specification(spec="flag evaluation", number="1.3", text="The API MUST provide a function to add hooks which " +
39-
"accepts one or more API-conformant hooks, and appends them to the collection of any previously added hooks." +
40-
"When new hooks are added, previously added hooks are not removed.")
4130
@Test void hook_addition() {
4231
Hook h1 = mock(Hook.class);
4332
Hook h2 = mock(Hook.class);
@@ -52,17 +41,12 @@ Client _client() {
5241
assertEquals(h2, api.getApiHooks().get(1));
5342
}
5443

55-
@Specification(spec="flag evaluation", number="1.5", text="The API MUST provide a function for creating a client " +
56-
"which accepts the following options: name (optional): A logical string identifier for the client.")
5744
@Test void namedClient() {
5845
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
5946
Client c = api.getClient("Sir Calls-a-lot");
6047
// TODO: Doesn't say that you can *get* the client name.. which seems useful?
6148
}
6249

63-
@Specification(spec="flag evaluation", number="1.6", text="The client MUST provide a method to add hooks which " +
64-
"accepts one or more API-conformant hooks, and appends them to the collection of any previously added hooks. " +
65-
"When new hooks are added, previously added hooks are not removed.")
6650
@Test void hookRegistration() {
6751
Client c = _client();
6852
Hook m1 = mock(Hook.class);
@@ -75,11 +59,6 @@ Client _client() {
7559
assertTrue(hooks.contains(m2));
7660
}
7761

78-
@Specification(spec="flag evaluation", number="1.7", text="The client MUST provide methods for flag evaluation, with" +
79-
" parameters flag key (string, required), default value (boolean | number | string | structure, required), " +
80-
"evaluation context (optional), and evaluation options (optional), which returns the flag value.")
81-
@Specification(spec="flag evaluation", number="1.8.1",text="The client MUST provide methods for typed flag " +
82-
"evaluation, including boolean, numeric, string, and structure.")
8362
@Test void value_flags() {
8463
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
8564
api.setProvider(new DoSomethingProvider());
@@ -104,17 +83,6 @@ Client _client() {
10483
assertEquals(null, c.getObjectValue(key, new Node<Integer>(), new EvaluationContext(), FlagEvaluationOptions.builder().build()));
10584
}
10685

107-
@Specification(spec="flag evaluation", number="1.9", text="The client MUST provide methods for detailed flag value " +
108-
"evaluation with parameters flag key (string, required), default value (boolean | number | string | " +
109-
"structure, required), evaluation context (optional), and evaluation options (optional), which returns an " +
110-
"evaluation details structure.")
111-
@Specification(spec="flag evaluation", number="1.10", text="The evaluation details structure's value field MUST " +
112-
"contain the evaluated flag value.")
113-
@Specification(spec="flag evaluation", number="1.11.1", text="The evaluation details structure SHOULD accept a " +
114-
"generic argument (or use an equivalent language feature) which indicates the type of the wrapped value " +
115-
"field.")
116-
@Specification(spec="flag evaluation", number="1.12", text="The evaluation details structure's flag key field MUST " +
117-
"contain the flag key argument passed to the detailed flag evaluation method.")
11886
@Test void detail_flags() {
11987
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
12088
api.setProvider(new DoSomethingProvider());
@@ -148,9 +116,6 @@ Client _client() {
148116
assertEquals(id, c.getIntegerDetails(key, 4, new EvaluationContext(), FlagEvaluationOptions.builder().build()));
149117
}
150118

151-
@Specification(spec="flag evaluation", number="1.17", text="The evaluation options structure's hooks field denotes " +
152-
"a collection of hooks that the client MUST execute for the respective flag evaluation, in addition to " +
153-
"those already configured.")
154119
@Test void hooks() {
155120
Client c = _client();
156121
Hook clientHook = mock(Hook.class);
@@ -163,42 +128,45 @@ Client _client() {
163128
verify(invocationHook, times(1)).before(any(), any());
164129
}
165130

166-
@Specification(spec="flag evaluation", number="1.18", text="Methods, functions, or operations on the client MUST " +
167-
"NOT throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the " +
168-
"default value in the event of abnormal execution. Exceptions include functions or methods for the " +
169-
"purposes for configuration or setup.")
170131
@Test void broken_provider() {
171132
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
172133
api.setProvider(new AlwaysBrokenProvider());
173134
Client c = api.getClient();
174135
assertFalse(c.getBooleanValue("key", false));
175136

176137
}
177-
@Specification(spec="flag evaluation", number="1.19", text="In the case of abnormal execution, the client SHOULD " +
178-
"log an informative error message.")
179138
@Disabled("Not actually sure how to mock out the slf4j logger")
180139
@Test void log_on_error() throws NotImplementedException {
181140
throw new NotImplementedException();
182141
}
183142

184-
@Specification(spec="flag evaluation", number="1.21", text="The client MUST transform the evaluation context using " +
185-
"the provider's context transformer function, before passing the result of the transformation to the " +
186-
"provider's flag resolution functions.")
187-
@Specification(spec="flag evaluation", number="1.13", text="In cases of normal execution, the evaluation details " +
188-
"structure's variant field MUST contain the value of the variant field in the flag resolution structure " +
189-
"returned by the configured provider, if the field is set.")
190-
@Specification(spec="flag evaluation", number="1.14", text="In cases of normal execution, the evaluation details " +
191-
"structure's reason field MUST contain the value of the reason field in the flag resolution structure " +
192-
"returned by the configured provider, if the field is set.")
193-
@Specification(spec="flag evaluation", number="1.15", text="In cases of abnormal execution, the evaluation details " +
194-
"structure's error code field MUST identify an error occurred during flag evaluation, having possible " +
195-
"values PROVIDER_NOT_READY, FLAG_NOT_FOUND, PARSE_ERROR, TYPE_MISMATCH, or GENERAL.")
196-
@Specification(spec="flag evaluation", number="1.16", text="In cases of abnormal execution (network failure, " +
197-
"unhandled error, etc) the reason field in the evaluation details SHOULD indicate an error.")
143+
@Specification(number="1.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.")
144+
@Specification(number="1.1.2", text="The API MUST provide a function to set the global provider singleton, which accepts an API-conformant provider implementation.")
145+
@Specification(number="1.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.")
146+
@Specification(number="1.1.4", text="The API MUST provide a function for retrieving the metadata field of the configured provider.")
147+
@Specification(number="1.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.")
148+
@Specification(number="1.1.6", text="The client creation function MUST NOT throw, or otherwise abnormally terminate.")
149+
@Specification(number="1.2.1", 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.")
150+
@Specification(number="1.2.2", text="The client interface MUST define a metadata member or accessor, containing an immutable name field or accessor of type string, which corresponds to the name value supplied during client creation.")
151+
@Specification(number="1.3.1", 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.")
152+
@Specification(number="1.3.2.1", text="The client MUST provide methods for typed flag evaluation, including boolean, numeric, string, and structure.")
153+
@Specification(number="1.3.3", text="The client SHOULD guarantee the returned value of any typed flag evaluation method is of the expected type. If the value returned by the underlying provider implementation does not match the expected type, it's to be considered abnormal execution, and the supplied default value should be returned.")
154+
@Specification(number="1.4.1", 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.")
155+
@Specification(number="1.4.2", text="The evaluation details structure's value field MUST contain the evaluated flag value.")
156+
@Specification(number="1.4.3.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.")
157+
@Specification(number="1.4.4", text="The evaluation details structure's flag key field MUST contain the flag key argument passed to the detailed flag evaluation method.")
158+
@Specification(number="1.4.5", 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.")
159+
@Specification(number="1.4.6", 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.")
160+
@Specification(number="1.4.7", text="In cases of abnormal execution, the evaluation details structure's error code field MUST contain a string identifying an error occurred during flag evaluation and the nature of the error.")
161+
@Specification(number="1.4.8", text="In cases of abnormal execution (network failure, unhandled error, etc) the reason field in the evaluation details SHOULD indicate an error.")
162+
@Specification(number="1.4.9", 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.")
163+
@Specification(number="1.4.10", text="In the case of abnormal execution, the client SHOULD log an informative error message.")
164+
@Specification(number="1.4.11", text="The client SHOULD provide asynchronous or non-blocking mechanisms for flag evaluation.")
165+
@Specification(number="1.5.1", text="The evaluation options structure's hooks field denotes an ordered collection of hooks that the client MUST execute for the respective flag evaluation, in addition to those already configured.")
166+
@Specification(number="1.6.1", text="The client SHOULD transform the evaluation context using the provider's context transformer function if one is defined, before passing the result of the transformation to the provider's flag resolution functions.")
167+
198168
@Test @Disabled void todo() {}
199169

200-
@Specification(spec="flag evaluation", number="1.20", text="The client SHOULD provide asynchronous or non-blocking " +
201-
"mechanisms for flag evaluation.")
202170
@Disabled("We're operating in a one request per thread model")
203171
@Test void explicitly_not_doing() {
204172

0 commit comments

Comments
 (0)