type, Transformer
.map(entry -> context.transform(entry, type))
.collect(toList());
} else {
- context.reportProblem(format("Invalid property of type %s. Expected JsonObject or JsonArray but got %s",
- type.getSimpleName(), value.getClass().getSimpleName()));
+ var problem = context.problem().unexpectedType().expected(OBJECT).expected(ARRAY);
+ if (value != null) {
+ problem.actual(value.getValueType());
+ }
+ problem.report();
return null;
}
}
/**
- * Transforms a JsonValue to the desired output type. If the value parameter is neither of type
- * JsonObject nor JsonArray, a problem is reported to the context.
+ * Transforms a JsonValue to the desired output type. If the value parameter is neither of type JsonObject nor JsonArray, a problem is reported to the context.
+ *
+ * This method reports errors it encounters.
*
* @param value the value to transform
* @param type the desired result type
@@ -370,15 +427,23 @@ protected T transformObject(JsonValue value, Class type, TransformerConte
.filter(Objects::nonNull)
.findFirst()
.orElseGet(() -> {
- context.reportProblem(format("Invalid property of type %s. Cannot map array values %s",
- type.getSimpleName(),
- value.getClass().getSimpleName()));
+ context.problem().unexpectedType()
+ .type(ARRAY)
+ .expected(type)
+ .report();
return null;
});
} else if (value instanceof JsonObject) {
return context.transform(value, type);
} else {
- context.reportProblem(format("Invalid property of type %s. Expected JsonObject but got %s", type.getSimpleName(), value.getClass().getSimpleName()));
+ var problem = context.problem().unexpectedType()
+ .type(type)
+ .expected(OBJECT)
+ .expected(ARRAY);
+ if (value != null) {
+ problem.actual(value.getValueType());
+ }
+ problem.report();
return null;
}
}
@@ -421,7 +486,11 @@ protected String nodeValue(JsonValue value, TransformerContext context) {
var array = (JsonArray) value;
return nodeValue(array.get(0), context);
} else {
- context.reportProblem("Invalid @value property: " + value);
+ context.problem()
+ .invalidProperty()
+ .property(VALUE)
+ .value(value != null ? value.toString() : null)
+ .report();
return null;
}
}
@@ -432,47 +501,45 @@ protected String nodeValue(JsonValue value, TransformerContext context) {
protected String nodeType(JsonObject object, TransformerContext context) {
var typeNode = object.get(TYPE);
if (typeNode == null) {
- context.reportProblem("Property @type not found on JSON Object");
+ context.problem()
+ .missingProperty()
+ .property(TYPE)
+ .report();
return null;
}
if (typeNode instanceof JsonString) {
return ((JsonString) typeNode).getString();
} else if (typeNode instanceof JsonArray) {
- var array = typeValueArray(typeNode, context);
- if (array == null) {
+ var array = (JsonArray) typeNode;
+ if (array.isEmpty()) {
return null;
}
var typeValue = array.get(0); // a note can have more than one type, take the first
if (!(typeValue instanceof JsonString)) {
- context.reportProblem("Expected @type value to be a string");
+ var problem = context.problem().unexpectedType().property(TYPE).expected(STRING);
+ if (typeValue != null) {
+ problem.actual(typeValue.getValueType());
+ }
+ problem.report();
return null;
}
return ((JsonString) typeValue).getString();
}
- context.reportProblem("Expected @type value to be either string or array");
+ context.problem()
+ .unexpectedType()
+ .property(TYPE)
+ .actual(typeNode.getValueType())
+ .expected(STRING)
+ .expected(ARRAY)
+ .report();
return null;
}
- @Nullable
- protected JsonArray typeValueArray(JsonValue typeNode, TransformerContext context) {
- if (!(typeNode instanceof JsonArray)) {
- context.reportProblem("Invalid @type node: " + typeNode.getValueType());
- return null;
- }
- var array = (JsonArray) typeNode;
- if (array.isEmpty()) {
- context.reportProblem("Expected @type node to be an array with at least one element");
- return null;
- }
- return array;
- }
-
/**
- * Tries to return the instance given by a supplier (a builder's build method). If this fails
- * due to validation errors, e.g. a required property is missing, reports a problem to the
- * context.
+ * Tries to return the instance given by a supplier (a builder's build method). If this fails due to validation errors, e.g. a required property is missing,
+ * reports a problem to the context.
*
* @param builder the supplier
* @param context the context
diff --git a/spi/common/json-ld-spi/src/test/java/org/eclipse/edc/jsonld/spi/transformer/AbstractJsonLdTransformerReturnObjectTest.java b/spi/common/json-ld-spi/src/test/java/org/eclipse/edc/jsonld/spi/transformer/AbstractJsonLdTransformerReturnObjectTest.java
index 7958cae773a..fd0b8feefe9 100644
--- a/spi/common/json-ld-spi/src/test/java/org/eclipse/edc/jsonld/spi/transformer/AbstractJsonLdTransformerReturnObjectTest.java
+++ b/spi/common/json-ld-spi/src/test/java/org/eclipse/edc/jsonld/spi/transformer/AbstractJsonLdTransformerReturnObjectTest.java
@@ -16,6 +16,7 @@
import jakarta.json.Json;
import jakarta.json.JsonBuilderFactory;
+import org.eclipse.edc.transform.spi.ProblemBuilder;
import org.eclipse.edc.transform.spi.TransformerContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,6 +33,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
class AbstractJsonLdTransformerReturnObjectTest {
@@ -53,7 +55,7 @@ public Object transform(@NotNull Object o, @NotNull TransformerContext context)
jsonFactory = Json.createBuilderFactory(Map.of());
context = mock(TransformerContext.class);
-
+ when(context.problem()).thenReturn(new ProblemBuilder(context));
}
@Test
@@ -71,7 +73,7 @@ void verify_returnFromNull() {
var result = transformer.returnMandatoryJsonObject(null, context, TEST_PROPERTY);
assertThat(result).isNull();
- verify(context, times(1)).reportProblem(eq(format("Property '%s' is null", TEST_PROPERTY)));
+ verify(context, times(1)).reportProblem(eq(format("Property '%s' was null", TEST_PROPERTY)));
}
@Test
@@ -89,7 +91,7 @@ void verify_returnFromInvalidType() {
var result = transformer.returnMandatoryJsonObject(value, context, TEST_PROPERTY);
assertThat(result).isNull();
- verify(context, times(1)).reportProblem(eq(format("Property '%s' contains an unexpected type: \"test\"", TEST_PROPERTY)));
+ verify(context, times(1)).reportProblem(eq("Property 'testProperty' must be OBJECT or ARRAY but was: \"test\""));
}
@Test
diff --git a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/AbstractProblemBuilder.java b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/AbstractProblemBuilder.java
new file mode 100644
index 00000000000..41c927f2c6d
--- /dev/null
+++ b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/AbstractProblemBuilder.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import java.util.List;
+
+import static java.lang.String.join;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Base functionality for problem builders.
+ */
+public abstract class AbstractProblemBuilder> {
+ protected static final String UNKNOWN = "unknown";
+
+ protected String type;
+ protected String property;
+
+ @SuppressWarnings("unchecked")
+ public B type(String type) {
+ this.type = type;
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B type(Class> type) {
+ this.type = type != null ? type.getName() : null;
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B type(Enum> type) {
+ this.type = type != null ? type.toString() : null;
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B property(String property) {
+ this.property = property;
+ return (B) this;
+ }
+
+ /**
+ * Concatenates the strings to a comma-separated list with the following form:
+ *
+ * ["one"] : "one"
+ * ["one", "two"] : "one or two"
+ * ["one", "two", "three"] : "one, two, or three"
+ *
+ */
+ protected String concatList(List elements) {
+ requireNonNull(elements);
+ if (elements.size() == 0) {
+ return "";
+ } else if (elements.size() == 1) {
+ return elements.get(0);
+ } else if (elements.size() == 2) {
+ return elements.get(0) + " or " + elements.get(1);
+ }
+ return join(", ", elements.subList(0, elements.size() - 1)) + ", or " + elements.get(elements.size() - 1);
+ }
+
+ public abstract void report();
+}
diff --git a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/InvalidPropertyBuilder.java b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/InvalidPropertyBuilder.java
new file mode 100644
index 00000000000..b71017fd94b
--- /dev/null
+++ b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/InvalidPropertyBuilder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import org.jetbrains.annotations.Nullable;
+
+import static java.lang.String.format;
+
+/**
+ * Reports a property that contains an invalid value.
+ */
+public class InvalidPropertyBuilder extends AbstractProblemBuilder {
+ private final TransformerContext context;
+
+ private String value = UNKNOWN;
+ private String error;
+
+ public InvalidPropertyBuilder(TransformerContext context) {
+ this.context = context;
+ }
+
+ public InvalidPropertyBuilder value(@Nullable String value) {
+ if (value == null) {
+ this.value = "null";
+ return this;
+ }
+ this.value = value;
+ return this;
+ }
+
+ public InvalidPropertyBuilder error(String error) {
+ this.error = error;
+ return this;
+ }
+
+ @Override
+ public void report() {
+ context.reportProblem(format("%s '%s' was invalid%s%s",
+ type == null ? "Property" : type + " property",
+ property,
+ value != null ? ": " + value : "",
+ error != null ? ". Error was: " + error : ""));
+ }
+
+}
diff --git a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/MissingPropertyBuilder.java b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/MissingPropertyBuilder.java
new file mode 100644
index 00000000000..b8ff2e2bbf9
--- /dev/null
+++ b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/MissingPropertyBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import static java.lang.String.format;
+
+/**
+ * Reports a missing mandatory property value.
+ */
+public class MissingPropertyBuilder extends AbstractProblemBuilder {
+ private final TransformerContext context;
+
+ public MissingPropertyBuilder(TransformerContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public void report() {
+ context.reportProblem(format("%s '%s' was missing", type == null ? "Property" : type + " property", property));
+ }
+
+}
diff --git a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/NullPropertyBuilder.java b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/NullPropertyBuilder.java
new file mode 100644
index 00000000000..802f9870c68
--- /dev/null
+++ b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/NullPropertyBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import static java.lang.String.format;
+
+/**
+ * Reports a mandatory property with a null value.
+ */
+public class NullPropertyBuilder extends AbstractProblemBuilder {
+ private final TransformerContext context;
+
+ public NullPropertyBuilder(TransformerContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public void report() {
+ context.reportProblem(format("%s '%s' was null", type == null ? "Property" : type + " property", property));
+ }
+
+}
diff --git a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/ProblemBuilder.java b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/ProblemBuilder.java
new file mode 100644
index 00000000000..a11b04becd4
--- /dev/null
+++ b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/ProblemBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+/**
+ * Reports typed problems to the transformation context.
+ */
+public class ProblemBuilder {
+ private TransformerContext context;
+
+ public ProblemBuilder(TransformerContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Reports a missing mandatory property.
+ */
+ public MissingPropertyBuilder missingProperty() {
+ return new MissingPropertyBuilder(context);
+ }
+
+ /**
+ * Reports a mandatory property whose value is null or empty.
+ */
+ public NullPropertyBuilder nullProperty() {
+ return new NullPropertyBuilder(context);
+ }
+
+ /**
+ * Reports an invalid property value.
+ */
+ public InvalidPropertyBuilder invalidProperty() {
+ return new InvalidPropertyBuilder(context);
+ }
+
+ /**
+ * Reports an attempt to read a property value that is not of the expected type.
+ */
+ public UnexpectedTypeBuilder unexpectedType() {
+ return new UnexpectedTypeBuilder(context);
+ }
+
+}
diff --git a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/TransformerContext.java b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/TransformerContext.java
index 6f6fed573c7..c090dadb7b4 100644
--- a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/TransformerContext.java
+++ b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/TransformerContext.java
@@ -36,9 +36,18 @@ public interface TransformerContext {
/**
* Reports a problem.
+ *
+ * Note {@link #problem()} should be used in most cases.
*/
void reportProblem(String problem);
+ /**
+ * Returns a problem builder that can be used to report problems in a typed manner.
+ *
+ * Note this method should be preferred to reporting untyped problems using {@link #reportProblem(String)}.
+ */
+ ProblemBuilder problem();
+
/**
* Transforms the input object and any contained types, returning its transformed representation or null if the
* operation cannot be completed or input is null.
@@ -64,4 +73,4 @@ default Class> typeAlias(String type) {
default Class> typeAlias(String type, Class> defaultType) {
return defaultType;
}
-}
\ No newline at end of file
+}
diff --git a/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/UnexpectedTypeBuilder.java b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/UnexpectedTypeBuilder.java
new file mode 100644
index 00000000000..a248a68159e
--- /dev/null
+++ b/spi/common/transform-spi/src/main/java/org/eclipse/edc/transform/spi/UnexpectedTypeBuilder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reports an attempt to read a property value that is not of the expected type(s).
+ */
+public class UnexpectedTypeBuilder extends AbstractProblemBuilder {
+ private final TransformerContext context;
+
+ private String actual = UNKNOWN;
+ private List expected = new ArrayList<>();
+
+ public UnexpectedTypeBuilder(TransformerContext context) {
+ this.context = context;
+ }
+
+ public UnexpectedTypeBuilder actual(String actual) {
+ this.actual = actual;
+ return this;
+ }
+
+ public UnexpectedTypeBuilder actual(Class> actual) {
+ this.actual = actual == null ? null : actual.getName();
+ return this;
+ }
+
+ public UnexpectedTypeBuilder actual(Enum> actual) {
+ this.actual = actual == null ? null : actual.toString();
+ return this;
+ }
+
+ public UnexpectedTypeBuilder expected(String expectedType) {
+ if (expectedType == null) {
+ return this;
+ }
+ expected.add(expectedType);
+ return this;
+ }
+
+ public UnexpectedTypeBuilder expected(Enum> expectedType) {
+ if (expectedType == null) {
+ return this;
+ }
+ expected.add(expectedType.toString());
+ return this;
+ }
+
+ public UnexpectedTypeBuilder expected(Class> expectedType) {
+ if (expectedType == null) {
+ return this;
+ }
+ if (expectedType.isEnum()) {
+ for (var constant : expectedType.getEnumConstants()) {
+ expected.add(constant.toString());
+ }
+ } else {
+ expected.add(expectedType.getName());
+ }
+ return this;
+ }
+
+ @Override
+ public void report() {
+ var builder = new StringBuilder();
+ if (type != null) {
+ builder.append(type);
+ if (property != null) {
+ builder.append(" property '").append(property).append("'");
+ }
+ } else {
+ if (property != null) {
+ builder.append("Property '").append(property).append("'");
+ }
+ }
+ if (expected.isEmpty()) {
+ if (builder.length() == 0) {
+ builder.append("Value ");
+ }
+ builder.append("was not of the expected type");
+ } else {
+ if (builder.length() == 0) {
+ builder.append("Value");
+ }
+ builder.append(" must be ").append(concatList(expected));
+ }
+ if (actual != null) {
+ builder.append(" but was: ").append(actual);
+ }
+ context.reportProblem(builder.toString());
+ }
+
+}
diff --git a/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/AbstractProblemBuilderTestBuilder.java b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/AbstractProblemBuilderTestBuilder.java
new file mode 100644
index 00000000000..18bc6fa4bb7
--- /dev/null
+++ b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/AbstractProblemBuilderTestBuilder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class AbstractProblemBuilderTestBuilder {
+ private AbstractProblemBuilder> builder;
+
+ @BeforeEach
+ void setUp() {
+ builder = new AbstractProblemBuilder<>() {
+ @Override
+ public void report() {
+
+ }
+ };
+ }
+
+ @Test
+ void verifyConcatList() {
+ assertThat(builder.concatList(List.of("one"))).isEqualTo("one");
+ assertThat(builder.concatList(List.of("one", "two"))).isEqualTo("one or two");
+ assertThat(builder.concatList(List.of("one", "two", "three"))).isEqualTo("one, two, or three");
+ }
+
+}
diff --git a/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/InvalidPropertyBuilderTest.java b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/InvalidPropertyBuilderTest.java
new file mode 100644
index 00000000000..0d76eb7694c
--- /dev/null
+++ b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/InvalidPropertyBuilderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+class InvalidPropertyBuilderTest {
+ private TransformerContext context;
+ private InvalidPropertyBuilder builder;
+
+ @BeforeEach
+ void setUp() {
+ context = mock(TransformerContext.class);
+ builder = new InvalidPropertyBuilder(context);
+ }
+
+ @Test
+ void verify_reportNoData() {
+ builder.report();
+ verify(context).reportProblem(eq("Property 'null' was invalid: unknown"));
+ }
+
+ @Test
+ void verify_report() {
+ builder.type("test").property("property").value("value").report();
+ verify(context).reportProblem(eq("test property 'property' was invalid: value"));
+ }
+
+ @Test
+ void verify_reportProperty() {
+ builder.property("property").value("value").report();
+ verify(context).reportProblem(eq("Property 'property' was invalid: value"));
+ }
+
+ @Test
+ void verify_reportNoProperty() {
+ builder.type("test").value("value").report();
+ verify(context).reportProblem(eq("test property 'null' was invalid: value"));
+ }
+
+}
diff --git a/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/MissingPropertyBuilderTest.java b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/MissingPropertyBuilderTest.java
new file mode 100644
index 00000000000..94b2d041fde
--- /dev/null
+++ b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/MissingPropertyBuilderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+class MissingPropertyBuilderTest {
+ private TransformerContext context;
+ private MissingPropertyBuilder builder;
+
+ @BeforeEach
+ void setUp() {
+ context = mock(TransformerContext.class);
+ builder = new MissingPropertyBuilder(context);
+ }
+
+ @Test
+ void verify_reportNoData() {
+ builder.report();
+ verify(context).reportProblem(eq("Property 'null' was missing"));
+ }
+
+ @Test
+ void verify_report() {
+ builder.type("test").property("property").report();
+ verify(context).reportProblem(eq("test property 'property' was missing"));
+ }
+
+ @Test
+ void verify_reportProperty() {
+ builder.property("property").report();
+ verify(context).reportProblem(eq("Property 'property' was missing"));
+ }
+
+ @Test
+ void verify_reportNoProperty() {
+ builder.type("test").report();
+ verify(context).reportProblem(eq("test property 'null' was missing"));
+ }
+
+}
diff --git a/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/NullPropertyBuilderTest.java b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/NullPropertyBuilderTest.java
new file mode 100644
index 00000000000..188337606cb
--- /dev/null
+++ b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/NullPropertyBuilderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+class NullPropertyBuilderTest {
+ private TransformerContext context;
+ private NullPropertyBuilder builder;
+
+ @BeforeEach
+ void setUp() {
+ context = mock(TransformerContext.class);
+ builder = new NullPropertyBuilder(context);
+ }
+
+ @Test
+ void verify_reportNoData() {
+ builder.report();
+ verify(context).reportProblem(eq("Property 'null' was null"));
+ }
+
+ @Test
+ void verify_report() {
+ builder.type("test").property("property").report();
+ verify(context).reportProblem(eq("test property 'property' was null"));
+ }
+
+ @Test
+ void verify_reportProperty() {
+ builder.property("property").report();
+ verify(context).reportProblem(eq("Property 'property' was null"));
+ }
+
+ @Test
+ void verify_reportNoProperty() {
+ builder.type("test").report();
+ verify(context).reportProblem(eq("test property 'null' was null"));
+ }
+
+}
diff --git a/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/UnexpectedTypeBuilderTest.java b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/UnexpectedTypeBuilderTest.java
new file mode 100644
index 00000000000..4b491e76e5d
--- /dev/null
+++ b/spi/common/transform-spi/src/test/java/org/eclipse/edc/transform/spi/UnexpectedTypeBuilderTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.transform.spi;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+class UnexpectedTypeBuilderTest {
+ private TransformerContext context;
+ private UnexpectedTypeBuilder builder;
+
+ @BeforeEach
+ void setUp() {
+ context = mock(TransformerContext.class);
+ builder = new UnexpectedTypeBuilder(context);
+ }
+
+ @Test
+ void verify_reportNoData() {
+ builder.report();
+ verify(context).reportProblem(eq("Value was not of the expected type but was: unknown"));
+ }
+
+ @Test
+ void verify_report() {
+ builder.type("test").property("property").expected(String.class).actual(Integer.class).report();
+ verify(context).reportProblem(eq("test property 'property' must be java.lang.String but was: java.lang.Integer"));
+ }
+
+ @Test
+ void verify_reportMultipleExpected() {
+ builder.type("test").property("property").expected(String.class).expected(Long.class).actual(Integer.class).report();
+ verify(context).reportProblem(eq("test property 'property' must be java.lang.String or java.lang.Long but was: java.lang.Integer"));
+ }
+
+ @Test
+ void verify_reportNoType() {
+ builder.type("test").type("test").expected(String.class).actual(Integer.class).report();
+ verify(context).reportProblem(eq("test must be java.lang.String but was: java.lang.Integer"));
+ }
+
+ @Test
+ void verify_reportNoProperty() {
+ builder.type("test").expected(String.class).actual(Integer.class).report();
+ verify(context).reportProblem(eq("test must be java.lang.String but was: java.lang.Integer"));
+ }
+
+ @Test
+ void verify_reportNoTypeNoProperty() {
+ builder.expected(String.class).actual(Integer.class).report();
+ verify(context).reportProblem(eq("Value must be java.lang.String but was: java.lang.Integer"));
+ }
+
+}