diff --git a/src/main/java/com/devsoap/json/JsonArrayAssert.java b/src/main/java/com/devsoap/json/JsonArrayAssert.java index 683f09a..464154d 100644 --- a/src/main/java/com/devsoap/json/JsonArrayAssert.java +++ b/src/main/java/com/devsoap/json/JsonArrayAssert.java @@ -24,20 +24,32 @@ import org.assertj.core.api.AbstractListAssert; import java.util.List; +import static java.util.Objects.requireNonNull; +/** + * JSON Array Assertions + * + * @author John Ahlroos + */ public class JsonArrayAssert<VALUE_TYPE extends JsonValue> extends AbstractListAssert<JsonArrayAssert<VALUE_TYPE>, List<VALUE_TYPE>, VALUE_TYPE, JsonValueAssert<VALUE_TYPE>> { private final String fieldName; - protected JsonArrayAssert(String fieldName, JsonArray array) { - super(array.getValuesAs(jsonValue -> (VALUE_TYPE) jsonValue), JsonArrayAssert.class); + /** + * Create a new JSON array assert + * @param fieldName the field the array refers to or <code>null</code> if the root element + * @param value The array value + */ + public JsonArrayAssert(String fieldName, VALUE_TYPE value) { + super(List.of(requireNonNull(value, "type cannot be null")), JsonArrayAssert.class); this.fieldName = fieldName; } - public JsonArrayAssert(String fieldName, VALUE_TYPE value) { - super(List.of(value), JsonArrayAssert.class); + @SuppressWarnings("unchecked") + protected JsonArrayAssert(String fieldName, JsonArray array) { + super(requireNonNull(array, "Array cannot be null").getValuesAs(jsonValue -> (VALUE_TYPE) jsonValue), JsonArrayAssert.class); this.fieldName = fieldName; } diff --git a/src/main/java/com/devsoap/json/JsonAssert.java b/src/main/java/com/devsoap/json/JsonAssert.java index f6a3772..99ee8d1 100644 --- a/src/main/java/com/devsoap/json/JsonAssert.java +++ b/src/main/java/com/devsoap/json/JsonAssert.java @@ -32,19 +32,50 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static java.util.Objects.requireNonNull; + +/** + * JSON Assertions + * + * @author John Ahlroos + */ public class JsonAssert extends AbstractAssert<JsonAssert, String> { + /** + * Instantiates a new Json assert. + * + * @param json the json as a string + */ protected JsonAssert(String json) { - super(json, JsonAssert.class); + super(requireNonNull(json, "Json cannot be null"), JsonAssert.class); } + /** + * Assert JSON as a String + * + * @param json the json to assert on + * @return the json assert + */ public static JsonAssert assertThat(String json) { - return new JsonAssert(json); - } - public static JsonAssert assertThat(Object object) { - return assertThat(object.toString()); + return new JsonAssert(requireNonNull(json, "Json cannot be null")); } + /** + * Assert JSON on any object. Uses toString() to convert to a string + * + * @param object the object + * @return the json assert + */ + public static JsonAssert assertThat(Object object) { + return assertThat(requireNonNull(object, "Object cannot be null").toString()); + } + + /** + * Assert root object is an array of objects + * + * @return the json array assert + * @throws AssertionError if field cannot be converted to Array + */ public JsonArrayAssert<?> asArray() { try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var actualParser = Json.createParser(stream); @@ -55,7 +86,16 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Assert root object is an array of a single type + * + * @param <ITEM> the item type + * @param itemClass the item class + * @return the list assert + * @throws AssertionError if field cannot be converted to Array + */ public <ITEM> ListAssert<ITEM> asArray(Class<ITEM> itemClass) { + requireNonNull(itemClass, "Item class cannot be null"); try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var actualParser = Json.createParser(stream); actualParser.next(); // START_ARRAY @@ -74,7 +114,15 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Get the first field (or property) of a JSON object + * + * @param fieldName the field or property name + * @return the json value assert + * @throws AssertionError if field not found + */ public JsonValueAssert<?> firstField(String fieldName) { + requireNonNull(fieldName, "Field or Property name cannot be null"); try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var actualParser = Json.createParser(stream); actualParser.next(); // START_OBJECT @@ -85,7 +133,15 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Get the last field (or property) of a JSON object + * + * @param fieldName the field or property name + * @return the json value assert + * @throws AssertionError if field not found + */ public JsonValueAssert<?> lastField(String fieldName) { + requireNonNull(fieldName, "Field or Property name cannot be null"); try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var scannerParser = Json.createParser(stream); scannerParser.next(); // START_OBJECT @@ -96,7 +152,19 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Get the nth field (or property) of a JSON object + * + * @param fieldName the field or property name + * @param index the zero based index + * @return the json value assert + * @throws AssertionError if field not found + */ public JsonValueAssert<?> nthField(String fieldName, int index) { + requireNonNull(fieldName, "Field or Property name cannot be null"); + if (index < 0) { + throw new IllegalArgumentException("index must be zero or positive"); + } try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var parser = Json.createParser(stream); parser.next(); // START_OBJECT @@ -107,6 +175,13 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Get field or property by name + * + * @param fieldName the field or property name + * @return the json value assert + * @throws AssertionError if field not found + */ public JsonValueAssert<?> field(String fieldName) { try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var actualParser = Json.createParser(stream); @@ -118,6 +193,13 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Get field as array + * + * @param fieldName the field name + * @return the json array assert + * @throws AssertionError if field is not found or is not an Array + */ public JsonArrayAssert<?> array(String fieldName) { try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var actualParser = Json.createParser(stream); @@ -128,6 +210,15 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Get field as typed array + * + * @param <ITEM> the type parameter + * @param fieldName the field name + * @param itemClass the item class + * @return the list assert + * @throws AssertionError if field not found, the field is not an array or the array types cannot be converted + */ public <ITEM> ListAssert<ITEM> array(String fieldName, Class<ITEM> itemClass) { try (var stream = new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))) { var actualParser = Json.createParser(stream); @@ -138,6 +229,13 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { } } + /** + * Find a field using a path + * + * @param fields the path segments + * @return the json value assert + * @throws AssertionError if path not found + */ public JsonValueAssert<?> path(String... fields) { var actualParser = Json.createParser(new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))); actualParser.next(); // START_OBJECT @@ -242,7 +340,6 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { return Optional.of(current).flatMap(v -> typedJsonValue(fieldName, v)); } - static Optional<JsonValueAssert<?>> typedJsonValue(String fieldName, JsonValue jsonValue) { return Optional.ofNullable(jsonValue).map(v -> switch (jsonValue.getValueType()) { case ARRAY -> null; @@ -282,6 +379,7 @@ public class JsonAssert extends AbstractAssert<JsonAssert, String> { static JsonArrayAssert<?> typedJsonArray(String fieldName, JsonValue jsonArray) { return new JsonArrayAssert<>(fieldName, (JsonArray) jsonArray); } + static <ITEM> ListAssert<ITEM> typedJsonArray(JsonValue jsonArray) { return new ListAssert<>(((JsonArray)jsonArray).getValuesAs(value -> (ITEM) switch (value.getValueType()) { case ARRAY -> null; diff --git a/src/main/java/com/devsoap/json/JsonValueAssert.java b/src/main/java/com/devsoap/json/JsonValueAssert.java index 78241ec..f905d57 100644 --- a/src/main/java/com/devsoap/json/JsonValueAssert.java +++ b/src/main/java/com/devsoap/json/JsonValueAssert.java @@ -29,7 +29,13 @@ import java.math.BigInteger; import java.util.regex.Pattern; import static com.devsoap.json.JsonAssert.typedJsonArray; +import static java.util.Objects.requireNonNull; +/** + * JSON Array Assertions + * + * @author John Ahlroos + */ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert<VALUE_TYPE>, VALUE_TYPE> { private final String fieldName; @@ -41,7 +47,14 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< this.value = value; } + /** + * Get a field or property by name + * + * @param fieldName the field name + * @return the json value assert + */ public JsonValueAssert<?> field(String fieldName) { + requireNonNull(fieldName, "Field or Property name cannot be null"); if (value.getValueType() != JsonValue.ValueType.OBJECT) { failWithActualExpectedAndMessage(value.getValueType(), JsonValue.ValueType.OBJECT, "Only objects can have fields"); @@ -50,7 +63,17 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< .orElseGet(() -> this.withFailMessage("Field %s not found", fieldName)); } + /** + * Get typed field + * + * @param fieldName the field name + * @param type the type + * @return the json value assert + */ + @SuppressWarnings("unchecked") public JsonValueAssert<VALUE_TYPE> field(String fieldName, Class<VALUE_TYPE> type) { + requireNonNull(fieldName, "Field or Property name cannot be null"); + requireNonNull(type, "Type cannot be null"); if (value.getValueType() != JsonValue.ValueType.OBJECT) { failWithActualExpectedAndMessage(value.getValueType(), JsonValue.ValueType.OBJECT, "Only objects can have fields"); @@ -59,7 +82,14 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< .orElseGet(() -> this.withFailMessage("Field %s not found", fieldName)); } + /** + * Get field as array + * + * @param fieldName the field name + * @return the json array assert + */ public JsonArrayAssert<?> array(String fieldName) { + requireNonNull(fieldName, "Field or Property name cannot be null"); if (value.getValueType() != JsonValue.ValueType.OBJECT) { failWithActualExpectedAndMessage(value.getValueType(), JsonValue.ValueType.OBJECT, "Only objects can have fields"); @@ -74,7 +104,17 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return typedJsonArray(fieldName, jsonValue); } + /** + * Get field as typed array + * + * @param <ITEM> the type parameter + * @param fieldName the field name + * @param itemClass the item class + * @return the list assert + */ public <ITEM> ListAssert<ITEM> array(String fieldName, Class<ITEM> itemClass) { + requireNonNull(fieldName, "Field or Property name cannot be null"); + requireNonNull(itemClass, "item type name cannot be null"); if (value.getValueType() != JsonValue.ValueType.OBJECT) { failWithActualExpectedAndMessage(value.getValueType(), JsonValue.ValueType.OBJECT, "Only objects can have fields"); @@ -98,7 +138,14 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< })); } + /** + * Get field by path + * + * @param fields the fields + * @return the json value assert + */ public JsonValueAssert<?> path(String... fields) { + requireNonNull(fields, "Path cannot be null"); JsonValueAssert<?> currentField = this; for (String field : fields) { currentField = currentField.field(field); @@ -106,6 +153,11 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return currentField; } + /** + * Cast value to {@link Integer} + * + * @return the integer assert + */ public IntegerAssert asInteger() { if (actual instanceof Integer) { return new IntegerAssert((Integer) actual); @@ -113,6 +165,11 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return new IntegerAssert(Integer.parseInt(actual.toString())); } + /** + * Cast value to {@link BigDecimal} + * + * @return the big decimal assert + */ public BigDecimalAssert asBigDecimal() { if (actual instanceof BigDecimal) { return new BigDecimalAssert((BigDecimal) actual); @@ -120,6 +177,11 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return new BigDecimalAssert(BigDecimal.valueOf(Double.parseDouble(actual.toString()))); } + /** + * Cast value to {@link BigInteger} + * + * @return the big integer assert + */ public BigIntegerAssert asBigInteger() { if (actual instanceof BigInteger) { return new BigIntegerAssert((BigInteger) actual); @@ -127,6 +189,11 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return new BigIntegerAssert(BigInteger.valueOf(Integer.parseInt(actual.toString()))); } + /** + * Cast value to {@link Double} + * + * @return the double assert + */ public DoubleAssert asDouble() { if (actual instanceof Double) { return new DoubleAssert((Double) actual); @@ -134,6 +201,11 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return new DoubleAssert(Double.parseDouble(actual.toString())); } + /** + * Cast value to {@link Long} + * + * @return the abstract long assert + */ public AbstractLongAssert<?> asLong() { if (actual instanceof Long) { return new LongAssert((Long) actual); @@ -141,6 +213,11 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return new LongAssert(Long.parseLong(actual.toString())); } + /** + * Cast value to Array + * + * @return the json array assert + */ public JsonArrayAssert<?> asArray() { if (value.getValueType() == JsonValue.ValueType.ARRAY) { return new JsonArrayAssert<>(fieldName, (JsonArray) value); @@ -148,10 +225,22 @@ public class JsonValueAssert<VALUE_TYPE> extends AbstractAssert<JsonValueAssert< return new JsonArrayAssert<>(fieldName, value); } + /** + * Assert value matches regular expression + * + * @param regex the regex + * @return the json value assert + */ public JsonValueAssert<?> matches(String regex) { return matches(valueType -> Pattern.matches(regex, valueType.toString())); } + /** + * Assert value does not match regular expression + * + * @param regex the regex + * @return the json value assert + */ public JsonValueAssert<?> notMatches(String regex) { return matches(valueType -> !Pattern.matches(regex, valueType.toString())); }