LargeNumberWriteTest.java

package com.fasterxml.jackson.core.constraints;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.*;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

/**
 * Tests verifying that there are no limits for writing very long numbers
 * ({@link BigInteger}s and {@link BigDecimal}s) even using default
 * settings where reads may be prevented for security reasons.
 *
 * @since 2.15
 */
class LargeNumberWriteTest extends JUnit5TestBase
{
    private final JsonFactory VANILLA_JSON_F = new JsonFactory();

    private final JsonFactory NO_LIMITS_JSON_F = JsonFactory.builder()
            .streamReadConstraints(StreamReadConstraints.builder().maxNumberLength(Integer.MAX_VALUE).build())
            .build();

    private final BigDecimal BIG_DECIMAL;
    {
        final StringBuilder sb = new StringBuilder("0.");
        for (int i = 0; i < 2500; i++) {
            sb.append(i % 10);
        }
        BIG_DECIMAL = new BigDecimal(sb.toString());
    }

    private final BigInteger BIG_INT;
    {
        final StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 2500; i++) {
            sb.append(i % 10);
        }
        BIG_INT = new BigInteger(sb.toString());
    }

    @Test
    void writeLargeIntegerByteArray() throws Exception
    {
        try(
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                JsonGenerator gen = VANILLA_JSON_F.createGenerator(out);
        ) {
            _writeLargeNumberDoc(gen, BIG_INT);
            try(JsonParser p = NO_LIMITS_JSON_F.createParser(out.toByteArray())) {
                _verifyLargeNumberDoc(p, BIG_INT);
            }
        }
    }

    @Test
    void writeLargeIntegerStringWriter() throws Exception
    {
        try(
                StringWriter out = new StringWriter();
                JsonGenerator gen = VANILLA_JSON_F.createGenerator(out);
        ) {
            _writeLargeNumberDoc(gen, BIG_INT);
            try(JsonParser p = NO_LIMITS_JSON_F.createParser(out.toString())) {
                _verifyLargeNumberDoc(p, BIG_INT);
            }
        }
    }

    @Test
    void writeLargeIntegerDataOutput() throws Exception
    {
        try(
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(out);
                JsonGenerator gen = VANILLA_JSON_F.createGenerator((DataOutput) dos);
        ) {
            _writeLargeNumberDoc(gen, BIG_INT);

            try(
                    ByteArrayInputStream bis = new ByteArrayInputStream(out.toByteArray());
                    DataInputStream dis = new DataInputStream(bis);
                    JsonParser p = NO_LIMITS_JSON_F.createParser((DataInput) dis)
            ) {
                _verifyLargeNumberDoc(p, BIG_INT);
            }
        }
    }

    @Test
    void writeLargeDecimalByteArray() throws Exception
    {
        try(
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            JsonGenerator gen = VANILLA_JSON_F.createGenerator(out);
        ) {
            _writeLargeNumberDoc(gen, BIG_DECIMAL);
            try(JsonParser p = NO_LIMITS_JSON_F.createParser(out.toByteArray())) {
                _verifyLargeNumberDoc(p, BIG_DECIMAL);
            }
        }
    }

    @Test
    void writeLargeDecimalStringWriter() throws Exception
    {
        try(
                StringWriter out = new StringWriter();
                JsonGenerator gen = VANILLA_JSON_F.createGenerator(out);
        ) {
            _writeLargeNumberDoc(gen, BIG_DECIMAL);
            try(JsonParser p = NO_LIMITS_JSON_F.createParser(out.toString())) {
                _verifyLargeNumberDoc(p, BIG_DECIMAL);
            }
        }
    }

    @Test
    void writeLargeDecimalDataOutput() throws Exception
    {
        try(
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(out);
                JsonGenerator gen = VANILLA_JSON_F.createGenerator((DataOutput) dos);
        ) {
            _writeLargeNumberDoc(gen, BIG_DECIMAL);
            try (
                    ByteArrayInputStream bis = new ByteArrayInputStream(out.toByteArray());
                    DataInputStream dis = new DataInputStream(bis);
                    JsonParser p = NO_LIMITS_JSON_F.createParser((DataInput) dis)
            ) {
                _verifyLargeNumberDoc(p, BIG_DECIMAL);
            }
        }
    }

    private void _writeLargeNumberDoc(JsonGenerator g, BigInteger bigValue) throws Exception
    {
        g.writeStartObject();
        g.writeFieldName("field");
        g.writeNumber(bigValue);
        g.writeEndObject();
        g.close();
    }

    private void _writeLargeNumberDoc(JsonGenerator g, BigDecimal bigValue) throws Exception
    {
        g.writeStartObject();
        g.writeFieldName("field");
        g.writeNumber(bigValue);
        g.writeEndObject();
        g.close();
    }

    private void _verifyLargeNumberDoc(JsonParser p, BigInteger bigValue) throws Exception
    {
        assertToken(JsonToken.START_OBJECT, p.nextToken());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("field", p.currentName());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(bigValue, p.getBigIntegerValue());
        assertToken(JsonToken.END_OBJECT, p.nextToken());
        assertNull(p.nextToken());
    }

    private void _verifyLargeNumberDoc(JsonParser p, BigDecimal bigValue) throws Exception
    {
        assertToken(JsonToken.START_OBJECT, p.nextToken());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("field", p.currentName());
        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
        assertEquals(bigValue, p.getDecimalValue());
        assertToken(JsonToken.END_OBJECT, p.nextToken());
        assertNull(p.nextToken());
    }
}