package io.dropwizard.jersey.jackson; import com.fasterxml.jackson.annotation.JsonIgnoreType; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.reflect.TypeToken; import io.dropwizard.jackson.Jackson; import io.dropwizard.validation.Validated; import org.hibernate.validator.constraints.NotEmpty; import org.junit.Before; import org.junit.Test; import javax.validation.Valid; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedHashMap; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assume.assumeThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @SuppressWarnings({"serial", "unchecked"}) public class JacksonMessageBodyProviderTest { private static final Annotation[] NONE = new Annotation[0]; public static class Example { @Min(0) @JsonProperty public int id; @Override public int hashCode() { return Objects.hash(id); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final Example other = (Example) obj; return Objects.equals(this.id, other.id); } } public static class ListExample { @NotEmpty @Valid @JsonProperty public List<Example> examples; } public interface Partial1 { } public interface Partial2 { } public static class PartialExample { @Min(value = 0, groups = Partial1.class) @JsonProperty public int id; @NotNull(groups = Partial2.class) @JsonProperty public String text; } @JsonIgnoreType public static interface Ignorable { } @JsonIgnoreType(false) public static interface NonIgnorable extends Ignorable { } private final ObjectMapper mapper = spy(Jackson.newObjectMapper()); private final JacksonMessageBodyProvider provider = new JacksonMessageBodyProvider(mapper); @Before public void setUp() throws Exception { assumeThat(Locale.getDefault().getLanguage(), is("en")); } @Test public void readsDeserializableTypes() throws Exception { assertThat(provider.isReadable(Example.class, null, null, null)) .isTrue(); } @Test public void writesSerializableTypes() throws Exception { assertThat(provider.isWriteable(Example.class, null, null, null)) .isTrue(); } @Test public void doesNotWriteIgnoredTypes() throws Exception { assertThat(provider.isWriteable(Ignorable.class, null, null, null)) .isFalse(); } @Test public void writesUnIgnoredTypes() throws Exception { assertThat(provider.isWriteable(NonIgnorable.class, null, null, null)) .isTrue(); } @Test public void doesNotReadIgnoredTypes() throws Exception { assertThat(provider.isReadable(Ignorable.class, null, null, null)) .isFalse(); } @Test public void readsUnIgnoredTypes() throws Exception { assertThat(provider.isReadable(NonIgnorable.class, null, null, null)) .isTrue(); } @Test public void isChunked() throws Exception { assertThat(provider.getSize(null, null, null, null, null)) .isEqualTo(-1); } @Test public void deserializesRequestEntities() throws Exception { final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":1}".getBytes(StandardCharsets.UTF_8)); final Class<?> klass = Example.class; final Object obj = provider.readFrom((Class<Object>) klass, Example.class, NONE, MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<>(), entity); assertThat(obj) .isInstanceOf(Example.class); assertThat(((Example) obj).id) .isEqualTo(1); } @Test public void returnsPartialValidatedRequestEntities() throws Exception { final Validated valid = mock(Validated.class); doReturn(Validated.class).when(valid).annotationType(); when(valid.value()).thenReturn(new Class<?>[]{Partial1.class, Partial2.class}); final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":1,\"text\":\"hello Cemo\"}".getBytes(StandardCharsets.UTF_8)); final Class<?> klass = PartialExample.class; final Object obj = provider.readFrom((Class<Object>) klass, PartialExample.class, new Annotation[]{valid}, MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<>(), entity); assertThat(obj) .isInstanceOf(PartialExample.class); assertThat(((PartialExample) obj).id) .isEqualTo(1); } @Test public void returnsPartialValidatedByGroupRequestEntities() throws Exception { final Validated valid = mock(Validated.class); doReturn(Validated.class).when(valid).annotationType(); when(valid.value()).thenReturn(new Class<?>[]{Partial1.class}); final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":1}".getBytes(StandardCharsets.UTF_8)); final Class<?> klass = PartialExample.class; final Object obj = provider.readFrom((Class<Object>) klass, PartialExample.class, new Annotation[]{valid}, MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<>(), entity); assertThat(obj) .isInstanceOf(PartialExample.class); assertThat(((PartialExample) obj).id) .isEqualTo(1); } @Test public void throwsAJsonProcessingExceptionForMalformedRequestEntities() throws Exception { final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":-1d".getBytes(StandardCharsets.UTF_8)); try { final Class<?> klass = Example.class; provider.readFrom((Class<Object>) klass, Example.class, NONE, MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<>(), entity); failBecauseExceptionWasNotThrown(WebApplicationException.class); } catch (JsonProcessingException e) { assertThat(e.getMessage()) .startsWith("Unexpected character ('d' (code 100)): " + "was expecting comma to separate Object entries\n"); } } @Test public void serializesResponseEntities() throws Exception { final ByteArrayOutputStream output = new ByteArrayOutputStream(); final Example example = new Example(); example.id = 500; provider.writeTo(example, Example.class, Example.class, NONE, MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<>(), output); assertThat(output.toString()) .isEqualTo("{\"id\":500}"); } @Test public void returnsValidatedCollectionRequestEntities() throws Exception { testValidatedCollectionType(Collection.class, new TypeToken<Collection<Example>>() { }.getType()); } @Test public void returnsValidatedSetRequestEntities() throws Exception { testValidatedCollectionType(Set.class, new TypeToken<Set<Example>>() { }.getType()); } @Test public void returnsValidatedListRequestEntities() throws Exception { testValidatedCollectionType(List.class, new TypeToken<List<Example>>() { }.getType()); } private void testValidatedCollectionType(Class<?> klass, Type type) throws IOException { final Annotation valid = mock(Annotation.class); doReturn(Valid.class).when(valid).annotationType(); final ByteArrayInputStream entity = new ByteArrayInputStream("[{\"id\":1}, {\"id\":2}]".getBytes(StandardCharsets.UTF_8)); final Object obj = provider.readFrom((Class<Object>) klass, type, new Annotation[]{valid}, MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<>(), entity); assertThat(obj) .isInstanceOf(klass); Iterator<Example> iterator = ((Iterable<Example>) obj).iterator(); assertThat(iterator.next().id).isEqualTo(1); assertThat(iterator.next().id).isEqualTo(2); } }