JsonNodeDecimalValueTest.java
package tools.jackson.databind.node;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.exc.JsonNodeException;
import tools.jackson.databind.util.RawValue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests for [databind#4958], JsonNode.decimalValue() (and related) parts
* over all types.
*/
public class JsonNodeDecimalValueTest
extends NodeTestBase
{
private final JsonNodeFactory NODES = newJsonMapper().getNodeFactory();
private final BigDecimal BD_ONE = BigDecimal.ONE;
private final BigDecimal BD_ONE_O = new BigDecimal("1.0");
private final BigDecimal BD_DEFAULT = bigDec(12.125);
// // // decimalValue() tests
// decimalValue() + Numbers/Integers
@Test
public void decimalValueFromNumberIntOk()
{
_assertDecimalValue(BD_ONE, NODES.numberNode((byte) 1));
_assertDecimalValue(bigDec(Byte.MIN_VALUE), NODES.numberNode(Byte.MIN_VALUE));
_assertDecimalValue(bigDec(Byte.MAX_VALUE), NODES.numberNode(Byte.MAX_VALUE));
_assertDecimalValue(BD_ONE, NODES.numberNode((short) 1));
_assertDecimalValue(bigDec(Short.MIN_VALUE), NODES.numberNode(Short.MIN_VALUE));
_assertDecimalValue(bigDec(Short.MAX_VALUE), NODES.numberNode(Short.MAX_VALUE));
_assertDecimalValue(BD_ONE, NODES.numberNode(1));
_assertDecimalValue(bigDec(Integer.MIN_VALUE), NODES.numberNode(Integer.MIN_VALUE));
_assertDecimalValue(bigDec(Integer.MAX_VALUE), NODES.numberNode(Integer.MAX_VALUE));
_assertDecimalValue(BD_ONE, NODES.numberNode(1L));
_assertDecimalValue(bigDec(Long.MIN_VALUE), NODES.numberNode(Long.MIN_VALUE));
_assertDecimalValue(bigDec(Long.MAX_VALUE), NODES.numberNode(Long.MAX_VALUE));
_assertDecimalValue(BD_ONE, NODES.numberNode(BigInteger.valueOf(1)));
_assertDecimalValue(bigDec(Long.MIN_VALUE), NODES.numberNode(BigInteger.valueOf(Long.MIN_VALUE)));
_assertDecimalValue(bigDec(Long.MAX_VALUE), NODES.numberNode(BigInteger.valueOf(Long.MAX_VALUE)));
}
// Cannot fail for Over/Underflow from Integer values
//@Test public void failBigDecimalValueFromNumberIntRange() { }
// decimalValue() + Numbers/FPs
@Test
public void decimalValueFromNumberFPOk()
{
_assertDecimalValue(BD_ONE_O, NODES.numberNode(1.0f));
_assertDecimalValue(BD_ONE_O, NODES.numberNode(1.0f));
_assertDecimalValue(bigDec("100000.0"), NODES.numberNode(100_000.0f));
_assertDecimalValue(bigDec("-100000.0"), NODES.numberNode(-100_000.0f));
_assertDecimalValue(BD_ONE_O, NODES.numberNode(1.0d));
_assertDecimalValue(bigDec("100000.0"), NODES.numberNode(100_000.0d));
_assertDecimalValue(bigDec("-100000.0"), NODES.numberNode(-100_000.0d));
_assertDecimalValue(new BigDecimal("100.001"),
NODES.numberNode(new BigDecimal("100.001")));
}
// Cannot fail for Over/Underflow from FP values either
//@Test public void failBigDecimalFromNumberFPRange() { }
// But can fail for NaN
@Test
public void decimalValueFromNumberFPFail()
{
_assertFailDecimalValueForNaN(NODES.numberNode(Float.NaN));
_assertFailDecimalValueForNaN(NODES.numberNode(Float.POSITIVE_INFINITY));
_assertFailDecimalValueForNaN(NODES.numberNode(Float.NEGATIVE_INFINITY));
_assertFailDecimalValueForNaN(NODES.numberNode(Double.NaN));
_assertFailDecimalValueForNaN(NODES.numberNode(Double.POSITIVE_INFINITY));
_assertFailDecimalValueForNaN(NODES.numberNode(Double.NEGATIVE_INFINITY));
}
// decimalValue() + non-Numeric types
@Test
public void failBigDecimalFromNonNumberScalar()
{
_assertFailDecimalValueForNonNumber(NODES.booleanNode(true));
_assertFailDecimalValueForNonNumber(NODES.binaryNode(new byte[3]));
_assertFailDecimalValueForNonNumber(NODES.stringNode("123"));
_assertFailDecimalValueForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertFailDecimalValueForNonNumber(NODES.pojoNode(Boolean.TRUE));
}
@Test
public void failBigDecimalValueFromMiscOther()
{
_assertFailDecimalValueForNonNumber(NODES.nullNode());
_assertFailDecimalValueForNonNumber(NODES.missingNode());
}
@Test
public void failBigDecimalValueFromStructural()
{
_assertFailDecimalValueForNonNumber(NODES.arrayNode(3));
_assertFailDecimalValueForNonNumber(NODES.objectNode());
}
// // // asDecimal() tests
@Test
public void asDecimalFromNumberIntOk()
{
_assertAsDecimal(BD_ONE, NODES.numberNode((byte) 1));
_assertAsDecimal(bigDec(Byte.MIN_VALUE), NODES.numberNode(Byte.MIN_VALUE));
_assertAsDecimal(bigDec(Byte.MAX_VALUE), NODES.numberNode(Byte.MAX_VALUE));
_assertAsDecimal(BD_ONE, NODES.numberNode((short) 1));
_assertAsDecimal(bigDec(Short.MIN_VALUE), NODES.numberNode(Short.MIN_VALUE));
_assertAsDecimal(bigDec(Short.MAX_VALUE), NODES.numberNode(Short.MAX_VALUE));
_assertAsDecimal(BD_ONE, NODES.numberNode(1));
_assertAsDecimal(bigDec(Integer.MIN_VALUE), NODES.numberNode(Integer.MIN_VALUE));
_assertAsDecimal(bigDec(Integer.MAX_VALUE), NODES.numberNode(Integer.MAX_VALUE));
_assertAsDecimal(BD_ONE, NODES.numberNode(1L));
_assertAsDecimal(bigDec(Long.MIN_VALUE), NODES.numberNode(Long.MIN_VALUE));
_assertAsDecimal(bigDec(Long.MAX_VALUE), NODES.numberNode(Long.MAX_VALUE));
_assertAsDecimal(BD_ONE, NODES.numberNode(BigInteger.valueOf(1)));
_assertAsDecimal(bigDec(Long.MIN_VALUE), NODES.numberNode(BigInteger.valueOf(Long.MIN_VALUE)));
_assertAsDecimal(bigDec(Long.MAX_VALUE), NODES.numberNode(BigInteger.valueOf(Long.MAX_VALUE)));
}
@Test
public void asDecimalFromNumberFPOk()
{
_assertAsDecimal(BD_ONE_O, NODES.numberNode(1.0f));
_assertAsDecimal(BD_ONE_O, NODES.numberNode(1.0f));
_assertAsDecimal(bigDec("100000.0"), NODES.numberNode(100_000.0f));
_assertAsDecimal(bigDec("-100000.0"), NODES.numberNode(-100_000.0f));
_assertAsDecimal(BD_ONE_O, NODES.numberNode(1.0d));
_assertAsDecimal(bigDec("100000.0"), NODES.numberNode(100_000.0d));
_assertAsDecimal(bigDec("-100000.0"), NODES.numberNode(-100_000.0d));
_assertAsDecimal(new BigDecimal("100.001"),
NODES.numberNode(new BigDecimal("100.001")));
}
// Cannot fail for Over/Underflow from FP values either
// But can fail for NaN
@Test
public void asDecimalFromNumberFPFail()
{
_assertFailAsDecimalForNaN(NODES.numberNode(Float.NaN));
_assertFailAsDecimalForNaN(NODES.numberNode(Float.POSITIVE_INFINITY));
_assertFailAsDecimalForNaN(NODES.numberNode(Float.NEGATIVE_INFINITY));
_assertFailAsDecimalForNaN(NODES.numberNode(Double.NaN));
_assertFailAsDecimalForNaN(NODES.numberNode(Double.POSITIVE_INFINITY));
_assertFailAsDecimalForNaN(NODES.numberNode(Double.NEGATIVE_INFINITY));
}
// asDecimal() + non-Numeric types
@Test
public void asDecimalFromNonNumberScalar()
{
// Regular failing cases
_assertFailAsDecimalForNonNumber(NODES.booleanNode(true));
_assertFailAsDecimalForNonNumber(NODES.binaryNode(new byte[3]));
_assertFailAsDecimalForNonNumber(NODES.rawValueNode(new RawValue("abc")));
_assertFailAsDecimalForNonNumber(NODES.pojoNode(Boolean.TRUE));
// Special failing cases:
_assertFailAsDecimal(NODES.stringNode("abc"),
"value not a valid String representation of `BigDecimal`");
// Passing cases
_assertAsDecimal(BigDecimal.valueOf(2), NODES.stringNode("2"));
_assertAsDecimal(BigDecimal.TEN, NODES.pojoNode(10));
}
@Test
public void asDecimalFailFromStructural()
{
_assertFailAsDecimalForNonNumber(NODES.arrayNode(3));
_assertFailAsDecimalForNonNumber(NODES.objectNode());
}
@Test
public void asDecimalFromMiscOther()
{
// "null" becomes "0.0"
_assertAsDecimal(BigDecimal.ZERO, NODES.nullNode());
// but "missing" still fails
_assertFailAsDecimalForNonNumber(NODES.missingNode());
}
// // // Shared helper methods
private void _assertDecimalValue(BigDecimal expected, JsonNode fromNode)
{
// main accessor
assertEquals(expected, fromNode.decimalValue());
// but also defaulting
assertEquals(expected, fromNode.decimalValue(BD_DEFAULT));
assertEquals(expected, fromNode.decimalValueOpt().get());
}
private void _assertAsDecimal(BigDecimal expected, JsonNode fromNode)
{
// main accessor
assertEquals(expected, fromNode.asDecimal());
// but also defaulting
assertEquals(expected, fromNode.asDecimal(BD_DEFAULT));
assertEquals(expected, fromNode.asDecimalOpt().get());
}
private void _assertFailDecimalValueForNonNumber(JsonNode node) {
Exception e = assertThrows(JsonNodeException.class,
() -> node.decimalValue(),
"For ("+node.getClass().getSimpleName()+") value: "+node);
assertThat(e.getMessage())
.contains("decimalValue()")
.contains("cannot convert value")
.contains("value type not numeric");
// Verify default value handling
assertEquals(BD_DEFAULT, node.decimalValue(BD_DEFAULT));
assertEquals(Optional.empty(), node.decimalValueOpt());
}
private void _assertFailDecimalValueForNaN(JsonNode node) {
Exception e = assertThrows(JsonNodeException.class,
() -> node.decimalValue(),
"For ("+node.getClass().getSimpleName()+") value: "+node);
assertThat(e.getMessage())
.contains("decimalValue()")
.contains("cannot convert value")
.contains("value non-Finite ('NaN')");
// Verify default value handling
assertEquals(BD_DEFAULT, node.decimalValue(BD_DEFAULT));
assertEquals(Optional.empty(), node.decimalValueOpt());
}
private void _assertFailAsDecimalForNonNumber(JsonNode node) {
_assertFailAsDecimal(node, "value type not coercible to `BigDecimal`");
}
private void _assertFailAsDecimal(JsonNode node, String extraFailMsg) {
Exception e = assertThrows(JsonNodeException.class,
() -> node.asDecimal(),
"For ("+node.getClass().getSimpleName()+") value: "+node);
assertThat(e.getMessage())
.contains("asDecimal()")
.contains("cannot convert value")
.contains(extraFailMsg);
// Verify default value handling
assertEquals(BD_DEFAULT, node.asDecimal(BD_DEFAULT));
assertEquals(Optional.empty(), node.asDecimalOpt());
}
private void _assertFailAsDecimalForNaN(JsonNode node) {
Exception e = assertThrows(JsonNodeException.class,
() -> node.asDecimal(),
"For ("+node.getClass().getSimpleName()+") value: "+node);
assertThat(e.getMessage())
.contains("asDecimal()")
.contains("cannot convert value")
.contains("value non-Finite ('NaN')");
// Verify default value handling
assertEquals(BD_DEFAULT, node.asDecimal(BD_DEFAULT));
assertEquals(Optional.empty(), node.asDecimalOpt());
}
protected static Optional<BigDecimal> bigDecOpt(BigDecimal bigDec) {
return Optional.of(bigDec);
}
}