ObjectIdDeserializationFailTest.java

package com.fasterxml.jackson.databind.tofix;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.objectid.Employee;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
import com.fasterxml.jackson.databind.testutil.failure.JacksonTestFailureExpected;
import com.fasterxml.jackson.databind.tofix.ObjectIdDeserializationFailTest.EnumMapCompany.FooEnum;

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

/**
 * Unit test to verify handling of Object Id deserialization.
 * <p>
 * NOTE: only tests that still fail, even with initial forward-ref-handling
 * code (2.4), are included here. Other cases moved to successfully
 * passing tests.
 */
class ObjectIdDeserializationFailTest extends DatabindTestUtil {
    static class ArrayCompany {
        public Employee[] employees;
    }

    static class ArrayBlockingQueueCompany {
        public ArrayBlockingQueue<Employee> employees;
    }

    static class EnumMapCompany {
        public EnumMap<FooEnum, Employee> employees;

        static enum FooEnum {
            A, B
        }
    }

    static class DefensiveCompany {
        public List<DefensiveEmployee> employees;

        static class DefensiveEmployee extends Employee {

            public void setReports(List<DefensiveEmployee> reports) {
                this.reports = new ArrayList<Employee>(reports);
            }
        }
    }

    private final ObjectMapper mapper = new ObjectMapper();

    @JacksonTestFailureExpected
    @Test
    void forwardReferenceInArray() throws Exception {
        String json = "{\"employees\":["
                + "{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
                + "2,"
                + "{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
                + "]}";
        ArrayCompany company = mapper.readValue(json, ArrayCompany.class);
        assertEquals(3, company.employees.length);
        Employee firstEmployee = company.employees[0];
        Employee secondEmployee = company.employees[1];
        assertEmployees(firstEmployee, secondEmployee);
    }

    // Do a specific test for ArrayBlockingQueue since it has its own deser.
    @Test
    void forwardReferenceInQueue() throws Exception {
        String json = "{\"employees\":["
                + "{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
                + "2,"
                + "{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
                + "]}";
        ArrayBlockingQueueCompany company = mapper.readValue(json, ArrayBlockingQueueCompany.class);
        assertEquals(3, company.employees.size());
        Employee firstEmployee = company.employees.take();
        Employee secondEmployee = company.employees.take();
        assertEmployees(firstEmployee, secondEmployee);
    }

    @JacksonTestFailureExpected
    @Test
    void forwardReferenceInEnumMap()
            throws Exception {
        String json = "{\"employees\":{"
                + "\"A\":{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
                + "\"B\": 2,"
                + "\"C\":{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
                + "}}";
        EnumMapCompany company = mapper.readValue(json, EnumMapCompany.class);
        assertEquals(3, company.employees.size());
        Employee firstEmployee = company.employees.get(FooEnum.A);
        Employee secondEmployee = company.employees.get(FooEnum.B);
        assertEmployees(firstEmployee, secondEmployee);
    }

    @JacksonTestFailureExpected
    @Test
    void forwardReferenceWithDefensiveCopy()
            throws Exception {
        String json = "{\"employees\":[" + "{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
                + "{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}" + "]}";
        DefensiveCompany company = mapper.readValue(json, DefensiveCompany.class);
        assertEquals(2, company.employees.size());
        Employee firstEmployee = company.employees.get(0);
        Employee secondEmployee = company.employees.get(1);
        assertEmployees(firstEmployee, secondEmployee);
    }

    private void assertEmployees(Employee firstEmployee, Employee secondEmployee) {
        assertEquals(1, firstEmployee.id);
        assertEquals(2, secondEmployee.id);
        assertEquals(1, firstEmployee.reports.size());
        assertSame(secondEmployee, firstEmployee.reports.get(0)); // Ensure that forward reference was properly resolved and in order.
        assertSame(firstEmployee, secondEmployee.manager); // And that back reference is also properly resolved.
    }
}