UnwrappedPerPropIgnoreProperties5985Test.java

package tools.jackson.databind.struct;

import java.util.LinkedHashMap;
import java.util.Map;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.annotation.JsonUnwrapped;

import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.testutil.DatabindTestUtil;

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

// [databind#5985]: Per-property `@JsonIgnoreProperties` /
// `@JsonIncludeProperties` placed directly on a `@JsonUnwrapped` field should
// filter the unwrapped inner POJO's properties ��� same as on a regular
// (non-unwrapped) POJO-valued property.
//
// Investigation showed this already works in 3.x without dedicated code:
// the inner deserializer is contextualized via the property's accessor in
// BeanDeserializerBase.createContextual(), which routes through
// _handleByNameInclusion() and produces a filtered inner deserializer; the
// unwrapping clone (BeanDeserializerBase ctor near line 337) preserves
// `_ignorableProps` / `_includableProps`. These tests pin that behavior so
// future refactors don't silently regress it.
public class UnwrappedPerPropIgnoreProperties5985Test extends DatabindTestUtil
{
    static class Inner {
        public String admin;
        public String name;
    }

    static class InnerWithAnySetter {
        public Map<String, Object> extra = new LinkedHashMap<>();
        @JsonAnySetter
        public void set(String k, Object v) { extra.put(k, v); }
    }

    static class OuterIgnore {
        @JsonUnwrapped
        @JsonIgnoreProperties({"admin"})
        public Inner inner = new Inner();
    }

    static class OuterInclude {
        @JsonUnwrapped
        @JsonIncludeProperties({"name"})
        public Inner inner = new Inner();
    }

    static class OuterIgnoreAnySetter {
        @JsonUnwrapped
        @JsonIgnoreProperties({"secret"})
        public InnerWithAnySetter inner = new InnerWithAnySetter();
    }

    private final ObjectMapper MAPPER = newJsonMapper();

    // (1) Per-property @JsonIgnoreProperties on @JsonUnwrapped ��� deserialization.
    @Test
    public void ignorePropertiesPerPropOnUnwrapped_deser() throws Exception
    {
        OuterIgnore r = MAPPER.readValue(
                a2q("{'admin':'INJECTED','name':'alice'}"), OuterIgnore.class);
        assertEquals("alice", r.inner.name);
        assertNull(r.inner.admin,
                "@JsonIgnoreProperties({'admin'}) on @JsonUnwrapped field"
                + " did not filter inner; admin=" + r.inner.admin);
    }

    // (2) Per-property @JsonIncludeProperties on @JsonUnwrapped ��� deserialization.
    @Test
    public void includePropertiesPerPropOnUnwrapped_deser() throws Exception
    {
        OuterInclude r = MAPPER.readValue(
                a2q("{'admin':'INJECTED','name':'alice'}"), OuterInclude.class);
        assertEquals("alice", r.inner.name);
        assertNull(r.inner.admin,
                "@JsonIncludeProperties({'name'}) on @JsonUnwrapped field"
                + " did not restrict inner; admin=" + r.inner.admin);
    }

    // (3) Per-property @JsonIgnoreProperties must also block the inner
    //     @JsonAnySetter fallback for a forbidden name ��� deserialization.
    @Test
    public void ignorePerPropBlocksInnerAnySetter_deser() throws Exception
    {
        OuterIgnoreAnySetter r = MAPPER.readValue(
                a2q("{'secret':'X','ok':'Y'}"), OuterIgnoreAnySetter.class);
        assertNotNull(r.inner.extra);
        assertFalse(r.inner.extra.containsKey("secret"),
                "@JsonIgnoreProperties({'secret'}) bypassed via inner @JsonAnySetter; extra="
                + r.inner.extra);
        assertEquals("Y", r.inner.extra.get("ok"));
    }

    // (4) Per-property @JsonIgnoreProperties on @JsonUnwrapped ��� serialization.
    @Test
    public void ignorePropertiesPerPropOnUnwrapped_ser() throws Exception
    {
        OuterIgnore o = new OuterIgnore();
        o.inner.admin = "secret";
        o.inner.name = "alice";
        String json = MAPPER.writeValueAsString(o);
        assertEquals(a2q("{'name':'alice'}"), json,
                "@JsonIgnoreProperties({'admin'}) on @JsonUnwrapped field"
                + " did not drop 'admin' from output: " + json);
    }

    // (5) Per-property @JsonIncludeProperties on @JsonUnwrapped ��� serialization.
    @Test
    public void includePropertiesPerPropOnUnwrapped_ser() throws Exception
    {
        OuterInclude o = new OuterInclude();
        o.inner.admin = "secret";
        o.inner.name = "alice";
        String json = MAPPER.writeValueAsString(o);
        assertEquals(a2q("{'name':'alice'}"), json,
                "@JsonIncludeProperties({'name'}) on @JsonUnwrapped field"
                + " did not restrict output: " + json);
    }
}