DoubleFPLengthConstraintTest.java

package tools.jackson.databind.deser.dos;

import org.junit.jupiter.api.Test;

import tools.jackson.core.StreamReadConstraints;
import tools.jackson.core.exc.StreamConstraintsException;
import tools.jackson.core.json.JsonFactory;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.fail;

import static tools.jackson.databind.testutil.DatabindTestUtil.verifyException;

// Number-length constraint (StreamReadConstraints.maxNumberLength) must be enforced
// when coercing a String to `double` the same way it already is for `float`.
public class DoubleFPLengthConstraintTest
{
    // Deliberately NOT the default (1000), so that these tests actually depend on
    // the configured constraint being applied
    private final static int MAX_NUMBER_LEN = 100;

    private final static int OVER_LONG_LEN = MAX_NUMBER_LEN + 50;

    private final static String OVER_LONG_NUMBER = "9".repeat(OVER_LONG_LEN);

    private ObjectMapper mapperWithNumberLen(int maxLen) {
        JsonFactory f = JsonFactory.builder()
                .streamReadConstraints(StreamReadConstraints.builder().maxNumberLength(maxLen).build())
                .build();
        return JsonMapper.builder(f).build();
    }

    private String jsonArrayWith(String value) {
        return """
                ["%s"]
                """.formatted(value);
    }

    @Test
    public void doubleArrayFromStringRespectsNumberLength() throws Exception
    {
        ObjectMapper mapper = mapperWithNumberLen(MAX_NUMBER_LEN);
        try {
            mapper.readValue(jsonArrayWith(OVER_LONG_NUMBER), double[].class);
            fail("Should not pass: number length exceeds configured maximum");
        } catch (StreamConstraintsException e) {
            verifyException(e, "Number value length ("+OVER_LONG_LEN+")");
            verifyException(e, "exceeds the maximum allowed ("+MAX_NUMBER_LEN);
        }
    }

    // Parity check: `float[]` already enforces the same limit; both should behave alike.
    @Test
    public void floatArrayFromStringRespectsNumberLength() throws Exception
    {
        ObjectMapper mapper = mapperWithNumberLen(MAX_NUMBER_LEN);
        try {
            mapper.readValue(jsonArrayWith(OVER_LONG_NUMBER), float[].class);
            fail("Should not pass: number length exceeds configured maximum");
        } catch (StreamConstraintsException e) {
            verifyException(e, "Number value length ("+OVER_LONG_LEN+")");
            verifyException(e, "exceeds the maximum allowed ("+MAX_NUMBER_LEN);
        }
    }

    // And verify the input itself is otherwise acceptable: it is only the lowered
    // limit above that rejects it, not the value being unparseable
    @Test
    public void overLongNumberAcceptedWithDefaultConstraints() throws Exception
    {
        ObjectMapper mapper = JsonMapper.builder().build();
        assertArrayEquals(new double[] { Double.parseDouble(OVER_LONG_NUMBER) },
                mapper.readValue(jsonArrayWith(OVER_LONG_NUMBER), double[].class));
    }
}