NumberNodes1770Test.java

package com.fasterxml.jackson.databind.node;

import java.math.BigDecimal;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.JsonNodeFeature;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.*;

// Tests for [databind#1770], [databind#4194]
public class NumberNodes1770Test extends DatabindTestUtil
{
    // For to [databind#1770] (broken due to fix for #1028): `JsonNodeDeserializer`
    // would coerce ok but does `parser.isNaN()` check which ends up parsing
    // as Double, gets `POSITIVE_INFINITY` and returns `true`: this results in
    // `DoubleNode` being used even tho `BigDecimal` could fit the number.
    @Test
    public void testBigDecimalCoercion() throws Exception
    {
        final String value = "7976931348623157e309";
        final JsonNode jsonNode = newJsonMapper().reader()
            .with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
            .readTree(value);
        assertTrue(jsonNode.isBigDecimal(), "Expected DecimalNode, got: "+jsonNode.getClass().getName()+": "+jsonNode);
        assertEquals(new BigDecimal(value), jsonNode.decimalValue());
    }

    @Test
    public void testBigDecimalCoercionInf() throws Exception
    {
        final String value = "+INF";
        JsonFactory factory = JsonFactory.builder()
                .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS)
                .build();
        final JsonNode jsonNode = new JsonMapper(factory).reader()
                .with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
                .readTree(value);
        assertTrue(jsonNode.isDouble(), "Expected DoubleNode, got: "+jsonNode.getClass().getName()+": "+jsonNode);
        assertEquals(Double.POSITIVE_INFINITY, jsonNode.doubleValue());
    }

    // [databind#4194]: should be able to, by configuration, fail coercing NaN to BigDecimal
    @Test
    public void testBigDecimalCoercionNaN() throws Exception
    {
        JsonNode n = _tryBigDecimalCoercionNaNWithOption(false);
        if (!n.isDouble()) {
            fail("Expected DoubleNode, got: "+n.getClass().getName());
        }
        assertEquals(Double.NaN, n.doubleValue());

        try {
            n = _tryBigDecimalCoercionNaNWithOption(true);
            fail("Should not pass without allowing coercion: produced JsonNode of type "
                    +n.getClass().getName());
        } catch (InvalidFormatException e) {
            verifyException(e, "Cannot convert NaN");
        }
    }

    private JsonNode _tryBigDecimalCoercionNaNWithOption(boolean isEnabled) throws Exception
    {
        JsonFactory factory = JsonFactory.builder()
                .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS)
                .build();
        final ObjectReader reader = new JsonMapper(factory)
                .reader()
                .with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);

        final String value = "NaN";
        // depending on option
        return isEnabled
                ? reader.with(JsonNodeFeature.FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION).readTree(value)
                : reader.without(JsonNodeFeature.FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION).readTree(value);
    }
}