Skip to content

Commit e89fab2

Browse files
test: improve gherkin test suite only relying on the newest version, with data loading (#1578)
* test: improve gherkin test suite only relying on the newest version, with data loading Signed-off-by: Simon Schrottner <[email protected]> * Update src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Simon Schrottner <[email protected]> * Update src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Simon Schrottner <[email protected]> * test: improve gherkin test suite only relying on the newest version, with data loading Signed-off-by: Simon Schrottner <[email protected]> diff --git c/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java i/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java index e4cd603..121b567 100644 --- c/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java +++ i/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java @@ -114,9 +114,9 @@ public class ContextSteps { public void a_context_containing_a_key_with_type_and_with_value(String key, String type, String value) throws ClassNotFoundException, InstantiationException { Map<String, Value> map = state.context.asMap(); - Map<String, Value> map = state.context.asMap(); map.put(key, new Value(Utils.convert(value, type))); state.context = new MutableContext(state.context.getTargetingKey(), map); + } @given("a context containing a targeting key with value {string}") public void a_context_containing_a_targeting_key_with_value(String string) { diff --git c/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java i/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java index 20f3f5f..138c23f 100644 --- c/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java +++ i/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java @@ -46,7 +46,6 @@ public class CelContextEvaluator<T> implements ContextEvaluator<T> { // Evaluate with context, overriding defaults. objectMap.putAll(evaluationContext.asObjectMap()); } - } Object result = program.eval(objectMap); diff --git c/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java i/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java index 339d8c8..9a63f58 100644 --- c/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java +++ i/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java @@ -50,6 +50,7 @@ public class VariantsMapDeserializer extends JsonDeserializer<Map<String, Object } private Object convertJsonNodeToValue(JsonNode node) throws InstantiationException { + if (node.isNull()) { return null; } else if (node.isBoolean()) { @@ -60,25 +61,8 @@ public class VariantsMapDeserializer extends JsonDeserializer<Map<String, Object return node.asDouble(); } else if (node.isTextual()) { return node.asText(); - } else if (node.isArray()) { - List<Object> list = new ArrayList<>(); - for (JsonNode item : node) { - list.add(convertJsonNodeToValue(item)); - } - return list; } else if (node.isObject()) { - Map<String, Value> map = new HashMap<>(); - Iterator<Map.Entry<String, JsonNode>> fields = node.fields(); - while (fields.hasNext()) { - Map.Entry<String, JsonNode> field = fields.next(); - Object o = convertJsonNodeToValue(field.getValue()); - if (o instanceof Value) { - map.put(field.getKey(), (Value) o); - } else { - map.put(field.getKey(), new Value(o)); - } - } - return new Value(new MutableStructure(map)); + return Value.objectToValue(node); } throw new IllegalArgumentException("Unsupported JSON node type: " + node.getNodeType()); Signed-off-by: Simon Schrottner <[email protected]> * Update src/test/java/dev/openfeature/sdk/e2e/steps/FlagStepDefinitions.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Simon Schrottner <[email protected]> * Update src/test/java/dev/openfeature/sdk/testutils/TestFlagsUtils.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Simon Schrottner <[email protected]> * test: improve gherkin test suite only relying on the newest version, with data loading Signed-off-by: Simon Schrottner <[email protected]> diff --git c/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java i/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java index e4cd603..121b567 100644 --- c/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java +++ i/src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java @@ -114,9 +114,9 @@ public class ContextSteps { public void a_context_containing_a_key_with_type_and_with_value(String key, String type, String value) throws ClassNotFoundException, InstantiationException { Map<String, Value> map = state.context.asMap(); - Map<String, Value> map = state.context.asMap(); map.put(key, new Value(Utils.convert(value, type))); state.context = new MutableContext(state.context.getTargetingKey(), map); + } @given("a context containing a targeting key with value {string}") public void a_context_containing_a_targeting_key_with_value(String string) { diff --git c/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java i/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java index 20f3f5f..138c23f 100644 --- c/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java +++ i/src/test/java/dev/openfeature/sdk/testutils/jackson/CelContextEvaluator.java @@ -46,7 +46,6 @@ public class CelContextEvaluator<T> implements ContextEvaluator<T> { // Evaluate with context, overriding defaults. objectMap.putAll(evaluationContext.asObjectMap()); } - } Object result = program.eval(objectMap); diff --git c/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java i/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java index 339d8c8..9a63f58 100644 --- c/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java +++ i/src/test/java/dev/openfeature/sdk/testutils/jackson/VariantsMapDeserializer.java @@ -50,6 +50,7 @@ public class VariantsMapDeserializer extends JsonDeserializer<Map<String, Object } private Object convertJsonNodeToValue(JsonNode node) throws InstantiationException { + if (node.isNull()) { return null; } else if (node.isBoolean()) { @@ -60,25 +61,8 @@ public class VariantsMapDeserializer extends JsonDeserializer<Map<String, Object return node.asDouble(); } else if (node.isTextual()) { return node.asText(); - } else if (node.isArray()) { - List<Object> list = new ArrayList<>(); - for (JsonNode item : node) { - list.add(convertJsonNodeToValue(item)); - } - return list; } else if (node.isObject()) { - Map<String, Value> map = new HashMap<>(); - Iterator<Map.Entry<String, JsonNode>> fields = node.fields(); - while (fields.hasNext()) { - Map.Entry<String, JsonNode> field = fields.next(); - Object o = convertJsonNodeToValue(field.getValue()); - if (o instanceof Value) { - map.put(field.getKey(), (Value) o); - } else { - map.put(field.getKey(), new Value(o)); - } - } - return new Value(new MutableStructure(map)); + return Value.objectToValue(node); } throw new IllegalArgumentException("Unsupported JSON node type: " + node.getNodeType()); Signed-off-by: Simon Schrottner <[email protected]> diff --git c/src/test/java/dev/openfeature/sdk/e2e/Utils.java i/src/test/java/dev/openfeature/sdk/e2e/Utils.java index 1500d99..565968c 100644 --- c/src/test/java/dev/openfeature/sdk/e2e/Utils.java +++ i/src/test/java/dev/openfeature/sdk/e2e/Utils.java @@ -7,6 +7,8 @@ import java.util.Objects; public final class Utils { + public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private Utils() {} public static Object convert(String value, String type) { @@ -27,7 +29,7 @@ public final class Utils { return Long.parseLong(value); case "object": try { - return Value.objectToValue(new ObjectMapper().readValue(value, Object.class)); + return Value.objectToValue(OBJECT_MAPPER.readValue(value, Object.class)); } catch (JsonProcessingException e) { throw new RuntimeException(e); } diff --git c/src/test/java/dev/openfeature/sdk/testutils/TestFlagsUtils.java i/src/test/java/dev/openfeature/sdk/testutils/TestFlagsUtils.java index 78a2fc5..13fe32f 100644 --- c/src/test/java/dev/openfeature/sdk/testutils/TestFlagsUtils.java +++ i/src/test/java/dev/openfeature/sdk/testutils/TestFlagsUtils.java @@ -17,6 +17,8 @@ import java.util.Map; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; +import static dev.openfeature.sdk.e2e.Utils.OBJECT_MAPPER; + /** * Test flags utils. */ @@ -41,7 +43,8 @@ public class TestFlagsUtils { */ public static synchronized Map<String, Flag<?>> buildFlags() { if (flags == null) { - ObjectMapper objectMapper = new ObjectMapper(); + if (flags == null) { + ObjectMapper objectMapper = OBJECT_MAPPER; objectMapper.configure(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature(), true); objectMapper.addMixIn(Flag.class, InMemoryFlagMixin.class); objectMapper.addMixIn(Flag.FlagBuilder.class, InMemoryFlagMixin.FlagBuilderMixin.class); Signed-off-by: Simon Schrottner <[email protected]> * fixup: add provider-status tests Signed-off-by: Simon Schrottner <[email protected]> * fixup: update spec Signed-off-by: Simon Schrottner <[email protected]> * fixup: add missing steps and adapt to new spec steps Signed-off-by: Simon Schrottner <[email protected]> * fix: race condition? Signed-off-by: Simon Schrottner <[email protected]> --------- Signed-off-by: Simon Schrottner <[email protected]> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 6fb139f commit e89fab2

16 files changed

+525
-95
lines changed

pom.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,31 @@
168168
<scope>test</scope>
169169
</dependency>
170170

171+
<dependency>
172+
<groupId>com.fasterxml.jackson.core</groupId>
173+
<artifactId>jackson-core</artifactId>
174+
<scope>test</scope>
175+
</dependency>
176+
177+
<dependency>
178+
<groupId>com.fasterxml.jackson.core</groupId>
179+
<artifactId>jackson-annotations</artifactId>
180+
<scope>test</scope>
181+
</dependency>
182+
183+
<dependency>
184+
<groupId>com.fasterxml.jackson.core</groupId>
185+
<artifactId>jackson-databind</artifactId>
186+
<scope>test</scope>
187+
</dependency>
188+
189+
<dependency>
190+
<groupId>dev.cel</groupId>
191+
<artifactId>cel</artifactId>
192+
<version>0.10.1</version>
193+
<scope>test</scope>
194+
</dependency>
195+
171196
</dependencies>
172197

173198
<dependencyManagement>
@@ -191,6 +216,14 @@
191216
</dependency>
192217
<!-- End mockito workaround-->
193218

219+
<dependency>
220+
<groupId>com.fasterxml.jackson</groupId>
221+
<artifactId>jackson-bom</artifactId>
222+
<version>2.16.1</version> <!-- Use the desired version -->
223+
<type>pom</type>
224+
<scope>import</scope>
225+
</dependency>
226+
194227
<dependency>
195228
<groupId>io.cucumber</groupId>
196229
<artifactId>cucumber-bom</artifactId>

src/main/java/dev/openfeature/sdk/providers/memory/Flag.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ public class Flag<T> {
2020
private String defaultVariant;
2121
private ContextEvaluator<T> contextEvaluator;
2222
private ImmutableMetadata flagMetadata;
23+
private boolean disabled;
2324
}

src/main/java/dev/openfeature/sdk/providers/memory/InMemoryProvider.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,36 +97,37 @@ public void updateFlag(String flagKey, Flag<?> newFlag) {
9797
@Override
9898
public ProviderEvaluation<Boolean> getBooleanEvaluation(
9999
String key, Boolean defaultValue, EvaluationContext evaluationContext) {
100-
return getEvaluation(key, evaluationContext, Boolean.class);
100+
return getEvaluation(key, defaultValue, evaluationContext, Boolean.class);
101101
}
102102

103103
@Override
104104
public ProviderEvaluation<String> getStringEvaluation(
105105
String key, String defaultValue, EvaluationContext evaluationContext) {
106-
return getEvaluation(key, evaluationContext, String.class);
106+
return getEvaluation(key, defaultValue, evaluationContext, String.class);
107107
}
108108

109109
@Override
110110
public ProviderEvaluation<Integer> getIntegerEvaluation(
111111
String key, Integer defaultValue, EvaluationContext evaluationContext) {
112-
return getEvaluation(key, evaluationContext, Integer.class);
112+
return getEvaluation(key, defaultValue, evaluationContext, Integer.class);
113113
}
114114

115115
@Override
116116
public ProviderEvaluation<Double> getDoubleEvaluation(
117117
String key, Double defaultValue, EvaluationContext evaluationContext) {
118-
return getEvaluation(key, evaluationContext, Double.class);
118+
return getEvaluation(key, defaultValue, evaluationContext, Double.class);
119119
}
120120

121121
@SneakyThrows
122122
@Override
123123
public ProviderEvaluation<Value> getObjectEvaluation(
124124
String key, Value defaultValue, EvaluationContext evaluationContext) {
125-
return getEvaluation(key, evaluationContext, Value.class);
125+
return getEvaluation(key, defaultValue, evaluationContext, Value.class);
126126
}
127127

128128
private <T> ProviderEvaluation<T> getEvaluation(
129-
String key, EvaluationContext evaluationContext, Class<?> expectedType) throws OpenFeatureError {
129+
String key, T defaultValue, EvaluationContext evaluationContext, Class<?> expectedType)
130+
throws OpenFeatureError {
130131
if (!ProviderState.READY.equals(state)) {
131132
if (ProviderState.NOT_READY.equals(state)) {
132133
throw new ProviderNotReadyError("provider not yet initialized");
@@ -138,11 +139,28 @@ private <T> ProviderEvaluation<T> getEvaluation(
138139
}
139140
Flag<?> flag = flags.get(key);
140141
if (flag == null) {
141-
throw new FlagNotFoundError("flag " + key + "not found");
142+
throw new FlagNotFoundError("flag " + key + " not found");
143+
}
144+
if (flag.isDisabled()) {
145+
return ProviderEvaluation.<T>builder()
146+
.reason(Reason.DISABLED.name())
147+
.value(defaultValue)
148+
.flagMetadata(flag.getFlagMetadata())
149+
.build();
142150
}
143151
T value;
152+
Reason reason = Reason.STATIC;
144153
if (flag.getContextEvaluator() != null) {
145-
value = (T) flag.getContextEvaluator().evaluate(flag, evaluationContext);
154+
try {
155+
value = (T) flag.getContextEvaluator().evaluate(flag, evaluationContext);
156+
reason = Reason.TARGETING_MATCH;
157+
} catch (Exception e) {
158+
value = null;
159+
}
160+
if (value == null) {
161+
value = (T) flag.getVariants().get(flag.getDefaultVariant());
162+
reason = Reason.DEFAULT;
163+
}
146164
} else if (!expectedType.isInstance(flag.getVariants().get(flag.getDefaultVariant()))) {
147165
throw new TypeMismatchError("flag " + key + "is not of expected type");
148166
} else {
@@ -151,7 +169,7 @@ private <T> ProviderEvaluation<T> getEvaluation(
151169
return ProviderEvaluation.<T>builder()
152170
.value(value)
153171
.variant(flag.getDefaultVariant())
154-
.reason(Reason.STATIC.toString())
172+
.reason(reason.toString())
155173
.flagMetadata(flag.getFlagMetadata())
156174
.build();
157175
}

src/test/java/dev/openfeature/sdk/e2e/EvaluationTest.java renamed to src/test/java/dev/openfeature/sdk/e2e/GherkinSpecTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
66

77
import org.junit.platform.suite.api.ConfigurationParameter;
8+
import org.junit.platform.suite.api.ExcludeTags;
89
import org.junit.platform.suite.api.IncludeEngines;
910
import org.junit.platform.suite.api.SelectDirectories;
1011
import org.junit.platform.suite.api.Suite;
@@ -15,4 +16,5 @@
1516
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
1617
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.sdk.e2e.steps")
1718
@ConfigurationParameter(key = OBJECT_FACTORY_PROPERTY_NAME, value = "io.cucumber.picocontainer.PicoFactory")
18-
public class EvaluationTest {}
19+
@ExcludeTags({"deprecated", "reason-codes-cached", "async", "immutability", "evaluation-options"})
20+
public class GherkinSpecTest {}

src/test/java/dev/openfeature/sdk/e2e/Utils.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package dev.openfeature.sdk.e2e;
22

3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import dev.openfeature.sdk.Value;
36
import java.util.Objects;
47

58
public final class Utils {
69

10+
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
11+
712
private Utils() {}
813

914
public static Object convert(String value, String type) {
@@ -22,6 +27,12 @@ public static Object convert(String value, String type) {
2227
return Double.parseDouble(value);
2328
case "long":
2429
return Long.parseLong(value);
30+
case "object":
31+
try {
32+
return Value.objectToValue(OBJECT_MAPPER.readValue(value, Object.class));
33+
} catch (JsonProcessingException e) {
34+
throw new RuntimeException(e);
35+
}
2536
}
2637
throw new RuntimeException("Unknown config type: " + type);
2738
}

src/test/java/dev/openfeature/sdk/e2e/steps/ContextSteps.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
import dev.openfeature.sdk.Hook;
99
import dev.openfeature.sdk.HookContext;
1010
import dev.openfeature.sdk.ImmutableContext;
11+
import dev.openfeature.sdk.ImmutableStructure;
12+
import dev.openfeature.sdk.MutableContext;
1113
import dev.openfeature.sdk.OpenFeatureAPI;
1214
import dev.openfeature.sdk.ThreadLocalTransactionContextPropagator;
1315
import dev.openfeature.sdk.Value;
1416
import dev.openfeature.sdk.e2e.ContextStoringProvider;
1517
import dev.openfeature.sdk.e2e.State;
18+
import dev.openfeature.sdk.e2e.Utils;
1619
import io.cucumber.datatable.DataTable;
1720
import io.cucumber.java.en.And;
1821
import io.cucumber.java.en.Given;
@@ -101,4 +104,29 @@ public void contextEntriesForEachLevelFromAPILevelDownToTheLevelWithKeyAndValue(
101104
}
102105
}
103106
}
107+
108+
@Given("a context containing a key {string} with null value")
109+
public void a_context_containing_a_key_with_null_value(String key) {
110+
a_context_containing_a_key_with_type_and_with_value(key, "String", null);
111+
}
112+
113+
@Given("a context containing a key {string}, with type {string} and with value {string}")
114+
public void a_context_containing_a_key_with_type_and_with_value(String key, String type, String value) {
115+
Map<String, Value> map = state.context.asMap();
116+
map.put(key, Value.objectToValue(Utils.convert(value, type)));
117+
state.context = new MutableContext(state.context.getTargetingKey(), map);
118+
}
119+
120+
@Given("a context containing a targeting key with value {string}")
121+
public void a_context_containing_a_targeting_key_with_value(String string) {
122+
state.context.setTargetingKey(string);
123+
}
124+
125+
@Given("a context containing a nested property with outer key {string} and inner key {string}, with value {string}")
126+
public void a_context_containing_a_nested_property_with_outer_key_and_inner_key_with_value(
127+
String outer, String inner, String value) {
128+
Map<String, Value> innerMap = new HashMap<>();
129+
innerMap.put(inner, new Value(value));
130+
state.context.add(outer, new ImmutableStructure(innerMap));
131+
}
104132
}

src/test/java/dev/openfeature/sdk/e2e/steps/FlagStepDefinitions.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

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

5+
import dev.openfeature.sdk.ErrorCode;
56
import dev.openfeature.sdk.FlagEvaluationDetails;
67
import dev.openfeature.sdk.ImmutableMetadata;
78
import dev.openfeature.sdk.Value;
@@ -23,7 +24,7 @@ public FlagStepDefinitions(State state) {
2324
this.state = state;
2425
}
2526

26-
@Given("a {}-flag with key {string} and a default value {string}")
27+
@Given("a {}-flag with key {string} and a fallback value {string}")
2728
public void givenAFlag(String type, String name, String defaultValue) {
2829
state.flag = new Flag(type, name, Utils.convert(defaultValue, type));
2930
}
@@ -60,7 +61,20 @@ public void the_flag_was_evaluated_with_details() {
6061

6162
@Then("the resolved details value should be {string}")
6263
public void the_resolved_details_value_should_be(String value) {
63-
assertThat(state.evaluation.getValue()).isEqualTo(Utils.convert(value, state.flag.type));
64+
Object evaluationValue = state.evaluation.getValue();
65+
if (state.flag.type.equalsIgnoreCase("object")) {
66+
assertThat(((Value) evaluationValue).asStructure().asObjectMap())
67+
.isEqualTo(((Value) Utils.convert(value, state.flag.type))
68+
.asStructure()
69+
.asObjectMap());
70+
} else {
71+
assertThat(evaluationValue).isEqualTo(Utils.convert(value, state.flag.type));
72+
}
73+
}
74+
75+
@Then("the flag key should be {string}")
76+
public void the_flag_key_should_be(String key) {
77+
assertThat(state.evaluation.getFlagKey()).isEqualTo(key);
6478
}
6579

6680
@Then("the reason should be {string}")
@@ -73,6 +87,20 @@ public void the_variant_should_be(String variant) {
7387
assertThat(state.evaluation.getVariant()).isEqualTo(variant);
7488
}
7589

90+
@Then("the error-code should be {string}")
91+
public void the_error_code_should_be(String errorCode) {
92+
if (errorCode.isEmpty()) {
93+
assertThat(state.evaluation.getErrorCode()).isNull();
94+
} else {
95+
assertThat(state.evaluation.getErrorCode()).isEqualTo(ErrorCode.valueOf(errorCode));
96+
}
97+
}
98+
99+
@Then("the error message should contain {string}")
100+
public void the_error_message_should_contain(String messageSubstring) {
101+
assertThat(state.evaluation.getErrorMessage()).contains(messageSubstring);
102+
}
103+
76104
@Then("the resolved metadata value \"{}\" with type \"{}\" should be \"{}\"")
77105
public void theResolvedMetadataValueShouldBe(String key, String type, String value)
78106
throws NoSuchFieldException, IllegalAccessException {

0 commit comments

Comments
 (0)