Java8Datetime4533Test.java

package com.fasterxml.jackson.databind.interop;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;

public class Java8Datetime4533Test
        extends DatabindTestUtil
{
    private final ObjectMapper MAPPER = newJsonMapper();

    private final ObjectMapper LENIENT_MAPPER = JsonMapper.builder()
            .disable(MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES)
            .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS)
            .build();

    @Test
    public void testPreventSerialization() throws Exception
    {
        // [databind#4533]: prevent accidental serialization of Java 8 date/time types
        // as POJOs, without Java 8 date/time module:
        _testPreventSerialization(java.time.LocalDateTime.now());
        _testPreventSerialization(java.time.LocalDate.now());
        _testPreventSerialization(java.time.LocalTime.now());
        _testPreventSerialization(java.time.OffsetDateTime.now());
        _testPreventSerialization(java.time.ZonedDateTime.now());
    }

    private void _testPreventSerialization(Object value) throws Exception
    {
        try {
            String json = MAPPER.writeValueAsString(value);
            fail("Should not pass, wrote out as\n: "+json);
        } catch (com.fasterxml.jackson.databind.exc.InvalidDefinitionException e) {
            verifyException(e, "Java 8 date/time type `"+value.getClass().getName()
                    +"` not supported by default");
            verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\"");
            verifyException(e, "(or disable `MapperFeature.%s`)",
                    MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES.name());
        }
    }

    @Test
    public void testPreventDeserialization() throws Exception
    {
        // [databind#4533]: prevent accidental deserialization of Java 8 date/time types
        // as POJOs, without Java 8 date/time module:
        _testPreventDeserialization(java.time.LocalDateTime.class);
        _testPreventDeserialization(java.time.LocalDate.class);
        _testPreventDeserialization(java.time.LocalTime.class);
        _testPreventDeserialization(java.time.OffsetDateTime.class);
        _testPreventDeserialization(java.time.ZonedDateTime.class);
    }

    private void _testPreventDeserialization(Class<?> value) throws Exception
    {
        try {
            Object result = MAPPER.readValue(" 0 ", value);
            fail("Not expecting to pass, resulted in: "+result);
        } catch (com.fasterxml.jackson.databind.exc.InvalidDefinitionException e) {
            verifyException(e, "Java 8 date/time type `"+value.getName()
                    +"` not supported by default");
            verifyException(e, "add Module \"com.fasterxml.jackson.datatype:jackson-datatype-jsr310\"");
            verifyException(e, "(or disable `MapperFeature.%s`)",
                    MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES.name());
        }
    }

    @Test
    public void testLenientSerialization() throws Exception
    {
        _testLenientSerialization(java.time.LocalDateTime.now());
        _testLenientSerialization(java.time.LocalDate.now());
        _testLenientSerialization(java.time.LocalTime.now());
        _testLenientSerialization(java.time.OffsetDateTime.now());

        // Except ZonedDateTime serialization fails with ...
        try {
            _testLenientSerialization(java.time.ZonedDateTime.now());
            fail("Should not pass, wrote out as\n: "+java.time.ZonedDateTime.now());
        } catch (JsonMappingException e) {
            verifyException(e, "Class com.fasterxml.jackson.databind.ser.BeanPropertyWriter");
            verifyException(e, "with modifiers \"public\"");
        }
    }

    private void _testLenientSerialization(Object value) throws Exception
    {
        String json = LENIENT_MAPPER.writeValueAsString(value);
        assertThat(json).isNotNull();
    }

    @Test
    public void testAllowDeserializationWithFeature() throws Exception
    {
        _testAllowDeserializationLenient(java.time.LocalDateTime.class);
        _testAllowDeserializationLenient(java.time.LocalDate.class);
        _testAllowDeserializationLenient(java.time.LocalTime.class);
        _testAllowDeserializationLenient(java.time.OffsetDateTime.class);
        _testAllowDeserializationLenient(java.time.ZonedDateTime.class);
    }

    private void _testAllowDeserializationLenient(Class<?> target) throws Exception {
        JacksonException e = assertThrows(JacksonException.class, () ->
                LENIENT_MAPPER.readValue("{}", target));
        Assertions.assertThat(e).hasMessageContaining("Cannot construct instance of `"+target.getName()+"`");
    }
}