diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..9e31981
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @splitio/sdk
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..2eeb3fc
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,37 @@
+name: test
+
+on:
+ pull_request:
+ branches:
+ - '*'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ name: Run tests
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ jdk:
+ - '11'
+ - '17'
+ - '21'
+ env:
+ MAVEN_OPTS: "-Xms2g -Xmx2g -XX:+PrintCommandLineFlags -XX:ThreadStackSize=1024 -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn"
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Setup JDK ${{ matrix.jdk }}
+ uses: actions/setup-java@v5
+ with:
+ distribution: 'adopt'
+ java-version: ${{ matrix.jdk }}
+ cache: 'maven'
+
+ - name: Test
+ run: mvn --batch-mode -T 2C clean package
diff --git a/.gitignore b/.gitignore
index ec376bb..df059e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,20 @@
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+# https://maven.apache.org/wrapper/#usage-without-binary-jar
+.mvn/wrapper/maven-wrapper.jar
+
+# Eclipse m2e generated files
+# Eclipse Core
+.project
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# Idea
.idea
-target
\ No newline at end of file
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..49239fa
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,9 @@
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v6.0.0
+ hooks:
+ - id: check-added-large-files
+ - id: check-json
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
diff --git a/CHANGES.txt b/CHANGES.txt
index 0cd9583..a94da51 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,9 @@
-1.0.0
-- First release. Up to date with java sdk v0.1.0
+1.2.0 (August XX, 2025)
+ - Updated `io.split.client` dependency to 4.16.1
+ - Updated `dev.openfeature` dependency to 1.17.0
+
1.1.0
-- Up tp date with spec v0.5.0 and java sdk v0.3.1
+ - Up tp date with spec v0.5.0 and java sdk v0.3.1
+
+1.0.0
+ - First release. Up to date with java sdk v0.1.0
diff --git a/LICENSE b/LICENSE
index aebce89..051b5fd 100644
--- a/LICENSE
+++ b/LICENSE
@@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
-limitations under the License.
\ No newline at end of file
+limitations under the License.
diff --git a/README.md b/README.md
index 19beb5e..ed1dd96 100644
--- a/README.md
+++ b/README.md
@@ -55,12 +55,12 @@ Client client = api.getClient("CLIENT_NAME");
EvaluationContext context = new MutableContext("TARGETING_KEY");
Boolean boolValue = client.getBooleanValue("boolFlag", false, context);
```
-If the same targeting key is used repeatedly, the evaluation context may be set at the client level
+If the same targeting key is used repeatedly, the evaluation context may be set at the client level
```java
EvaluationContext context = new MutableContext("TARGETING_KEY");
client.setEvaluationContext(context)
```
-or at the OpenFeatureAPI level
+or at the OpenFeatureAPI level
```java
EvaluationContext context = new MutableContext("TARGETING_KEY");
OpenFeatureAPI.getInstance().setEvaluationContext(context)
@@ -68,7 +68,7 @@ OpenFeatureAPI.getInstance().setEvaluationContext(context)
If the context was set at the client or api level, it is not required to provide it during flag evaluation.
## Submitting issues
-
+
The Split team monitors all issues submitted to this [issue tracker](https://github.com/splitio/split-openfeature-provider-java/issues). We encourage you to use this issue tracker to submit any bug reports, feedback, and feature enhancements. We'll do our best to respond in a timely manner.
## Contributing
@@ -78,27 +78,34 @@ Please see [Contributors Guide](CONTRIBUTORS-GUIDE.md) to find all you need to s
Licensed under the Apache License, Version 2.0. See: [Apache License](http://www.apache.org/licenses/).
## About Split
-
+
Split is the leading Feature Delivery Platform for engineering teams that want to confidently deploy features as fast as they can develop them. Split’s fine-grained management, real-time monitoring, and data-driven experimentation ensure that new features will improve the customer experience without breaking or degrading performance. Companies like Twilio, Salesforce, GoDaddy and WePay trust Split to power their feature delivery.
-
+
To learn more about Split, contact hello@split.io, or get started with feature flags for free at https://www.split.io/signup.
-
+
Split has built and maintains SDKs for:
-
-* Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
-* Javascript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
-* Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
+
* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
-* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
-* PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
-* Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
-* GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
* Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK)
+* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities)
+* Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://help.split.io/hc/en-us/articles/26988707417869-Elixir-Thin-Client-SDK)
+* Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/8096158017165-Flutter-plugin)
+* GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK)
-
+* Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
+* JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
+* JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK)
+* Node.js [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
+* PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
+* PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://help.split.io/hc/en-us/articles/18305128673933-PHP-Thin-Client-SDK)
+* Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
+* React [Github](https://github.com/splitio/react-client) [Docs](https://help.split.io/hc/en-us/articles/360038825091-React-SDK)
+* React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK)
+* Redux [Github](https://github.com/splitio/redux-client) [Docs](https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK)
+* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
+
For a comprehensive list of open source projects visit our [Github page](https://github.com/splitio?utf8=%E2%9C%93&query=%20only%3Apublic%20).
-
+
**Learn more about Split:**
-
-Visit [split.io/product](https://www.split.io/product) for an overview of Split, or visit our documentation at [help.split.io](http://help.split.io) for more detailed information.
+Visit [split.io/product](https://www.split.io/product) for an overview of Split, or visit our documentation at [help.split.io](http://help.split.io) for more detailed information.
diff --git a/pom.xml b/pom.xml
index 66c81c7..b766653 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,188 +1,184 @@
-
-
- 4.0.0
-
- io.split.openfeature
- split-openfeature-provider
- 1.1.0
-
- split-openfeature-provider-java
- Split OpenFeature Java Provider
- www.split.io
-
-
- scm:git:git@github.com:splitio/split-openfeature-provider-java.git
- scm:git@github.com:splitio/split-openfeature-provider-java.git
- git@github.com:splitio/split-openfeature-provider-java.git
-
-
-
-
- Robert Grassian
- robert.grassian@split.io
-
-
-
-
-
- Apache 2.0
- http://www.apache.org/licenses/LICENSE-2.0
-
-
-
-
- UTF-8
- 1.7
- 1.7
-
-
-
-
- org.junit.jupiter
- junit-jupiter-engine
- 5.4.0
- test
-
-
- org.junit.vintage
- junit-vintage-engine
- 5.4.0
- test
-
-
- io.split.client
- java-client
- 4.4.3
-
-
- org.apache.httpcomponents
- httpclient
- 4.5.13
-
-
- io.split.integrations.azure
- impressions-listener
- 0.9.1
-
-
- org.mockito
- mockito-core
- 3.4.6
- test
-
-
- dev.openfeature
- sdk
- 0.3.1
-
-
-
-
-
-
-
-
- maven-clean-plugin
- 3.1.0
-
-
-
- maven-resources-plugin
- 3.0.2
-
-
- maven-compiler-plugin
- 3.8.0
-
-
- maven-surefire-plugin
- 2.22.1
-
-
- maven-jar-plugin
- 3.0.2
-
-
- maven-install-plugin
- 2.5.2
-
-
- maven-deploy-plugin
- 2.8.2
-
-
-
- maven-site-plugin
- 3.7.1
-
-
- maven-project-info-reports-plugin
- 3.0.0
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 11
- 11
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
-
-
- attach-javadocs
-
- jar
-
-
- -Xdoclint:none
- 1.8
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.3
- true
-
- true
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.5
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
+ 4.0.0
+ io.split.openfeature
+ split-openfeature-provider
+ 1.2.0
+ split-openfeature-provider-java
+ Split OpenFeature Java Provider
+ www.split.io
+
+ scm:git:git@github.com:splitio/split-openfeature-provider-java.git
+ scm:git@github.com:splitio/split-openfeature-provider-java.git
+ git@github.com:splitio/split-openfeature-provider-java.git
+
+
+
+ Robert Grassian
+ robert.grassian@split.io
+
+
+ Emmanuel Zamora
+ emmanuel.zamora@harness.io
+
+
+
+
+ Apache 2.0
+ http://www.apache.org/licenses/LICENSE-2.0
+
+
+
+ UTF-8
+ 11
+ 5.10.2
+ 3.2.5
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.jupiter.version}
+ test
+
+
+ io.split.client
+ java-client
+ 4.16.1
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.14
+
+
+ io.split.integrations.azure
+ impressions-listener
+ 0.9.1
+
+
+ org.mockito
+ mockito-core
+ 3.4.6
+ test
+
+
+ dev.openfeature
+ sdk
+ 1.17.0
+
+
+
+
+
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-jar-plugin
+ 3.0.2
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+ maven-site-plugin
+ 3.7.1
+
+
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 11
+ 11
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.11.3
+
+
+ attach-javadocs
+
+ jar
+
+
+ -Xdoclint:none
+ 11
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.13
+ true
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.3.1
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
diff --git a/src/main/java/io/split/openfeature/SplitProvider.java b/src/main/java/io/split/openfeature/SplitProvider.java
index c2d1437..06982d3 100644
--- a/src/main/java/io/split/openfeature/SplitProvider.java
+++ b/src/main/java/io/split/openfeature/SplitProvider.java
@@ -3,21 +3,26 @@
import dev.openfeature.sdk.ErrorCode;
import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.FeatureProvider;
+import dev.openfeature.sdk.ImmutableMetadata;
import dev.openfeature.sdk.Metadata;
import dev.openfeature.sdk.MutableStructure;
import dev.openfeature.sdk.ProviderEvaluation;
import dev.openfeature.sdk.Reason;
+import dev.openfeature.sdk.TrackingEventDetails;
import dev.openfeature.sdk.Value;
import dev.openfeature.sdk.exceptions.GeneralError;
import dev.openfeature.sdk.exceptions.OpenFeatureError;
import dev.openfeature.sdk.exceptions.ParseError;
import dev.openfeature.sdk.exceptions.TargetingKeyMissingError;
import io.split.client.SplitClient;
+import io.split.client.api.SplitResult;
import io.split.openfeature.utils.Serialization;
import java.time.Instant;
import java.time.format.DateTimeParseException;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
public class SplitProvider implements FeatureProvider {
@@ -44,126 +49,138 @@ public Metadata getMetadata() {
}
@Override
- public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultTreatment, EvaluationContext evaluationContext) {
- try {
- String evaluated = evaluateTreatment(key, evaluationContext);
- if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
- }
+ public ProviderEvaluation getBooleanEvaluation(
+ String key, Boolean defaultVal, EvaluationContext ctx) {
+ return getEvaluation(key, defaultVal, ctx, s -> {
// if treatment is "on" or "true" we treat that as true
// if it is "off" or "false" we treat it as false
// if it is some other value we throw an error (sdk will catch it and throw default treatment)
- boolean value;
- if (Boolean.parseBoolean(evaluated) || evaluated.equals("on")) {
- value = true;
- } else if (evaluated.equalsIgnoreCase("false") || evaluated.equals("off")) {
- value = false;
+ if (Boolean.parseBoolean(s) || s.equals("on")) {
+ return true;
+ } else if (s.equalsIgnoreCase("false") || s.equals("off")) {
+ return false;
} else {
throw new ParseError();
}
- return constructProviderEvaluation(value, evaluated);
- } catch (OpenFeatureError e) {
- throw e;
- } catch (Exception e) {
- throw new GeneralError("Error getting boolean evaluation", e);
- }
+ }, "Boolean");
+ }
+ @Override
+ public ProviderEvaluation getStringEvaluation(
+ String key, String defaultVal, EvaluationContext ctx) {
+ return getEvaluation(key, defaultVal, ctx, s -> s, "String");
}
@Override
- public ProviderEvaluation getStringEvaluation(String key, String defaultTreatment, EvaluationContext evaluationContext) {
- try {
- String evaluated = evaluateTreatment(key, evaluationContext);
- if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
- }
- return constructProviderEvaluation(evaluated, evaluated);
- } catch (OpenFeatureError e) {
- throw e;
- } catch (Exception e) {
- throw new GeneralError("Error getting String evaluation", e);
- }
+ public ProviderEvaluation getIntegerEvaluation(
+ String key, Integer defaultVal, EvaluationContext ctx) {
+ return getEvaluation(key, defaultVal, ctx, Integer::valueOf, "Integer");
}
@Override
- public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultTreatment, EvaluationContext evaluationContext) {
- try {
- String evaluated = evaluateTreatment(key, evaluationContext);
- if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
- }
- Integer value = Integer.valueOf(evaluated);
- return constructProviderEvaluation(value, evaluated);
- } catch (OpenFeatureError e) {
- throw e;
- } catch (NumberFormatException e) {
- throw new ParseError();
- } catch (Exception e) {
- throw new GeneralError("Error getting Integer evaluation", e);
- }
+ public ProviderEvaluation getDoubleEvaluation(
+ String key, Double defaultTreatment, EvaluationContext ctx) {
+
+ return getEvaluation(key, defaultTreatment, ctx, s -> {
+ if (s == null) throw new NumberFormatException("null");
+ return Double.valueOf(s.trim());
+ }, "Double");
}
@Override
- public ProviderEvaluation getDoubleEvaluation(String key, Double defaultTreatment, EvaluationContext evaluationContext) {
+ public ProviderEvaluation getObjectEvaluation(
+ String key, Value defaultVal, EvaluationContext ctx) {
+ return getEvaluation(key, defaultVal, ctx, s -> {
+ Map rawMap = Serialization.stringToMap(s);
+ return mapToValue(rawMap);
+ }, "Object");
+ }
+
+ @FunctionalInterface
+ interface Mapper {
+ T map(String s) throws Exception;
+ }
+
+ private ProviderEvaluation getEvaluation(
+ String key,
+ T defaultValue,
+ EvaluationContext ctx,
+ Mapper mapper,
+ String typeLabel
+ ) {
try {
- String evaluated = evaluateTreatment(key, evaluationContext);
- if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
+ SplitResult evaluated = evaluateTreatment(key, ctx);
+ String treatment = evaluated.treatment();
+ String config = evaluated.config();
+ ImmutableMetadata metadata = ImmutableMetadata.builder().addString("config", config).build();
+
+ if (noTreatment(treatment)) {
+ return constructProviderEvaluation(
+ defaultValue, treatment, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND, metadata);
}
- Double value = Double.valueOf(evaluated);
- return constructProviderEvaluation(value, evaluated);
+ T mapped = mapper.map(treatment);
+ return constructProviderEvaluation(mapped, treatment, metadata);
+
} catch (OpenFeatureError e) {
throw e;
- } catch (NumberFormatException e) {
- throw new ParseError();
} catch (Exception e) {
- throw new GeneralError("Error getting Double evaluation", e);
+ throw new GeneralError(String.format("Error getting %s evaluation", typeLabel), e);
}
}
@Override
- public ProviderEvaluation getObjectEvaluation(String key, Value defaultTreatment, EvaluationContext evaluationContext) {
- try {
- String evaluated = evaluateTreatment(key, evaluationContext);
- if (noTreatment(evaluated)) {
- return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND);
- }
- Map rawMap = Serialization.stringToMap(evaluated);
- Value value = mapToValue(rawMap);
- return constructProviderEvaluation(value, evaluated);
- } catch (OpenFeatureError e) {
- throw e;
- } catch (Exception e) {
- throw new GeneralError("Error getting Object evaluation", e);
+ public void track(String eventName, EvaluationContext context, TrackingEventDetails details) {
+
+ // targetingKey is always required
+ String key = context.getTargetingKey();
+ if (key == null || key.isEmpty()) throw new TargetingKeyMissingError();
+
+ // eventName is always required
+ if (eventName == null || eventName.isBlank()) throw new GeneralError("Missing eventName, required to track");
+
+ // trafficType is always required
+ Value ttVal = context.getValue("trafficType");
+ String trafficType = (ttVal != null && !ttVal.isNull() && ttVal.isString()) ? ttVal.asString() : null;
+ if (trafficType == null || trafficType.isBlank()) throw new GeneralError("Missing trafficType variable, required to track");
+
+ double value = 0;
+ Map attributes = new HashMap<>();
+ if (details != null) {
+ Optional optionalValue = details.getValue();
+ value = optionalValue.orElse(0).doubleValue();
+ attributes = details.asObjectMap();
}
+
+ client.track(key, trafficType, eventName, value, attributes);
}
public Map transformContext(EvaluationContext context) {
return context.asObjectMap();
}
- private String evaluateTreatment(String key, EvaluationContext evaluationContext) {
+ private SplitResult evaluateTreatment(String key, EvaluationContext evaluationContext) {
String id = evaluationContext.getTargetingKey();
if (id == null || id.isEmpty()) {
// targeting key is always required
throw new TargetingKeyMissingError();
}
Map attributes = transformContext(evaluationContext);
- return client.getTreatment(id, key, attributes);
+ return client.getTreatmentWithConfig(id, key, attributes);
}
private boolean noTreatment(String treatment) {
return treatment == null || treatment.isEmpty() || treatment.equals("control");
}
- private ProviderEvaluation constructProviderEvaluation(T value, String variant) {
- return constructProviderEvaluation(value, variant, Reason.TARGETING_MATCH, null);
+ private ProviderEvaluation constructProviderEvaluation(T value, String variant, ImmutableMetadata metadata) {
+ return constructProviderEvaluation(value, variant, Reason.TARGETING_MATCH, null, metadata);
}
- private ProviderEvaluation constructProviderEvaluation(T value, String variant, Reason reason, ErrorCode errorCode) {
+ private ProviderEvaluation constructProviderEvaluation(T value, String variant, Reason reason, ErrorCode errorCode, ImmutableMetadata metadata) {
ProviderEvaluation.ProviderEvaluationBuilder builder = ProviderEvaluation.builder();
return builder
.value(value)
+ .flagMetadata(metadata)
.reason(reason.name())
.variant(variant)
.errorCode(errorCode)
diff --git a/src/test/java/io/split/openfeature/ClientTest.java b/src/test/java/io/split/openfeature/ClientTest.java
index 88a0f63..a0ec342 100644
--- a/src/test/java/io/split/openfeature/ClientTest.java
+++ b/src/test/java/io/split/openfeature/ClientTest.java
@@ -37,14 +37,13 @@ public void init() {
try {
SplitClientConfig config = SplitClientConfig.builder().splitFile("src/test/resources/split.yaml").build();
SplitClient client = SplitFactoryBuilder.build("localhost", config).client();
- openFeatureAPI.setProvider(new SplitProvider(client));
+ openFeatureAPI.setProviderAndWait(new SplitProvider(client));
} catch (URISyntaxException | IOException e) {
System.out.println("Unexpected Exception occurred initializing Split Provider.");
}
client = openFeatureAPI.getClient("Split Client");
- EvaluationContext evaluationContext = new MutableContext();
String targetingKey = "key";
- evaluationContext.setTargetingKey(targetingKey);
+ EvaluationContext evaluationContext = new MutableContext(targetingKey);
client.setEvaluationContext(evaluationContext);
}
@@ -106,8 +105,7 @@ public void getBooleanSplitWithKeyTest() {
// if we override the evaluation context for this check to use a different key,
// this should take priority, and therefore we should receive a treatment of off
- EvaluationContext evaluationContext = new MutableContext();
- evaluationContext.setTargetingKey("randomKey");
+ EvaluationContext evaluationContext = new MutableContext("randomKey");
result = client.getBooleanValue("my_feature", true, evaluationContext);
assertFalse(result);
}
@@ -150,6 +148,7 @@ public void getBooleanDetailsTest() {
assertFalse(details.getValue());
// the flag has a treatment of "off", this is returned as a value of false but the variant is still "off"
assertEquals("off", details.getVariant());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getErrorCode());
}
@@ -161,17 +160,30 @@ public void getIntegerDetailsTest() {
assertEquals(32, details.getValue());
// the flag has a treatment of "32", this is resolved to an integer but the variant is still "32"
assertEquals("32", details.getVariant());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getErrorCode());
}
@Test
- public void getStringDetailsTest() {
+ public void getStringWithDetailsTest() {
+ FlagEvaluationDetails details = client.getStringDetails("my_feature", "key");
+ assertEquals("my_feature", details.getFlagKey());
+ assertEquals(Reason.TARGETING_MATCH.name(), details.getReason());
+ assertEquals("on", details.getValue());
+ assertEquals("on", details.getVariant());
+ assertEquals("{\"desc\" : \"this applies only to ON treatment\"}", details.getFlagMetadata().getString("config"));
+ assertNull(details.getErrorCode());
+ }
+
+ @Test
+ public void getStringWithoutDetailsTest() {
FlagEvaluationDetails details = client.getStringDetails("some_other_feature", "blah");
assertEquals("some_other_feature", details.getFlagKey());
assertEquals(Reason.TARGETING_MATCH.name(), details.getReason());
assertEquals("off", details.getValue());
// the flag has a treatment of "off", since this is a string the variant is the same as the value
assertEquals("off", details.getVariant());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getErrorCode());
}
@@ -183,6 +195,7 @@ public void getObjectDetailsTest() {
assertEquals(mapToValue(Map.of("key", new Value("value"))), details.getValue());
// the flag's treatment is stored as a string, and the variant is that raw string
assertEquals("{\"key\": \"value\"}", details.getVariant());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getErrorCode());
}
@@ -194,6 +207,7 @@ public void getDoubleDetailsTest() {
assertEquals(32D, details.getValue());
// the flag has a treatment of "32", this is resolved to a double but the variant is still "32"
assertEquals("32", details.getVariant());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getErrorCode());
}
@@ -207,6 +221,7 @@ public void getBooleanFailTest() {
assertFalse(details.getValue());
assertEquals(ErrorCode.PARSE_ERROR, details.getErrorCode());
assertEquals(Reason.ERROR.name(), details.getReason());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getVariant());
}
@@ -218,8 +233,9 @@ public void getIntegerFailTest() {
FlagEvaluationDetails details = client.getIntegerDetails("obj_feature", 10);
assertEquals(10, details.getValue());
- assertEquals(ErrorCode.PARSE_ERROR, details.getErrorCode());
+ assertEquals(ErrorCode.GENERAL, details.getErrorCode());
assertEquals(Reason.ERROR.name(), details.getReason());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getVariant());
}
@@ -231,8 +247,9 @@ public void getDoubleFailTest() {
FlagEvaluationDetails details = client.getDoubleDetails("obj_feature", 10D);
assertEquals(10D, details.getValue());
- assertEquals(ErrorCode.PARSE_ERROR, details.getErrorCode());
+ assertEquals(ErrorCode.GENERAL, details.getErrorCode());
assertEquals(Reason.ERROR.name(), details.getReason());
+ assertNull(details.getFlagMetadata().getString("config"));
assertNull(details.getVariant());
}
diff --git a/src/test/java/io/split/openfeature/SplitProviderTest.java b/src/test/java/io/split/openfeature/SplitProviderTest.java
index 64ff453..d5c0dc8 100644
--- a/src/test/java/io/split/openfeature/SplitProviderTest.java
+++ b/src/test/java/io/split/openfeature/SplitProviderTest.java
@@ -3,12 +3,15 @@
import dev.openfeature.sdk.ErrorCode;
import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.MutableContext;
+import dev.openfeature.sdk.MutableTrackingEventDetails;
import dev.openfeature.sdk.MutableStructure;
import dev.openfeature.sdk.ProviderEvaluation;
+import dev.openfeature.sdk.TrackingEventDetails;
import dev.openfeature.sdk.Value;
import dev.openfeature.sdk.exceptions.GeneralError;
import dev.openfeature.sdk.exceptions.OpenFeatureError;
import io.split.client.SplitClient;
+import io.split.client.api.SplitResult;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
@@ -21,10 +24,16 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
+
import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
public class SplitProviderTest {
@@ -35,12 +44,11 @@ public class SplitProviderTest {
private SplitClient mockSplitClient;
@BeforeEach
- private void init() {
+ public void init() {
MockitoAnnotations.openMocks(this);
key = "key";
- evaluationContext = new MutableContext();
- evaluationContext.setTargetingKey(key);
+ evaluationContext = new MutableContext(key);
}
@Test
@@ -65,7 +73,7 @@ public void evalBooleanNullEmptyTest() {
String flagName = "flagName";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(null);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(null,""));
ProviderEvaluation response = splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
assertFalse(response.getValue());
@@ -73,7 +81,7 @@ public void evalBooleanNullEmptyTest() {
response = splitProvider.getBooleanEvaluation(flagName, true, evaluationContext);
assertTrue(response.getValue());
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("",""));
response = splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
assertFalse(response.getValue());
@@ -89,7 +97,7 @@ public void evalBooleanControlTest() {
String flagName = "flagName";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("control");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("control",""));
ProviderEvaluation response = splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
assertFalse(response.getValue());
@@ -104,7 +112,7 @@ public void evalBooleanTrueTest() {
SplitProvider splitProvider = new SplitProvider(mockSplitClient);
String flagName = "flagName";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("true");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("true",""));
ProviderEvaluation response = splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
assertTrue(response.getValue());
@@ -116,7 +124,7 @@ public void evalBooleanOnTest() {
SplitProvider splitProvider = new SplitProvider(mockSplitClient);
String flagName = "flagName";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("on");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("on",""));
ProviderEvaluation response = splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
assertTrue(response.getValue());
@@ -128,7 +136,7 @@ public void evalBooleanFalseTest() {
SplitProvider splitProvider = new SplitProvider(mockSplitClient);
String flagName = "flagName";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("false");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("false",""));
ProviderEvaluation response = splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
assertFalse(response.getValue());
@@ -140,7 +148,7 @@ public void evalBooleanOffTest() {
SplitProvider splitProvider = new SplitProvider(mockSplitClient);
String flagName = "flagName";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("off");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("off",""));
ProviderEvaluation response = splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
assertFalse(response.getValue());
@@ -152,7 +160,7 @@ public void evalBooleanErrorTest() {
SplitProvider splitProvider = new SplitProvider(mockSplitClient);
String flagName = "flagName";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("a random string");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("a random string",""));
try {
splitProvider.getBooleanEvaluation(flagName, false, evaluationContext);
fail("Should have thrown an exception casting string to boolean");
@@ -173,12 +181,12 @@ public void evalStringNullEmptyTest() {
String flagName = "flagName";
String defaultTreatment = "defaultTreatment";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(null);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(null,""));
ProviderEvaluation response = splitProvider.getStringEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("",""));
response = splitProvider.getStringEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -192,7 +200,7 @@ public void evalStringControlTest() {
String flagName = "flagName";
String defaultTreatment = "defaultTreatment";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("control");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("control",""));
ProviderEvaluation response = splitProvider.getStringEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -206,7 +214,7 @@ public void evalStringRegularTest() {
String flagName = "flagName";
String treatment = "treatment";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(treatment);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(treatment,""));
ProviderEvaluation response = splitProvider.getStringEvaluation(flagName, "defaultTreatment", evaluationContext);
assertEquals(treatment, response.getValue());
@@ -222,12 +230,12 @@ public void evalIntNullEmptyTest() {
String flagName = "flagName";
int defaultTreatment = 10;
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(null);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(null,""));
ProviderEvaluation response = splitProvider.getIntegerEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("",""));
response = splitProvider.getIntegerEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -241,7 +249,7 @@ public void evalIntControlTest() {
String flagName = "flagName";
int defaultTreatment = 10;
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("control");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("control",""));
ProviderEvaluation response = splitProvider.getIntegerEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -256,7 +264,7 @@ public void evalIntRegularTest() {
String numString = "50";
int numInt = 50;
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(numString);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(numString,""));
ProviderEvaluation response = splitProvider.getIntegerEvaluation(flagName, 10, evaluationContext);
assertEquals(numInt, response.getValue());
@@ -276,7 +284,7 @@ public void evalIntErrorTest() {
splitProvider.getIntegerEvaluation(flagName, 10, evaluationContext);
fail("Should have thrown an exception casting string to integer");
} catch (OpenFeatureError e) {
- assertEquals(ErrorCode.PARSE_ERROR, e.getErrorCode());
+ assertEquals(ErrorCode.GENERAL, e.getErrorCode());
} catch (Exception e) {
fail("Unexpected exception occurred", e);
}
@@ -292,12 +300,12 @@ public void evalDoubleNullEmptyTest() {
String flagName = "flagName";
double defaultTreatment = 10;
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(null);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(null,""));
ProviderEvaluation response = splitProvider.getDoubleEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("",""));
response = splitProvider.getDoubleEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -311,7 +319,7 @@ public void evalDoubleControlTest() {
String flagName = "flagName";
double defaultTreatment = 10;
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("control");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("control",""));
ProviderEvaluation response = splitProvider.getDoubleEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -326,7 +334,7 @@ public void evalDoubleRegularTest() {
String numString = "50";
double num = 50;
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(numString);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(numString,""));
ProviderEvaluation response = splitProvider.getDoubleEvaluation(flagName, 10D, evaluationContext);
assertEquals(num, response.getValue());
@@ -340,13 +348,13 @@ public void evalDoubleErrorTest() {
String flagName = "flagName";
String numString = "notAnInt";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(numString);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(numString,""));
try {
splitProvider.getDoubleEvaluation(flagName, 10D, evaluationContext);
fail("Should have thrown an exception casting string to integer");
} catch (OpenFeatureError e) {
- assertEquals(ErrorCode.PARSE_ERROR, e.getErrorCode());
+ assertEquals(ErrorCode.GENERAL, e.getErrorCode());
} catch (Exception e) {
fail("Unexpected exception occurred", e);
}
@@ -362,12 +370,12 @@ public void evalStructureNullEmptyTest() {
String flagName = "flagName";
Value defaultTreatment = mapToValue(Map.of("foo", new Value("bar")));
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(null);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(null,""));
ProviderEvaluation response = splitProvider.getObjectEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("",""));
response = splitProvider.getObjectEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -381,7 +389,7 @@ public void evalStructureControlTest() {
String flagName = "flagName";
Value defaultTreatment = mapToValue(Map.of("foo", new Value("bar")));
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn("control");
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult("control",""));
ProviderEvaluation response = splitProvider.getObjectEvaluation(flagName, defaultTreatment, evaluationContext);
assertEquals(defaultTreatment, response.getValue());
@@ -396,7 +404,7 @@ public void evalStructureRegularTest() {
Value treatment = mapToValue(Map.of("abc", new Value("def")));
String treatmentAsString = "{\"abc\":\"def\"}";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(treatmentAsString);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(treatmentAsString,""));
ProviderEvaluation response =
splitProvider.getObjectEvaluation(flagName, mapToValue(Map.of("foo", new Value("bar"))), evaluationContext);
@@ -434,7 +442,7 @@ public void evalStructureComplexTest() {
));
String treatmentAsString = "{\"string\":\"blah\",\"int\":10,\"double\":100.0,\"bool\":true, \"struct\":{\"foo\":\"bar\",\"baz\":10,\"innerMap\":{\"aa\":\"bb\"}},\"list\":[1,true,{\"cc\":\"dd\"},{\"ee\":1}],\"dateTime\":\"2022-10-13T22:05:54.828Z\"}";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(treatmentAsString);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(treatmentAsString,""));
ProviderEvaluation response =
splitProvider.getObjectEvaluation(flagName, mapToValue(Map.of("foo", new Value("bar"))), evaluationContext);
@@ -449,7 +457,7 @@ public void evalStructureErrorTest() {
String flagName = "flagName";
String treatment = "not an object";
- when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(treatment);
+ when(mockSplitClient.getTreatmentWithConfig(eq(key), eq(flagName), anyMap())).thenReturn(new SplitResult(treatment,""));
try {
splitProvider.getObjectEvaluation(flagName, mapToValue(Map.of("foo", new Value("bar"))), evaluationContext);
@@ -461,6 +469,62 @@ public void evalStructureErrorTest() {
}
}
+ @Test
+ public void trackWithDetailsTest() {
+ SplitProvider provider = new SplitProvider(mockSplitClient);
+
+
+ EvaluationContext ctx = new MutableContext(key).add("trafficType", "user");
+ TrackingEventDetails details = new MutableTrackingEventDetails(42.5)
+ .add("plan", new Value("pro"))
+ .add("beta", new Value(true));
+
+ when(mockSplitClient.track("key", "user", "purchase", 42.5, details.asObjectMap()))
+ .thenReturn(true);
+
+ provider.track("purchase", ctx, details);
+
+ verify(mockSplitClient).track(
+ eq(key), eq("user"), eq("purchase"), eq(42.5),
+ argThat(m -> "pro".equals(m.get("plan")) && Boolean.TRUE.equals(m.get("beta"))));
+ verifyNoMoreInteractions(mockSplitClient);
+ }
+
+ @Test
+ public void trackTargetingKeyErrorTest() {
+ // Tracking without targetingKey should throw error
+ SplitProvider provider = new SplitProvider(mockSplitClient);
+ EvaluationContext ctx = new MutableContext().add("trafficType", "user");
+
+ assertThrows(dev.openfeature.sdk.exceptions.TargetingKeyMissingError.class,
+ () -> provider.track("purchase", ctx, null));
+ verifyNoInteractions(mockSplitClient);
+ }
+
+ @Test
+ public void trackEventNameErrorTest() {
+ // Tracking without eventName should throw error
+ SplitProvider provider = new SplitProvider(mockSplitClient);
+ EvaluationContext ctx = new MutableContext(key).add("trafficType", "user");
+
+ GeneralError ex = assertThrows(GeneralError.class,
+ () -> provider.track(" ", ctx, null)); // blank name
+ assertTrue(ex.getMessage().toLowerCase().contains("eventname"));
+ verifyNoInteractions(mockSplitClient);
+ }
+
+ @Test
+ public void trackTrafficTypeErrorTest() {
+ // Tracking without trafficType should throw error
+ SplitProvider provider = new SplitProvider(mockSplitClient);
+ EvaluationContext ctx = new MutableContext(key);
+
+ GeneralError ex = assertThrows(GeneralError.class,
+ () -> provider.track("purchase", ctx, null));
+ assertTrue(ex.getMessage().toLowerCase().contains("traffictype"));
+ verifyNoInteractions(mockSplitClient);
+ }
+
private Value mapToValue(Map map) {
return new Value(new MutableStructure(map));
}