Skip to content

Commit d145f75

Browse files
committed
fixup: add provider-status tests
Signed-off-by: Simon Schrottner <[email protected]>
1 parent f3512f9 commit d145f75

File tree

2 files changed

+145
-3
lines changed

2 files changed

+145
-3
lines changed

src/test/java/dev/openfeature/sdk/e2e/GherkinSpecTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
1717
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.sdk.e2e.steps")
1818
@ConfigurationParameter(key = OBJECT_FACTORY_PROPERTY_NAME, value = "io.cucumber.picocontainer.PicoFactory")
19-
@ExcludeTags({"deprecated", "provider-status", "reason-codes", "async", "immutability", "evaluation-options"})
19+
@ExcludeTags({"deprecated", "reason-codes", "async", "immutability", "evaluation-options"})
2020
public class GherkinSpecTest {}
Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11
package dev.openfeature.sdk.e2e.steps;
22

33
import static dev.openfeature.sdk.testutils.TestFlagsUtils.buildFlags;
4+
import static org.assertj.core.api.Assertions.assertThat;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.ArgumentMatchers.anyString;
7+
import static org.mockito.Mockito.doAnswer;
8+
import static org.mockito.Mockito.doThrow;
9+
import static org.mockito.Mockito.mock;
10+
import static org.mockito.Mockito.spy;
11+
import static org.mockito.Mockito.when;
412

13+
import dev.openfeature.sdk.Client;
14+
import dev.openfeature.sdk.ErrorCode;
15+
import dev.openfeature.sdk.EventProvider;
16+
import dev.openfeature.sdk.FeatureProvider;
517
import dev.openfeature.sdk.OpenFeatureAPI;
18+
import dev.openfeature.sdk.ProviderEvaluation;
19+
import dev.openfeature.sdk.ProviderEventDetails;
20+
import dev.openfeature.sdk.ProviderState;
21+
import dev.openfeature.sdk.Reason;
22+
import dev.openfeature.sdk.Value;
623
import dev.openfeature.sdk.e2e.State;
24+
import dev.openfeature.sdk.exceptions.FatalError;
25+
import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
726
import dev.openfeature.sdk.providers.memory.Flag;
827
import dev.openfeature.sdk.providers.memory.InMemoryProvider;
928
import io.cucumber.java.en.Given;
29+
import io.cucumber.java.en.Then;
30+
import org.awaitility.Awaitility;
1031
import java.util.Map;
32+
import java.util.concurrent.atomic.AtomicBoolean;
1133

