Skip to content

feat(flagd): Improve e2e coverage #1092

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 6 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
34 changes: 33 additions & 1 deletion providers/flagd/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

<dependencies>
<!-- we inherent dev.openfeature.javasdk and the test dependencies from the parent pom -->

<!-- pin protobuf-java to version used by io.grpc deps; update to 4.0 when io.grpc requires it -->
<dependency>
<groupId>com.google.protobuf</groupId>
Expand Down Expand Up @@ -335,6 +335,38 @@
</arguments>
</configuration>
</execution>
<execution>
<id>copy-gherkin-flagd-rpc-caching.feature</id>
<phase>validate</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<!-- copy the feature spec we want to test into resources so them can be easily loaded -->
<!-- run: cp test-harness/features/flagd.feature src/test/resources/features/ -->
<executable>cp</executable>
<arguments>
<argument>test-harness/gherkin/flagd-rpc-caching.feature</argument>
<argument>src/test/resources/features/</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>copy-gherkin-config.feature</id>
<phase>validate</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<!-- copy the feature spec we want to test into resources so them can be easily loaded -->
<!-- run: cp test-harness/features/flagd.feature src/test/resources/features/ -->
<executable>cp</executable>
<arguments>
<argument>test-harness/gherkin/config.feature</argument>
<argument>src/test/resources/features/</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>copy-gherkin-flagd-reconnect.feature</id>
<phase>validate</phase>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.openfeature.contrib.providers.flagd.e2e;

import org.junit.jupiter.api.Order;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;

import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;

