CustomCollectionMerge4783Test.java

package com.fasterxml.jackson.databind.deser.merge;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonMerge;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

// [databind#4783] Test to verify that JsonMerge also works for custom list
@SuppressWarnings("serial")
public class CustomCollectionMerge4783Test
    extends DatabindTestUtil
{
    static class MyArrayListJDK<T> extends ArrayList<T> { }

    static class MergeListJDK {
        @JsonMerge
        @JsonProperty
        public List<String> values = new MyArrayListJDK<>();
        { values.add("a");}
    }

    interface MyListCustom<T> extends List<T> { }

    static class MyArrayListCustom<T> extends ArrayList<T> implements MyListCustom<T> { }

    static abstract class MyAbstractStringList extends ArrayList<String> {
        MyAbstractStringList() { super(); }
        MyAbstractStringList(int i) { super(); }
    }

    static class MergeCustomStringList {
        @JsonMerge
        @JsonProperty
        public MyListCustom<String> values = new MyArrayListCustom<>();
        { values.add("a"); }
    }

    static class MergeMyCustomLongList {
        @JsonMerge
        @JsonProperty
        public MyListCustom<Long> values = new MyArrayListCustom<>();
        { values.add(1L); }
    }

    static class MergeMyCustomPojoList {
        @JsonMerge
        @JsonProperty
        public MyListCustom<CustomPojo> values = new MyArrayListCustom<>();
        {
            values.add(CustomPojo.create("a", 1));
            values.add(CustomPojo.create("b", 2));
        }
    }

    // And then non-merging case too
    static class NonMergeCustomStringList {
        public MyListCustom<String> values;
    }

    public static class CustomPojo {
        public String name;
        public int age;

        public static CustomPojo create(String name, int age) {
            CustomPojo pojo = new CustomPojo();
            pojo.name = name;
            pojo.age = age;
            return pojo;
        }
    }

    private final ObjectMapper MAPPER = newJsonMapper();

    @Test
    void testJDKMapperReading() throws Exception {
        MergeListJDK result = MAPPER.readValue("{\"values\":[\"x\"]}", MergeListJDK.class);

        assertEquals(2, result.values.size());
        assertTrue(result.values.contains("x"));
        assertTrue(result.values.contains("a"));
    }

    @Test
    void testCustomMapperReading() throws Exception {
        MergeCustomStringList result = MAPPER.readValue("{\"values\":[\"x\"]}",
                MergeCustomStringList.class);

        assertEquals(2, result.values.size());
        assertTrue(result.values.contains("x"));
        assertTrue(result.values.contains("a"));
    }

    @Test
    void testCustomMapperReadingLongArrayList() throws Exception {
        MergeMyCustomLongList result = MAPPER.readValue("{\"values\":[7]}",
                MergeMyCustomLongList.class);

        assertEquals(2, result.values.size());
        assertTrue(result.values.contains(1L));
        assertTrue(result.values.contains(7L));
    }

    @Test
    void testCustomMapperReadingPojoArrayList() throws Exception {
        MergeMyCustomPojoList result = MAPPER.readValue("{\"values\":[{\"name\":\"c\",\"age\":3}]}",
                MergeMyCustomPojoList.class);

        assertEquals(3, result.values.size());
    }

    // // // And then failure cases

    // Fail can't construct Collection interface unless there's maaping
    @Test
    void failNonMergeInterfaceList() throws Exception {
        try {
            MAPPER.readValue("{\"values\":[\"x\"]}", NonMergeCustomStringList.class);
            fail("Should not pass");
        } catch (InvalidDefinitionException e) {
            verifyException(e, String.format(
                    "Cannot construct instance of `%s` (no Creators",
                    MyListCustom.class.getName()));
        }
    }

    // Fail can't construct abstract types
    @Test
    void failNonMergeAbstractList() throws Exception {
        try {
            MAPPER.readValue("[]", MyAbstractStringList.class);
            fail("Should not pass");
        } catch (InvalidDefinitionException e) {
            verifyException(e, String.format(
                    "Cannot construct instance of `%s` (no Creators",
                    MyAbstractStringList.class.getName()));
        }
    }
}