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": {} + } + } +}