java-sdk/src/test/java/dev/openfeature/sdk/EvalContextTest.java

234 lines
10 KiB
Java

package dev.openfeature.sdk;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.cucumber.java.hu.Ha;
import org.junit.jupiter.api.Test;
public class EvalContextTest {
@Specification(number="3.1.1",
text="The `evaluation context` structure **MUST** define an optional `targeting key` field of " +
"type string, identifying the subject of the flag evaluation.")
@Test void requires_targeting_key() {
EvaluationContext ec = new ImmutableContext("targeting-key", new HashMap<>());
assertEquals("targeting-key", ec.getTargetingKey());
}
@Specification(number="3.1.2", text= "The evaluation context MUST support the inclusion of " +
"custom fields, having keys of type `string`, and " +
"values of type `boolean | string | number | datetime | structure`.")
@Test void eval_context() {
Map<String, Value> attributes = new HashMap<>();
Instant dt = Instant.now().truncatedTo(ChronoUnit.MILLIS);
attributes.put("str", new Value("test"));
attributes.put("bool", new Value(true));
attributes.put("int", new Value(4));
attributes.put("dt", new Value(dt));
EvaluationContext ec = new ImmutableContext(attributes);
assertEquals("test", ec.getValue("str").asString());
assertEquals(true, ec.getValue("bool").asBoolean());
assertEquals(4, ec.getValue("int").asInteger());
assertEquals(dt, ec.getValue("dt").asInstant().truncatedTo(ChronoUnit.MILLIS));
}
@Specification(number="3.1.2", text="The evaluation context MUST support the inclusion of " +
"custom fields, having keys of type `string`, and " +
"values of type `boolean | string | number | datetime | structure`.")
@Test void eval_context_structure_array() {
Map<String, Value> attributes = new HashMap<>();
attributes.put("obj", new Value(new MutableStructure().add("val1", 1).add("val2", "2")));
List<Value> values = new ArrayList<Value>(){{
add(new Value("one"));
add(new Value("two"));
}};
attributes.put("arr", new Value(values));
EvaluationContext ec = new ImmutableContext(attributes);
Structure str = ec.getValue("obj").asStructure();
assertEquals(1, str.getValue("val1").asInteger());
assertEquals("2", str.getValue("val2").asString());
List<Value> arr = ec.getValue("arr").asList();
assertEquals("one", arr.get(0).asString());
assertEquals("two", arr.get(1).asString());
}
@Specification(number="3.1.3", text="The evaluation context MUST support fetching the custom fields by key and also fetching all key value pairs.")
@Test void fetch_all() {
Map<String, Value> attributes = new HashMap<>();
Instant dt = Instant.now();
MutableStructure mutableStructure = new MutableStructure().add("val1", 1).add("val2", "2");
attributes.put("str", new Value("test"));
attributes.put("str2", new Value("test2"));
attributes.put("bool", new Value(true));
attributes.put("bool2", new Value(false));
attributes.put("int", new Value(4));
attributes.put("int2", new Value(2));
attributes.put("dt", new Value(dt));
attributes.put("obj", new Value(mutableStructure));
EvaluationContext ec = new ImmutableContext(attributes);
Map<String, Value> foundStr = ec.asMap();
assertEquals(ec.getValue("str").asString(), foundStr.get("str").asString());
assertEquals(ec.getValue("str2").asString(), foundStr.get("str2").asString());
Map<String, Value> foundBool = ec.asMap();
assertEquals(ec.getValue("bool").asBoolean(), foundBool.get("bool").asBoolean());
assertEquals(ec.getValue("bool2").asBoolean(), foundBool.get("bool2").asBoolean());
Map<String, Value> foundInt = ec.asMap();
assertEquals(ec.getValue("int").asInteger(), foundInt.get("int").asInteger());
assertEquals(ec.getValue("int2").asInteger(), foundInt.get("int2").asInteger());
Structure foundObj = ec.getValue("obj").asStructure();
assertEquals(1, foundObj.getValue("val1").asInteger());
assertEquals("2", foundObj.getValue("val2").asString());
}
@Specification(number="3.1.4", text="The evaluation context fields MUST have an unique key.")
@Test void unique_key_across_types() {
MutableContext ec = new MutableContext();
ec.add("key", "val");
ec.add("key", "val2");
assertEquals("val2", ec.getValue("key").asString());
ec.add("key", 3);
assertEquals(null, ec.getValue("key").asString());
assertEquals(3, ec.getValue("key").asInteger());
}
@Test void unique_key_across_types_immutableContext() {
HashMap<String, Value> attributes = new HashMap<>();
attributes.put("key", new Value("val"));
attributes.put("key", new Value("val2"));
attributes.put("key", new Value(3));
EvaluationContext ec = new ImmutableContext(attributes);
assertEquals(null, ec.getValue("key").asString());
assertEquals(3, ec.getValue("key").asInteger());
}
@Test void can_chain_attribute_addition() {
MutableContext ec = new MutableContext();
MutableContext out = ec.add("str", "test")
.add("int", 4)
.add("bool", false)
.add("str", new MutableStructure());
assertEquals(MutableContext.class, out.getClass());
}
@Test void can_add_key_with_null() {
MutableContext ec = new MutableContext()
.add("Boolean", (Boolean)null)
.add("String", (String)null)
.add("Double", (Double)null)
.add("Structure", (MutableStructure)null)
.add("List", (List<Value>)null)
.add("Instant", (Instant)null);
assertEquals(6, ec.asMap().size());
assertEquals(null, ec.getValue("Boolean").asBoolean());
assertEquals(null, ec.getValue("String").asString());
assertEquals(null, ec.getValue("Double").asDouble());
assertEquals(null, ec.getValue("Structure").asStructure());
assertEquals(null, ec.getValue("List").asList());
assertEquals(null, ec.getValue("Instant").asString());
}
@Test void Immutable_context_merge_targeting_key() {
String key1 = "key1";
EvaluationContext ctx1 = new ImmutableContext(key1, new HashMap<>());
EvaluationContext ctx2 = new ImmutableContext(new HashMap<>());
EvaluationContext ctxMerged = ctx1.merge(ctx2);
assertEquals(key1, ctxMerged.getTargetingKey());
String key2 = "key2";
ctx2 = new ImmutableContext(key2, new HashMap<>());
ctxMerged = ctx1.merge(ctx2);
assertEquals(key2, ctxMerged.getTargetingKey());
ctx2 = new ImmutableContext(" ",new HashMap<>());
ctxMerged = ctx1.merge(ctx2);
assertEquals(key1, ctxMerged.getTargetingKey());
}
@Test void merge_targeting_key() {
String key1 = "key1";
MutableContext ctx1 = new MutableContext(key1);
MutableContext ctx2 = new MutableContext();
EvaluationContext ctxMerged = ctx1.merge(ctx2);
assertEquals(key1, ctxMerged.getTargetingKey());
String key2 = "key2";
ctx2.setTargetingKey(key2);
ctxMerged = ctx1.merge(ctx2);
assertEquals(key2, ctxMerged.getTargetingKey());
ctx2.setTargetingKey(" ");
ctxMerged = ctx1.merge(ctx2);
assertEquals(key1, ctxMerged.getTargetingKey());
}
@Test void asObjectMap() {
String key1 = "key1";
MutableContext ctx = new MutableContext(key1);
ctx.add("stringItem", "stringValue");
ctx.add("boolItem", false);
ctx.add("integerItem", 1);
ctx.add("doubleItem", 1.2);
ctx.add("instantItem", Instant.ofEpochSecond(1663331342));
List<Value> listItem = new ArrayList<>();
listItem.add(new Value("item1"));
listItem.add(new Value("item2"));
ctx.add("listItem", listItem);
List<Value> listItem2 = new ArrayList<>();
listItem2.add(new Value(true));
listItem2.add(new Value(false));
ctx.add("listItem2", listItem2);
Map<String, Value> structureValue = new HashMap<>();
structureValue.put("structStringItem", new Value("stringValue"));
structureValue.put("structBoolItem", new Value(false));
structureValue.put("structIntegerItem", new Value(1));
structureValue.put("structDoubleItem", new Value(1.2));
structureValue.put("structInstantItem", new Value(Instant.ofEpochSecond(1663331342)));
Structure structure = new MutableStructure(structureValue);
ctx.add("structureItem", structure);
Map<String, Object> want = new HashMap<>();
want.put("stringItem", "stringValue");
want.put("boolItem", false);
want.put("integerItem", 1);
want.put("doubleItem", 1.2);
want.put("instantItem", Instant.ofEpochSecond(1663331342));
List<String> wantListItem = new ArrayList<>();
wantListItem.add("item1");
wantListItem.add("item2");
want.put("listItem", wantListItem);
List<Boolean> wantListItem2 = new ArrayList<>();
wantListItem2.add(true);
wantListItem2.add(false);
want.put("listItem2", wantListItem2);
Map<String, Object> wantStructureValue = new HashMap<>();
wantStructureValue.put("structStringItem", "stringValue");
wantStructureValue.put("structBoolItem", false);
wantStructureValue.put("structIntegerItem", 1);
wantStructureValue.put("structDoubleItem", 1.2);
wantStructureValue.put("structInstantItem", Instant.ofEpochSecond(1663331342));
want.put("structureItem",wantStructureValue);
assertEquals(want,ctx.asObjectMap());
}
}