diff --git a/src/main/java/dev/openfeature/sdk/EventSupport.java b/src/main/java/dev/openfeature/sdk/EventSupport.java index 1a7c4d870..1458a610f 100644 --- a/src/main/java/dev/openfeature/sdk/EventSupport.java +++ b/src/main/java/dev/openfeature/sdk/EventSupport.java @@ -2,6 +2,7 @@ import dev.openfeature.sdk.internal.ConfigurableThreadFactory; import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -117,7 +118,7 @@ public void removeGlobalHandler(ProviderEvent event, Consumer hand * @return set of domain names */ public Set getAllDomainNames() { - return this.handlerStores.keySet(); + return new HashSet<>(this.handlerStores.keySet()); } /** diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java index 2fdb4e3f0..b1f5b6ed8 100644 --- a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java +++ b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java @@ -1,16 +1,22 @@ package dev.openfeature.sdk; +import static dev.openfeature.sdk.ProviderEvent.PROVIDER_CONFIGURATION_CHANGED; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import dev.openfeature.sdk.testutils.testProvider.TestProvider; import java.util.HashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class OpenFeatureAPITest { @@ -115,4 +121,35 @@ void featureProviderTrackIsCalled() throws Exception { verify(featureProvider, times(2)).getMetadata(); verify(featureProvider).track(any(), any(), any()); } + + @Test + @DisplayName( + "Domainless provider initialized after domain provider - OnConfigurationChanged handlers are invoked for both providers after emitting PROVIDER_CONFIGURATION_CHANGED") + void onConfigurationChangedAreCalledForDomainlessAndDomainProviders() throws InterruptedException { + EventProvider domainlessFeatureProvider = spy(EventProvider.class); + EventProvider featureProvider = spy(EventProvider.class); + + Client domainlessClient = api.getClient(); + Client client = api.getClient("domain"); + + CountDownLatch domainlessLatch = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); + + domainlessClient.onProviderConfigurationChanged(eventDetails -> domainlessLatch.countDown()); + client.onProviderConfigurationChanged(eventDetails -> latch.countDown()); + + api.setProviderAndWait("domain", featureProvider); + api.setProviderAndWait(domainlessFeatureProvider); + + featureProvider.emit(PROVIDER_CONFIGURATION_CHANGED, mock(ProviderEventDetails.class)); + domainlessFeatureProvider.emit(PROVIDER_CONFIGURATION_CHANGED, mock(ProviderEventDetails.class)); + + boolean domainlessCompleted = domainlessLatch.await(5, TimeUnit.SECONDS); + assertTrue( + domainlessCompleted, + "onProviderConfigurationChanged was not be invoked for a client with default domain"); + + boolean completed = latch.await(5, TimeUnit.SECONDS); + assertTrue(completed, "onProviderConfigurationChanged was not be invoked for a client with domain 'domain'"); + } }