/**
* Class for running the tests associated with "stable" e2e tests (no fake disconnection) for the in-process provider
*/
@Order(value = Integer.MAX_VALUE)
@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features/config.feature")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps.config")
public class RunConfigCucumberTest {


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@SelectClasspathResource("features/evaluation.feature")
@SelectClasspathResource("features/flagd-json-evaluator.feature")
@SelectClasspathResource("features/flagd.feature")
@SelectClasspathResource("features/flagd-rpc-caching.feature")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.rpc,dev.openfeature.contrib.providers.flagd.e2e.steps")
@Testcontainers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig;
import io.cucumber.java.AfterAll;
import io.cucumber.java.Before;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.parallel.Isolated;

Expand All @@ -24,6 +25,10 @@ public class FlagdInProcessSetup {
@BeforeAll()
public static void setup() throws InterruptedException {
flagdContainer.start();
}

@Before()
public static void setupTest() throws InterruptedException {
FlagdInProcessSetup.provider = new FlagdProvider(FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.deadline(1000)
Expand All @@ -37,4 +42,4 @@ public static void setup() throws InterruptedException {
public static void tearDown() {
flagdContainer.stop();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig;
import io.cucumber.java.AfterAll;
import io.cucumber.java.Before;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.parallel.Isolated;

Expand All @@ -21,6 +22,9 @@ public class FlagdInProcessSetup {
@BeforeAll()
public static void setup() throws InterruptedException {
flagdContainer.start();
}
@Before()
public static void setupTest() throws InterruptedException {
FeatureProvider workingProvider = new FlagdProvider(FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.port(flagdContainer.getFirstMappedPort())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
package dev.openfeature.contrib.providers.flagd.e2e.reconnect.rpc;

import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig;
import io.cucumber.java.AfterAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.parallel.Isolated;

import dev.openfeature.contrib.providers.flagd.Config;
import dev.openfeature.contrib.providers.flagd.FlagdOptions;
import dev.openfeature.contrib.providers.flagd.FlagdProvider;
import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig;
import dev.openfeature.contrib.providers.flagd.e2e.reconnect.steps.StepDefinitions;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType;
import dev.openfeature.sdk.FeatureProvider;
import io.cucumber.java.AfterAll;
import io.cucumber.java.Before;
import io.cucumber.java.BeforeAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.parallel.Isolated;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.utility.DockerImageName;

@Isolated()
@Order(value = Integer.MAX_VALUE)
public class FlagdRpcSetup {
private static final GenericContainer flagdContainer = ContainerConfig.flagd(true);

@BeforeAll()
public static void setup() throws InterruptedException {
public static void setups() throws InterruptedException {
flagdContainer.start();
}

@Before()
public static void setupTest() throws InterruptedException {

FeatureProvider workingProvider = new FlagdProvider(FlagdOptions.builder()
.resolverType(Config.Resolver.RPC)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,4 @@ public void an_error_should_be_indicated_within_the_configured_deadline() {
OpenFeatureAPI.getInstance().setProviderAndWait("unavailable", StepDefinitions.unavailableProvider);
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
package dev.openfeature.contrib.providers.flagd.e2e.rpc;

import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType;
import io.cucumber.java.AfterAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.parallel.Isolated;

import dev.openfeature.contrib.providers.flagd.Config;
import dev.openfeature.contrib.providers.flagd.FlagdOptions;
import dev.openfeature.contrib.providers.flagd.FlagdProvider;
import dev.openfeature.contrib.providers.flagd.e2e.ContainerConfig;
import dev.openfeature.contrib.providers.flagd.e2e.steps.StepDefinitions;
import dev.openfeature.sdk.FeatureProvider;
import io.cucumber.java.AfterAll;
import io.cucumber.java.Before;
import io.cucumber.java.BeforeAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.parallel.Isolated;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@Isolated()
@Order(value = Integer.MAX_VALUE)
Expand All @@ -27,11 +23,14 @@ public class FlagdRpcSetup {
@BeforeAll()
public static void setup() {
flagdContainer.start();
}

@Before()
public static void test_setup() {
FlagdRpcSetup.provider = new FlagdProvider(FlagdOptions.builder()
.resolverType(Config.Resolver.RPC)
.port(flagdContainer.getFirstMappedPort())
.deadline(1000)
.cacheType(CacheType.DISABLED.getValue())
.deadline(500)
.build());
StepDefinitions.setProvider(provider);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public class StepDefinitions {
* Injects the client to use for this test.
* Tests run one at a time, but just in case, a lock is used to make sure the
* client is not updated mid-test.
*
*
* @param client client to inject into test.
*/
public static void setProvider(FeatureProvider provider) {
Expand Down Expand Up @@ -208,6 +208,12 @@ public void a_string_flag_with_key_is_evaluated_with_details_and_default_value(S
this.stringFlagDefaultValue = defaultValue;
}

@When("a string flag with key {string} is evaluated with details")
public void a_string_flag_with_key_is_evaluated_with_details(String flagKey) {
this.stringFlagKey = flagKey;
this.stringFlagDefaultValue = "";
}

@Then("the resolved string details value should be {string}, the variant should be {string}, and the reason should be {string}")
public void the_resolved_string_value_should_be_the_variant_should_be_and_the_reason_should_be(String expectedValue,
String expectedVariant, String expectedReason) {
Expand Down Expand Up @@ -347,6 +353,7 @@ public void a_string_flag_with_key_is_evaluated_as_an_integer_with_details_and_a
typeErrorDetails = client.getIntegerDetails(typeErrorFlagKey, typeErrorDefaultValue);
}


@Then("the default integer value should be returned")
public void then_the_default_integer_value_should_be_returned() {
assertEquals(typeErrorDefaultValue, typeErrorDetails.getValue());
Expand Down Expand Up @@ -525,7 +532,7 @@ public void the_resolved_string_zero_value_should_be(String expected) {
String value = client.getStringValue(this.stringFlagKey, this.stringFlagDefaultValue);
assertEquals(expected, value);
}

@When("a context containing a targeting key with value {string}")
public void a_context_containing_a_targeting_key_with_value(String targetingKey) {
this.context = new ImmutableContext(targetingKey);
Expand All @@ -537,4 +544,4 @@ public void the_returned_reason_should_be(String reason) {
this.context);
assertEquals(reason, details.getReason());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package dev.openfeature.contrib.providers.flagd.e2e.steps.config;

import dev.openfeature.contrib.providers.flagd.Config;
import dev.openfeature.contrib.providers.flagd.FlagdOptions;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static org.assertj.core.api.Assertions.assertThat;

public class ConfigSteps {

FlagdOptions.FlagdOptionsBuilder builder = FlagdOptions.builder();
FlagdOptions options;

@When("we initialize a config")
public void we_initialize_a_config() {
options = builder.build();
}

@When("we initialize a config for {string}")
public void we_initialize_a_config_for(String string) {
switch (string.toLowerCase()) {
case "in-process":
options = builder.resolverType(Config.Resolver.IN_PROCESS).build();
break;
case "rpc":
options = builder.resolverType(Config.Resolver.RPC).build();
break;
default:
throw new RuntimeException("Unknown resolver type: " + string);
}
}

@When("we have an option {string} of type {string} with value {string}")
public void we_have_an_option_of_type_with_value(String option, String type, String value) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object converted = convert(value, type);
Method method = Arrays.stream(builder.getClass().getMethods())
.filter(method1 -> method1.getName().equals(option))
.findFirst()
.orElseThrow(RuntimeException::new);
method.invoke(builder, converted);
}


Map<String, String> envVarsSet = new HashMap<>();

@When("we have an environment variable {string} with value {string}")
public void we_have_an_environment_variable_with_value(String varName, String value) throws IllegalAccessException, NoSuchFieldException {
String getenv = System.getenv(varName);
envVarsSet.put(varName, getenv);
EnvironmentVariableUtils.set(varName, value);
}

private Object convert(String value, String type) throws ClassNotFoundException {
if (Objects.equals(value, "null")) return null;
switch (type) {
case "Boolean":
return Boolean.parseBoolean(value);
case "String":
return value;
case "Integer":
return Integer.parseInt(value);
case "Long":
return Long.parseLong(value);
case "ResolverType":
switch (value.toLowerCase()) {
case "in-process":
return Config.Resolver.IN_PROCESS;
case "rpc":
return Config.Resolver.RPC;
default:
throw new RuntimeException("Unknown resolver type: " + value);
}
case "CacheType":
return CacheType.valueOf(value.toUpperCase()).getValue();
}
throw new RuntimeException("Unknown config type: " + type);
}

@Then("the option {string} of type {string} should have the value {string}")
public void the_option_of_type_should_have_the_value(String option, String type, String value) throws Throwable {
Object convert = convert(value, type);

assertThat(options).hasFieldOrPropertyWithValue(option, convert);

// Resetting env vars
for (Map.Entry<String, String> envVar : envVarsSet.entrySet()) {
if (envVar.getValue() == null) {
EnvironmentVariableUtils.clear(envVar.getKey());
} else {
EnvironmentVariableUtils.set(envVar.getKey(), envVar.getValue());
}
}
}
}
Loading
Loading