diff --git a/pom.xml b/pom.xml
index 6af087035..a315bf85e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -203,6 +203,13 @@
test
+
+ dev.openfeature.contrib.providers
+ flagd
+ 0.11.18
+ test
+
+
diff --git a/src/main/java/dev/openfeature/sdk/EventProvider.java b/src/main/java/dev/openfeature/sdk/EventProvider.java
index 4ccac184e..596ef16ca 100644
--- a/src/main/java/dev/openfeature/sdk/EventProvider.java
+++ b/src/main/java/dev/openfeature/sdk/EventProvider.java
@@ -112,7 +112,7 @@ public Awaitable emit(final ProviderEvent event, final ProviderEventDetails deta
*
* @param details The details of the event
*/
- public Awaitable emitProviderReady(ProviderEventDetails details) {
+ /*public*/ Awaitable emitProviderReady(ProviderEventDetails details) {
return emit(ProviderEvent.PROVIDER_READY, details);
}
diff --git a/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java b/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java
index 19108bde5..a29a0fb0f 100644
--- a/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java
+++ b/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java
@@ -184,7 +184,7 @@ void shouldPutTheProviderInStateReadyAfterEmittingReadyEvent() {
assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
provider.emitProviderStale(ProviderEventDetails.builder().build()).await();
assertThat(client.getProviderState()).isEqualTo(ProviderState.STALE);
- provider.emitProviderReady(ProviderEventDetails.builder().build()).await();
- assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
+ // provider.emitProviderReady(ProviderEventDetails.builder().build()).await();
+ // assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
}
}
diff --git a/src/test/java/dev/openfeature/sdk/NoBreakingChangesTest.java b/src/test/java/dev/openfeature/sdk/NoBreakingChangesTest.java
new file mode 100644
index 000000000..bc921747d
--- /dev/null
+++ b/src/test/java/dev/openfeature/sdk/NoBreakingChangesTest.java
@@ -0,0 +1,88 @@
+package dev.openfeature.sdk;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import dev.openfeature.contrib.providers.flagd.Config;
+import dev.openfeature.contrib.providers.flagd.FlagdOptions;
+import dev.openfeature.contrib.providers.flagd.FlagdProvider;
+import java.util.HashSet;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class NoBreakingChangesTest {
+
+ private AtomicBoolean isTestRunning;
+ private ConcurrentLinkedQueue uncaughtExceptions;
+ private Thread threadWatcher;
+
+ @BeforeEach
+ void setup() {
+ final var isRunning = new AtomicBoolean(true);
+ final var uncaught = new ConcurrentLinkedQueue();
+ uncaughtExceptions = uncaught;
+ isTestRunning = isRunning;
+
+ threadWatcher = new Thread(() -> {
+ var seenThreads = new HashSet();
+ while (isRunning.get()) {
+ var stacks = Thread.getAllStackTraces();
+ for (var thread : stacks.keySet()) {
+ if (seenThreads.add(thread)) {
+ thread.setUncaughtExceptionHandler((thread1, throwable) -> {
+ uncaught.add(throwable);
+ });
+ }
+ }
+ }
+ });
+ threadWatcher.setDaemon(true);
+ threadWatcher.start();
+ }
+
+ @Test
+ void noBreakingChanges() throws InterruptedException {
+ try {
+ var testProvider = new FlagdProvider(FlagdOptions.builder()
+ .resolverType(Config.Resolver.FILE)
+ .offlineFlagSourcePath(NoBreakingChangesTest.class
+ .getResource("/testFlags.json")
+ .getPath())
+ .build());
+ var api = new OpenFeatureAPI();
+ api.setProviderAndWait(testProvider);
+
+ var client = api.getClient();
+ var flagFound = client.getBooleanDetails("basic-boolean", false);
+ assertThat(flagFound).isNotNull();
+ assertThat(flagFound.getValue()).isTrue();
+ assertThat(flagFound.getVariant()).isEqualTo("true");
+ assertThat(flagFound.getReason()).isEqualTo(Reason.STATIC.toString());
+
+ var flagNotFound = client.getStringDetails("unknown", "asd");
+ assertThat(flagNotFound).isNotNull();
+ assertThat(flagNotFound.getValue()).isEqualTo("asd");
+ assertThat(flagNotFound.getVariant()).isNull();
+ assertThat(flagNotFound.getReason()).isEqualTo(Reason.ERROR.toString());
+ assertThat(flagNotFound.getErrorCode()).isEqualTo(ErrorCode.FLAG_NOT_FOUND);
+
+ testProvider.shutdown();
+ api.shutdown();
+
+ } finally {
+ assertNoUncaughtExceptions();
+ }
+ }
+
+ private void assertNoUncaughtExceptions() throws InterruptedException {
+ try {
+ Thread.sleep(1000); // wait a bit for any uncaught exceptions to be reported
+
+ isTestRunning.set(false);
+ threadWatcher.join(1000);
+ } finally {
+ assertThat(uncaughtExceptions).isEmpty();
+ }
+ }
+}
diff --git a/src/test/java/dev/openfeature/sdk/e2e/steps/ProviderSteps.java b/src/test/java/dev/openfeature/sdk/e2e/steps/ProviderSteps.java
index f22a0811a..f089e4ab3 100644
--- a/src/test/java/dev/openfeature/sdk/e2e/steps/ProviderSteps.java
+++ b/src/test/java/dev/openfeature/sdk/e2e/steps/ProviderSteps.java
@@ -112,11 +112,11 @@ private void setupMockProvider(ErrorCode errorCode, String errorMessage, Provide
switch (providerState) {
case FATAL:
case ERROR:
- mockProvider.emitProviderReady(details).await();
+ // mockProvider.emitProviderReady(details).await();
mockProvider.emitProviderError(details).await();
break;
case STALE:
- mockProvider.emitProviderReady(details).await();
+ // mockProvider.emitProviderReady(details).await();
mockProvider.emitProviderStale(details).await();
break;
default:
diff --git a/src/test/java/dev/openfeature/sdk/testutils/TestStackedEmitCallsProvider.java b/src/test/java/dev/openfeature/sdk/testutils/TestStackedEmitCallsProvider.java
index d1bf65c57..350b6d226 100644
--- a/src/test/java/dev/openfeature/sdk/testutils/TestStackedEmitCallsProvider.java
+++ b/src/test/java/dev/openfeature/sdk/testutils/TestStackedEmitCallsProvider.java
@@ -5,7 +5,6 @@
import dev.openfeature.sdk.Metadata;
import dev.openfeature.sdk.ProviderEvaluation;
import dev.openfeature.sdk.ProviderEvent;
-import dev.openfeature.sdk.ProviderEventDetails;
import dev.openfeature.sdk.Value;
import java.util.function.Consumer;
@@ -38,7 +37,7 @@ private void onProviderEvent(ProviderEvent providerEvent) {
* This line deadlocked in the original implementation without the emitterExecutor see
* https://github.com/open-feature/java-sdk/issues/1299
*/
- emitProviderReady(ProviderEventDetails.builder().build());
+ // emitProviderReady(ProviderEventDetails.builder().build());
}
}
}
diff --git a/src/test/resources/testFlags.json b/src/test/resources/testFlags.json
new file mode 100644
index 000000000..ed5005823
--- /dev/null
+++ b/src/test/resources/testFlags.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://flagd.dev/schema/v0/flags.json",
+ "flags": {
+ "basic-boolean": {
+ "state": "ENABLED",
+ "defaultVariant": "true",
+ "variants": {
+ "true": true,
+ "false": false
+ },
+ "targeting": {}
+ }
+ }
+}