Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified mvnw
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public synchronized void initialize(EvaluationContext evaluationContext) throws

@Override
public synchronized void shutdown() {
if (!initialized) {
if (!this.initialized) {
return;
}

Expand Down Expand Up @@ -162,6 +162,12 @@ private void handleStateTransition(ProviderState oldState, ProviderState newStat
log.debug("Init completed");
return;
}
// we got shutdown, not checking oldState as behavior remains the same for shutdown
if (ProviderState.NOT_READY.equals(newState)) {
// nothing to do
log.debug("shutdown completed");
return;
}
// configuration changed
if (ProviderState.READY.equals(oldState) && ProviderState.READY.equals(newState)) {
log.debug("Configuration changed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void shutdown() throws Exception {
this.channel.awaitTermination(this.deadline, TimeUnit.MILLISECONDS);
log.warn(String.format("Unable to shut down channel by %d deadline", this.deadline));
}
this.stateConsumer.accept(ProviderState.NOT_READY);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void init() throws Exception {
public void shutdown() throws InterruptedException {
flagStore.shutdown();
this.connected.set(false);
stateConsumer.accept(ProviderState.NOT_READY);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
Expand All @@ -16,22 +17,29 @@

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.concurrent.LinkedBlockingQueue;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import com.google.protobuf.Struct;

import dev.openfeature.contrib.providers.flagd.resolver.Resolver;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcConnector;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcResolver;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache;
import dev.openfeature.contrib.providers.flagd.resolver.process.InProcessResolver;
import dev.openfeature.contrib.providers.flagd.resolver.process.MockStorage;
import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag;
import dev.openfeature.contrib.providers.flagd.resolver.process.storage.StorageState;
import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc;
import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanRequest;
import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanResponse;
Expand Down Expand Up @@ -824,6 +832,43 @@ void initializationAndShutdown() throws Exception{
verify(resolverMock, times(1)).shutdown();
}

@Test
void test_state_on_grpc_resolver_shutdown() throws Exception {
// setup mock provider
final FlagdProvider grpcProvider = Mockito.spy(FlagdProvider.class);
try {
doAnswer(invocation -> {
final Field stateField = FlagdProvider.class.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(grpcProvider, ProviderState.READY);

final Field intializedField = FlagdProvider.class.getDeclaredField("initialized");
intializedField.setAccessible(true);
intializedField.set(grpcProvider, true);

return null;
}).when(grpcProvider).initialize(any());
} catch (Exception e) {
throw new RuntimeException(e);
}

grpcProvider.initialize(new ImmutableContext());
assertEquals(ProviderState.READY, grpcProvider.getState());
grpcProvider.shutdown();
assertEquals(ProviderState.NOT_READY, grpcProvider.getState());
}

@Test
void test_state_on_in_process_resolver_shutdown() throws Exception {
// setup mock in-process provider
FlagdProvider inProcessProvider = createInProcessProvider();

inProcessProvider.initialize(new ImmutableContext());
assertEquals(ProviderState.READY, inProcessProvider.getState());
inProcessProvider.shutdown();
assertEquals(ProviderState.NOT_READY, inProcessProvider.getState());
}


// test helper

Expand Down Expand Up @@ -863,4 +908,29 @@ private FlagdProvider createProvider(GrpcConnector grpc, Cache cache, Supplier<P
return provider;
}

// Create an in process provider
private FlagdProvider createInProcessProvider() {

final FlagdOptions flagdOptions = FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.deadline(1000)
.build();
final FlagdProvider provider = new FlagdProvider(flagdOptions);
final MockStorage mockStorage = new MockStorage(new HashMap<String, FeatureFlag>(), new LinkedBlockingQueue<StorageState>(Arrays.asList(StorageState.OK)));

try {
final Field flagResolver = FlagdProvider.class.getDeclaredField("flagResolver");
flagResolver.setAccessible(true);
final Resolver resolver = (Resolver) flagResolver.get(provider);

final Field flagStore = InProcessResolver.class.getDeclaredField("flagStore");
flagStore.setAccessible(true);
flagStore.set(resolver, mockStorage);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}

return provider;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
import java.util.Map;
import java.util.concurrent.BlockingQueue;

class MockStorage implements Storage {
public class MockStorage implements Storage {

private final Map<String, FeatureFlag> mockFlags;
private final BlockingQueue<StorageState> mockQueue;

MockStorage(Map<String, FeatureFlag> mockFlags, BlockingQueue<StorageState> mockQueue) {
public MockStorage(Map<String, FeatureFlag> mockFlags, BlockingQueue<StorageState> mockQueue) {
this.mockFlags = mockFlags;
this.mockQueue = mockQueue;
}

MockStorage(Map<String, FeatureFlag> flagMap) {
public MockStorage(Map<String, FeatureFlag> flagMap) {
this.mockFlags = flagMap;
this.mockQueue = null;
}
Expand Down