diff --git a/.gitmodules b/.gitmodules index 619d4fff1..72b2fc1fd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,7 +4,7 @@ [submodule "providers/flagd/test-harness"] path = providers/flagd/test-harness url = https://github.com/open-feature/test-harness.git - branch = v1.1.1 + branch = v1.3.3 [submodule "providers/flagd/spec"] path = providers/flagd/spec url = https://github.com/open-feature/spec.git diff --git a/providers/flagd/pom.xml b/providers/flagd/pom.xml index 9c31fe0de..c7679ef3a 100644 --- a/providers/flagd/pom.xml +++ b/providers/flagd/pom.xml @@ -150,14 +150,13 @@ test - org.testcontainers - toxiproxy - 1.20.4 + io.rest-assured + rest-assured + 5.5.0 test - - + org.slf4j slf4j-simple 2.0.16 @@ -254,7 +253,7 @@ com.google.protobuf:protoc:3.25.5:exe:${os.detected.classifier} grpc-java - io.grpc:protoc-gen-grpc-java:1.68.2:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:1.69.1:exe:${os.detected.classifier} ${project.basedir}/schemas/protobuf/ diff --git a/providers/flagd/schemas b/providers/flagd/schemas index bb763438a..37baa2cde 160000 --- a/providers/flagd/schemas +++ b/providers/flagd/schemas @@ -1 +1 @@ -Subproject commit bb763438abc5b0e97dc26a795bc72b7a9f3e4020 +Subproject commit 37baa2cdea48a5ac614ba3e718b7d02ad4120611 diff --git a/providers/flagd/spec b/providers/flagd/spec index 8d6eeb324..5b0706598 160000 --- a/providers/flagd/spec +++ b/providers/flagd/spec @@ -1 +1 @@ -Subproject commit 8d6eeb3247600f6f66ffc92afa50ebde75b4d3ce +Subproject commit 5b070659853062262e618c74da5b640555f9ae18 diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java index bbf7674f1..e899e7005 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java @@ -209,7 +209,7 @@ EvaluationContext getEnrichedContext() { private void onProviderEvent(FlagdProviderEvent flagdProviderEvent) { synchronized (eventsLock) { - log.info("FlagdProviderEvent: {}", flagdProviderEvent); + log.info("FlagdProviderEvent: {}", flagdProviderEvent.getEvent()); eventsLock.syncMetadata = flagdProviderEvent.getSyncMetadata(); if (flagdProviderEvent.getSyncMetadata() != null) { eventsLock.enrichedContext = contextEnricher.apply(flagdProviderEvent.getSyncMetadata()); diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelMonitor.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelMonitor.java index 1b201d640..7e27da34b 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelMonitor.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelMonitor.java @@ -47,8 +47,11 @@ public static void monitorChannelState( log.debug("onConnectionLost is null"); } } - // Re-register the state monitor to watch for the next state transition. - monitorChannelState(currentState, channel, onConnectionReady, onConnectionLost); + if (currentState != ConnectivityState.SHUTDOWN) { + log.debug("shutting down grpc channel"); + // Re-register the state monitor to watch for the next state transition. + monitorChannelState(currentState, channel, onConnectionReady, onConnectionLost); + } }); } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java index 8b8886bf8..c1ddca3e9 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/EventStreamObserver.java @@ -64,6 +64,7 @@ public void onCompleted() {} * @param value the event stream response containing configuration change data */ private void handleConfigurationChangeEvent(EventStreamResponse value) { + log.debug("Received provider change event"); List changedFlags = new ArrayList<>(); Map data = value.getData().getFieldsMap(); @@ -80,7 +81,7 @@ private void handleConfigurationChangeEvent(EventStreamResponse value) { * Handles provider readiness events by clearing the cache (if enabled) and notifying listeners of readiness. */ private void handleProviderReadyEvent() { - log.info("Received provider ready event"); + log.debug("Received provider ready event"); onReady.accept(new FlagdProviderEvent(ProviderEvent.PROVIDER_READY)); } } diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java index 832c8a487..e76ebfb24 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/storage/connector/grpc/GrpcStreamConnector.java @@ -118,7 +118,6 @@ void observeEventStream(final BlockingQueue writeTo, final AtomicB metadataException = e; } - log.info("stream"); while (!shutdown.get()) { final GrpcResponseModel response = streamReceiver.take(); if (response.isComplete()) { diff --git a/providers/flagd/src/main/resources/simplelogger.properties b/providers/flagd/src/main/resources/simplelogger.properties new file mode 100644 index 000000000..d9d489e82 --- /dev/null +++ b/providers/flagd/src/main/resources/simplelogger.properties @@ -0,0 +1,3 @@ +org.org.slf4j.simpleLogger.defaultLogLevel=debug + +io.grpc.level=trace diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunConfigCucumberTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/ConfigCucumberTest.java similarity index 85% rename from providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunConfigCucumberTest.java rename to providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/ConfigCucumberTest.java index d5dce18fe..f031091da 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunConfigCucumberTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/ConfigCucumberTest.java @@ -1,4 +1,4 @@ -package dev.openfeature.contrib.providers.flagd.e2e; +package dev.openfeature.contrib.providers.flagd; import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; @@ -18,5 +18,5 @@ @IncludeEngines("cucumber") @SelectFile("test-harness/gherkin/config.feature") @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps") -public class RunConfigCucumberTest {} +@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps.config") +public class ConfigCucumberTest {} diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java index 0aa226fcf..2d94b3a8d 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java @@ -46,7 +46,6 @@ import dev.openfeature.sdk.Reason; import dev.openfeature.sdk.Structure; import dev.openfeature.sdk.Value; -import io.cucumber.java.AfterAll; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; @@ -60,7 +59,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; @@ -90,15 +90,15 @@ class FlagdProviderTest { .build(); private static final String STRING_VALUE = "hi!"; - private static OpenFeatureAPI api; + private OpenFeatureAPI api; - @BeforeAll - public static void init() { + @BeforeEach + public void init() { api = OpenFeatureAPI.getInstance(); } - @AfterAll - public static void cleanUp() { + @AfterEach + public void cleanUp() { api.shutdown(); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java index 0aa8f50d0..8fb83833a 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/FlagdContainer.java @@ -1,9 +1,9 @@ package dev.openfeature.contrib.providers.flagd.e2e; +import dev.openfeature.contrib.providers.flagd.Config; import java.io.File; import java.nio.file.Files; import java.util.List; -import org.apache.logging.log4j.util.Strings; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; @@ -25,19 +25,25 @@ public class FlagdContainer extends GenericContainer { } } - private String feature; - public FlagdContainer() { - this(""); + super(generateContainerName()); + this.addExposedPorts(8013, 8014, 8015, 8080); } - public FlagdContainer(String feature) { - super(generateContainerName(feature)); - this.withReuse(true); - this.feature = feature; - if (!"socket".equals(this.feature)) this.addExposedPorts(8013, 8014, 8015, 8016); + public int getPort(Config.Resolver resolver) { + switch (resolver) { + case RPC: + return getMappedPort(8013); + case IN_PROCESS: + return getMappedPort(8015); + default: + return 0; + } } + public String getLaunchpadUrl() { + return this.getHost() + ":" + this.getMappedPort(8080); + } /** * @return a {@link org.testcontainers.containers.GenericContainer} instance of envoy container using * flagd sync service as backend expose on port 9211 @@ -52,11 +58,8 @@ public static GenericContainer envoy() { .withNetworkAliases("envoy"); } - public static @NotNull String generateContainerName(String feature) { + public static @NotNull String generateContainerName() { String container = "ghcr.io/open-feature/flagd-testbed"; - if (!Strings.isBlank(feature)) { - container += "-" + feature; - } container += ":v" + version; return container; } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java index e0edef240..0c0b32420 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java @@ -6,6 +6,7 @@ import dev.openfeature.contrib.providers.flagd.Config; import org.apache.logging.log4j.core.config.Order; +import org.junit.jupiter.api.parallel.Isolated; import org.junit.platform.suite.api.BeforeSuite; import org.junit.platform.suite.api.ConfigurationParameter; import org.junit.platform.suite.api.ExcludeTags; @@ -30,6 +31,7 @@ @IncludeTags("in-process") @ExcludeTags({"unixsocket", "targetURI"}) @Testcontainers +@Isolated public class RunInProcessTest { @BeforeSuite diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java index 4ecab84e5..37507020f 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/State.java @@ -6,6 +6,7 @@ import dev.openfeature.contrib.providers.flagd.e2e.steps.FlagSteps; import dev.openfeature.contrib.providers.flagd.e2e.steps.ProviderType; import dev.openfeature.sdk.Client; +import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.FlagEvaluationDetails; import dev.openfeature.sdk.MutableContext; import java.util.LinkedList; @@ -15,6 +16,7 @@ public class State { public ProviderType providerType; public Client client; + public FeatureProvider provider; public List events = new LinkedList<>(); public Optional lastEvent; public FlagSteps.Flag flag; diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/AbstractSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/AbstractSteps.java index 133c1fb49..4a74400c4 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/AbstractSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/AbstractSteps.java @@ -2,10 +2,10 @@ import dev.openfeature.contrib.providers.flagd.e2e.State; -abstract class AbstractSteps { - State state; +public abstract class AbstractSteps { + protected State state; - public AbstractSteps(State state) { + protected AbstractSteps(State state) { this.state = state; } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EnvironmentVariableUtils.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EnvironmentVariableUtils.java index b3ef4346e..f7427cfa2 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EnvironmentVariableUtils.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EnvironmentVariableUtils.java @@ -14,7 +14,7 @@ * This class modifies the internals of the environment variables map with reflection. Warning: If * your {@link SecurityManager} does not allow modifications, it fails. */ -class EnvironmentVariableUtils { +public class EnvironmentVariableUtils { private EnvironmentVariableUtils() { // private constructor to prevent instantiation of utility class diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java index 6dbd0c9ca..de2abcb79 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/EventSteps.java @@ -25,7 +25,7 @@ public EventSteps(State state) { @Given("a {} event handler") public void a_stale_event_handler(String eventType) { state.client.on(mapEventType(eventType), eventDetails -> { - log.info("event tracked for {} ", eventType); + log.info("event tracked for {} at {} ms ", eventType, System.currentTimeMillis() % 100_000); state.events.add(new Event(eventType, eventDetails)); }); } @@ -47,24 +47,24 @@ public void a_stale_event_handler(String eventType) { @When("a {} event was fired") public void eventWasFired(String eventType) throws InterruptedException { - eventHandlerShouldBeExecutedWithin(eventType, 10000); - // we might be too fast in the execution - Thread.sleep(500); + eventHandlerShouldBeExecutedWithin(eventType, 8000); } @Then("the {} event handler should have been executed") public void eventHandlerShouldBeExecuted(String eventType) { - eventHandlerShouldBeExecutedWithin(eventType, 30000); + eventHandlerShouldBeExecutedWithin(eventType, 10000); } @Then("the {} event handler should have been executed within {int}ms") public void eventHandlerShouldBeExecutedWithin(String eventType, int ms) { log.info("waiting for eventtype: {}", eventType); - await().atMost(ms, MILLISECONDS) + await().alias("waiting for eventtype " + eventType) + .atMost(ms, MILLISECONDS) + .pollInterval(10, MILLISECONDS) .until(() -> state.events.stream().anyMatch(event -> event.type.equals(eventType))); state.lastEvent = state.events.stream() .filter(event -> event.type.equals(eventType)) .findFirst(); - state.events.removeIf(event -> event.type.equals(eventType)); + state.events.clear(); } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/FlagSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/FlagSteps.java index aab14a857..cd0d65afa 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/FlagSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/FlagSteps.java @@ -1,8 +1,6 @@ package dev.openfeature.contrib.providers.flagd.e2e.steps; -import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; import dev.openfeature.contrib.providers.flagd.e2e.State; import dev.openfeature.sdk.FlagEvaluationDetails; @@ -70,15 +68,9 @@ public void the_variant_should_be(String variant) { } @Then("the flag should be part of the event payload") - @Then("the flag was modified") public void the_flag_was_modified() { - await().atMost(5000, MILLISECONDS).until(() -> state.events.stream() - .anyMatch(event -> event.type.equals("change") - && event.details.getFlagsChanged().contains(state.flag.name))); - state.lastEvent = state.events.stream() - .filter(event -> event.type.equals("change") - && event.details.getFlagsChanged().contains(state.flag.name)) - .findFirst(); + Event event = state.lastEvent.orElseThrow(AssertionError::new); + assertThat(event.details.getFlagsChanged()).contains(state.flag.name); } public class Flag { diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java index cf0e5ed0c..0f312ff9d 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.providers.flagd.e2e.steps; +import static io.restassured.RestAssured.when; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import dev.openfeature.contrib.providers.flagd.Config; @@ -8,9 +10,6 @@ import dev.openfeature.contrib.providers.flagd.e2e.State; import dev.openfeature.sdk.FeatureProvider; import dev.openfeature.sdk.OpenFeatureAPI; -import eu.rekawek.toxiproxy.Proxy; -import eu.rekawek.toxiproxy.ToxiproxyClient; -import eu.rekawek.toxiproxy.model.ToxicDirection; import io.cucumber.java.After; import io.cucumber.java.AfterAll; import io.cucumber.java.Before; @@ -22,17 +21,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; -import java.util.Timer; -import java.util.TimerTask; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.parallel.Isolated; import org.testcontainers.containers.BindMode; -import org.testcontainers.containers.Network; -import org.testcontainers.containers.ToxiproxyContainer; import org.testcontainers.shaded.org.apache.commons.io.FileUtils; @Isolated() @@ -40,11 +33,7 @@ public class ProviderSteps extends AbstractSteps { public static final int UNAVAILABLE_PORT = 9999; - static Map containers = new HashMap<>(); - public static Network network = Network.newNetwork(); - public static ToxiproxyContainer toxiproxy = - new ToxiproxyContainer("ghcr.io/shopify/toxiproxy:2.5.0").withNetwork(network); - public static ToxiproxyClient toxiproxyClient; + static FlagdContainer container; static Path sharedTempDir; @@ -52,96 +41,42 @@ public ProviderSteps(State state) { super(state); } - static String generateProxyName(Config.Resolver resolver, ProviderType providerType) { - return providerType + "-" + resolver; - } - @BeforeAll public static void beforeAll() throws IOException { - toxiproxy.start(); - toxiproxyClient = new ToxiproxyClient(toxiproxy.getHost(), toxiproxy.getControlPort()); - toxiproxyClient.createProxy( - generateProxyName(Config.Resolver.RPC, ProviderType.DEFAULT), "0.0.0.0:8666", "default:8013"); - - toxiproxyClient.createProxy( - generateProxyName(Config.Resolver.IN_PROCESS, ProviderType.DEFAULT), "0.0.0.0:8667", "default:8015"); - toxiproxyClient.createProxy( - generateProxyName(Config.Resolver.RPC, ProviderType.SSL), "0.0.0.0:8668", "ssl:8013"); - toxiproxyClient.createProxy( - generateProxyName(Config.Resolver.IN_PROCESS, ProviderType.SSL), "0.0.0.0:8669", "ssl:8015"); - - containers.put( - ProviderType.DEFAULT, new FlagdContainer().withNetwork(network).withNetworkAliases("default")); - containers.put( - ProviderType.SSL, new FlagdContainer("ssl").withNetwork(network).withNetworkAliases("ssl")); + State.resolverType = Config.Resolver.RPC; sharedTempDir = Files.createDirectories( Paths.get("tmp/" + RandomStringUtils.randomAlphanumeric(8).toLowerCase() + "/")); - containers.put( - ProviderType.SOCKET, - new FlagdContainer("socket") - .withFileSystemBind(sharedTempDir.toAbsolutePath().toString(), "/tmp", BindMode.READ_WRITE)); + container = new FlagdContainer() + .withFileSystemBind(sharedTempDir.toAbsolutePath().toString(), "/tmp", BindMode.READ_WRITE); } @AfterAll public static void afterAll() throws IOException { - - containers.forEach((name, container) -> container.stop()); + container.stop(); FileUtils.deleteDirectory(sharedTempDir.toFile()); - toxiproxyClient.reset(); - toxiproxy.stop(); } @Before public void before() throws IOException { - - toxiproxyClient.getProxies().forEach(proxy -> { - try { - proxy.toxics().getAll().forEach(toxic -> { - try { - toxic.remove(); - } catch (IOException e) { - log.debug("Failed to remove timout", e); - } - }); - } catch (IOException e) { - log.debug("Failed to remove timout", e); - } - }); - - containers.values().stream() - .filter(containers -> !containers.isRunning()) - .forEach(FlagdContainer::start); + if (!container.isRunning()) { + container.start(); + } } @After - public void tearDown() { - OpenFeatureAPI.getInstance().shutdown(); - } - - public int getPort(Config.Resolver resolver, ProviderType providerType) { - switch (resolver) { - case RPC: - switch (providerType) { - case DEFAULT: - return toxiproxy.getMappedPort(8666); - case SSL: - return toxiproxy.getMappedPort(8668); - } - case IN_PROCESS: - switch (providerType) { - case DEFAULT: - return toxiproxy.getMappedPort(8667); - case SSL: - return toxiproxy.getMappedPort(8669); - } - default: - throw new IllegalArgumentException("Unsupported resolver: " + resolver); + public void tearDown() throws InterruptedException { + if (state.client != null) { + when().post("http://" + container.getLaunchpadUrl() + "/stop") + .then() + .statusCode(200); } + OpenFeatureAPI.getInstance().shutdown(); } @Given("a {} flagd provider") - public void setupProvider(String providerType) throws IOException { - state.builder.deadline(500).keepAlive(0).retryGracePeriod(3); + public void setupProvider(String providerType) throws IOException, InterruptedException { + String flagdConfig = "default"; + state.builder.deadline(1000).keepAlive(0).retryGracePeriod(2); boolean wait = true; switch (providerType) { case "unavailable": @@ -163,11 +98,13 @@ public void setupProvider(String providerType) throws IOException { String absolutePath = file.getAbsolutePath(); this.state.providerType = ProviderType.SSL; state.builder - .port(getPort(State.resolverType, state.providerType)) + .port(container.getPort(State.resolverType)) .tls(true) .certPath(absolutePath); + flagdConfig = "ssl"; break; case "offline": + State.resolverType = Config.Resolver.IN_PROCESS; File flags = new File("test-harness/flags"); ObjectMapper objectMapper = new ObjectMapper(); Object merged = new Object(); @@ -185,48 +122,47 @@ public void setupProvider(String providerType) throws IOException { default: this.state.providerType = ProviderType.DEFAULT; - state.builder.port(getPort(State.resolverType, state.providerType)); + state.builder.port(container.getPort(State.resolverType)); break; } + when().post("http://" + container.getLaunchpadUrl() + "/start?config={config}", flagdConfig) + .then() + .statusCode(200); + + // giving flagd a little time to start + Thread.sleep(100); FeatureProvider provider = new FlagdProvider(state.builder.resolverType(State.resolverType).build()); OpenFeatureAPI api = OpenFeatureAPI.getInstance(); + String providerName = providerType + Math.random(); if (wait) { - api.setProviderAndWait(providerType, provider); + api.setProviderAndWait(providerName, provider); } else { - api.setProvider(providerType, provider); + api.setProvider(providerName, provider); } - this.state.client = api.getClient(providerType); + log.info("provider name: {}", providerName); + this.state.client = api.getClient(providerName); } - @When("the connection is lost for {int}s") - public void the_connection_is_lost_for(int seconds) throws InterruptedException, IOException { - log.info("Timeout and wait for {} seconds", seconds); - String randomizer = RandomStringUtils.randomAlphanumeric(5); - String timeoutUpName = "restart-up-" + randomizer; - String timeoutDownName = "restart-down-" + randomizer; - Proxy proxy = toxiproxyClient.getProxy(generateProxyName(State.resolverType, state.providerType)); - proxy.toxics().timeout(timeoutDownName, ToxicDirection.DOWNSTREAM, seconds); - proxy.toxics().timeout(timeoutUpName, ToxicDirection.UPSTREAM, seconds); - - TimerTask task = new TimerTask() { - public void run() { - try { - proxy.toxics().get(timeoutUpName).remove(); - proxy.toxics().get(timeoutDownName).remove(); - } catch (IOException e) { - log.debug("Failed to remove timeout", e); - } - } - }; - Timer restartTimer = new Timer("Timer" + randomizer); + @When("the connection is lost") + public void the_connection_is_lost() throws InterruptedException { + when().post("http://" + container.getLaunchpadUrl() + "/stop").then().statusCode(200); + } - restartTimer.schedule(task, seconds * 1000L); + @When("the connection is lost for {int}s") + public void the_connection_is_lost_for(int seconds) throws InterruptedException { + when().post("http://" + container.getLaunchpadUrl() + "/restart?seconds={seconds}", seconds) + .then() + .statusCode(200); } - static FlagdContainer getContainer(ProviderType providerType) { - log.info("getting container for {}", providerType); - return containers.getOrDefault(providerType, containers.get(ProviderType.DEFAULT)); + @When("the flag was modified") + public void the_flag_was_modded() throws InterruptedException { + + when().post("http://" + container.getLaunchpadUrl() + "/change").then().statusCode(200); + + // we might be too fast in the execution + Thread.sleep(100); } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ConfigSteps.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/ConfigSteps.java similarity index 93% rename from providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ConfigSteps.java rename to providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/ConfigSteps.java index 8e8ee44d6..2824dc12b 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ConfigSteps.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/config/ConfigSteps.java @@ -1,9 +1,12 @@ -package dev.openfeature.contrib.providers.flagd.e2e.steps; +package dev.openfeature.contrib.providers.flagd.e2e.steps.config; import static org.assertj.core.api.Assertions.assertThat; import dev.openfeature.contrib.providers.flagd.Config; import dev.openfeature.contrib.providers.flagd.e2e.State; +import dev.openfeature.contrib.providers.flagd.e2e.steps.AbstractSteps; +import dev.openfeature.contrib.providers.flagd.e2e.steps.EnvironmentVariableUtils; +import dev.openfeature.contrib.providers.flagd.e2e.steps.Utils; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; diff --git a/providers/flagd/src/test/resources/simplelogger.properties b/providers/flagd/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..d2ca1bbdc --- /dev/null +++ b/providers/flagd/src/test/resources/simplelogger.properties @@ -0,0 +1,4 @@ +org.org.slf4j.simpleLogger.defaultLogLevel=debug +org.slf4j.simpleLogger.showDateTime= + +io.grpc.level=trace diff --git a/providers/flagd/test-harness b/providers/flagd/test-harness index fc7867922..1252f5145 160000 --- a/providers/flagd/test-harness +++ b/providers/flagd/test-harness @@ -1 +1 @@ -Subproject commit fc786792273b7984911dc3bcb7b47489f261ba57 +Subproject commit 1252f5145324cfd700c4e5dc35130551904a8f05