1234
public class ProviderSteps {
1335
private final State state;
@@ -16,11 +38,131 @@ public ProviderSteps(State state) {
1638
this.state = state;
1739
}
1840

19-
@Given("a stable provider")
20-
public void aStableProvider() {
41+
@Given("a {} provider")
42+
public void a_provider_with_status(String providerType) throws Exception {
43+
// Normalize input to handle both single word and quoted strings
44+
String normalizedType = providerType.toLowerCase().replaceAll("[\"\\s]+", " ").trim();
45+
46+
switch (normalizedType) {
47+
case "not ready":
48+
setupMockProvider(ErrorCode.PROVIDER_NOT_READY, "Provider in not ready state",
49+
ProviderState.NOT_READY);
50+
break;
51+
case "stable":
52+
case "ready":
53+
setupStableProvider();
54+
break;
55+
case "fatal":
56+
setupMockProvider(ErrorCode.PROVIDER_FATAL, "Provider in fatal state",
57+
ProviderState.FATAL);
58+
break;
59+
case "error":
60+
setupMockProvider(ErrorCode.GENERAL, "Provider in error state", ProviderState.ERROR);
61+
break;
62+
case "stale":
63+
setupMockProvider(null, null, ProviderState.STALE);
64+
break;
65+
default:
66+
throw new IllegalArgumentException("Unsupported provider type: " + providerType);
67+
}
68+
}
69+
70+
// ===============================
71+
// Provider Status Assertion Steps
72+
// ===============================
73+
74+
@Then("the provider status should be {string}")
75+
public void the_provider_status_should_be(String expectedStatus) {
76+
ProviderState actualStatus = state.client.getProviderState();
77+
ProviderState expected = ProviderState.valueOf(expectedStatus);
78+
assertThat(actualStatus).isEqualTo(expected);
79+
}
80+
81+
// ===============================
82+
// Helper Methods
83+
// ===============================
84+
85+
private void setupStableProvider() throws Exception {
2186
Map<String, Flag<?>> flags = buildFlags();
2287
InMemoryProvider provider = new InMemoryProvider(flags);
2388
OpenFeatureAPI.getInstance().setProviderAndWait(provider);
2489
state.client = OpenFeatureAPI.getInstance().getClient();
2590
}
91+
92+
private void setupMockProvider(ErrorCode errorCode, String errorMessage,
93+
ProviderState providerState)
94+
throws Exception {
95+
EventProvider mockProvider = spy(EventProvider.class);
96+
97+
switch (providerState) {
98+
case NOT_READY:
99+
doAnswer(invocationOnMock -> {
100+
while(true) {}
101+
}).when(mockProvider).initialize(any());
102+
break;
103+
case FATAL:
104+
doThrow(new FatalError(errorMessage)).when(mockProvider).initialize(any());
105+
break;
106+
}
107+
// Configure all evaluation methods with a single helper
108+
configureMockEvaluations(mockProvider, errorCode, errorMessage);
109+
110+
OpenFeatureAPI.getInstance().setProvider(providerState.name(), mockProvider);
111+
state.client = OpenFeatureAPI.getInstance().getClient(providerState.name());
112+
113+
ProviderEventDetails details = ProviderEventDetails.builder().errorCode(errorCode).build();
114+
switch (providerState) {
115+
case FATAL:
116+
case ERROR:
117+
mockProvider.emitProviderReady(details);
118+
mockProvider.emitProviderError(details);
119+
break;
120+
case STALE:
121+
mockProvider.emitProviderReady(details);
122+
mockProvider.emitProviderStale(details);
123+
break;
124+
default:
125+
}
126+
Awaitility.await().until(() -> {
127+
ProviderState providerState1 = state.client.getProviderState();
128+
return providerState1 == providerState;
129+
});
130+
}
131+
132+
private void configureMockEvaluations(FeatureProvider mockProvider, ErrorCode errorCode, String errorMessage) {
133+
// Configure Boolean evaluation
134+
when(mockProvider.getBooleanEvaluation(anyString(), any(Boolean.class), any()))
135+
.thenAnswer(invocation -> createProviderEvaluation(invocation.getArgument(1), errorCode, errorMessage
136+
));
137+
138+
// Configure String evaluation
139+
when(mockProvider.getStringEvaluation(anyString(), any(String.class), any()))
140+
.thenAnswer(invocation -> createProviderEvaluation(invocation.getArgument(1), errorCode, errorMessage
141+
));
142+
143+
// Configure Integer evaluation
144+
when(mockProvider.getIntegerEvaluation(anyString(), any(Integer.class), any()))
145+
.thenAnswer(invocation -> createProviderEvaluation(invocation.getArgument(1), errorCode, errorMessage
146+
));
147+
148+
// Configure Double evaluation
149+
when(mockProvider.getDoubleEvaluation(anyString(), any(Double.class), any()))
150+
.thenAnswer(invocation -> createProviderEvaluation(invocation.getArgument(1), errorCode, errorMessage
151+
));
152+
153+
// Configure Object evaluation
154+
when(mockProvider.getObjectEvaluation(anyString(), any(Value.class), any()))
155+
.thenAnswer(invocation -> createProviderEvaluation(invocation.getArgument(1), errorCode, errorMessage
156+
));
157+
}
158+
159+
private <T> ProviderEvaluation<T> createProviderEvaluation(T defaultValue, ErrorCode errorCode,
160+
String errorMessage) {
161+
return ProviderEvaluation.<T>builder()
162+
.value(defaultValue)
163+
.errorCode(errorCode)
164+
.errorMessage(errorMessage)
165+
.reason(Reason.ERROR.toString())
166+
.build();
167+
}
26168
}

0 commit comments

Comments
 (0)