package com.yammer.dropwizard.jersey;

import com.sun.jersey.core.util.MultivaluedMapImpl;
import com.sun.jersey.core.util.StringKeyObjectValueIgnoreCaseMultivaluedMap;
import com.yammer.dropwizard.json.Json;
import org.codehaus.jackson.annotate.JsonProperty;
import org.junit.Test;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.annotation.Annotation;

import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;

@SuppressWarnings("unchecked")
public class JacksonMessageBodyProviderTest {
    private static final Annotation[] NONE = new Annotation[0];

    public static class Example {
        @Min(0)
        @JsonProperty
        int id;
    }

    private final Json json = spy(new Json());

    private final JacksonMessageBodyProvider provider = new JacksonMessageBodyProvider(json);

    @Test
    public void readsDeserializableTypes() throws Exception {
        assertThat(provider.isReadable(String.class, null, null, null),
                   is(true));

        verify(json).canDeserialize(String.class);
    }

    @Test
    public void writesSerializableTypes() throws Exception {
        assertThat(provider.isWriteable(String.class, null, null, null),
                   is(true));

        verify(json).canSerialize(String.class);
    }

    @Test
    public void isChunked() throws Exception {
        assertThat(provider.getSize(null, null, null, null, null),
                   is(-1L));
    }

    @Test
    public void deserializesRequestEntities() throws Exception {
        final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":1}".getBytes());
        final Class<?> klass = Example.class;

        final Object obj = provider.readFrom((Class<Object>) klass,
                                             Example.class,
                                             NONE,
                                             MediaType.APPLICATION_JSON_TYPE,
                                             new MultivaluedMapImpl(),
                                             entity);

        assertThat(obj,
                   is(instanceOf(Example.class)));

        assertThat(((Example) obj).id,
                   is(1));
    }

    @Test
    public void returnsValidatedRequestEntities() throws Exception {
        final Annotation valid = mock(Annotation.class);
        doReturn(Valid.class).when(valid).annotationType();

        final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":1}".getBytes());
        final Class<?> klass = Example.class;

        final Object obj = provider.readFrom((Class<Object>) klass,
                                             Example.class,
                                             new Annotation[] { valid },
                                             MediaType.APPLICATION_JSON_TYPE,
                                             new MultivaluedMapImpl(),
                                             entity);

        assertThat(obj,
                   is(instanceOf(Example.class)));

        assertThat(((Example) obj).id,
                   is(1));
    }

    @Test
    public void returnsA422ForInvalidRequestEntities() throws Exception {
        final Annotation valid = mock(Annotation.class);
        doReturn(Valid.class).when(valid).annotationType();

        final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":-1}".getBytes());

        try {
            final Class<?> klass = Example.class;
            provider.readFrom((Class<Object>) klass,
                              Example.class,
                              new Annotation[]{ valid },
                              MediaType.APPLICATION_JSON_TYPE,
                              new MultivaluedMapImpl(),
                              entity);
            fail("should have thrown a WebApplicationException but didn't");
        } catch (WebApplicationException e) {
            assertThat(e.getResponse().getStatus(),
                       is(422));

            assertThat((String) e.getResponse().getEntity(),
                       is("The request entity had the following errors:\n" +
                                  "  * id must be greater than or equal to 0 (was -1)\n"));
        }
    }

    @Test
    public void returnsA400ForMalformedRequestEntities() throws Exception {
        final ByteArrayInputStream entity = new ByteArrayInputStream("{\"id\":-1d".getBytes());

        try {
            final Class<?> klass = Example.class;
            provider.readFrom((Class<Object>) klass,
                              Example.class,
                              NONE,
                              MediaType.APPLICATION_JSON_TYPE,
                              new MultivaluedMapImpl(),
                              entity);
            fail("should have thrown a WebApplicationException but didn't");
        } catch (WebApplicationException e) {
            assertThat(e.getResponse().getStatus(),
                       is(400));
        }
    }

    @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 StringKeyObjectValueIgnoreCaseMultivaluedMap(),
                         output);

        assertThat(output.toString(),
                   is("{\"id\":500}"));
    }
}

@@ -32,16 +32,64 @@
 ersey;%0A%0A
+import com.google.common.collect.ImmutableList;%0A
 import c
@@ -132,16 +132,16 @@
 apImpl;%0A
-
 import c
@@ -246,24 +246,141 @@
 .json.Json;%0A
+import com.yammer.dropwizard.validation.InvalidEntityException;%0Aimport org.codehaus.jackson.JsonProcessingException;%0A
 import org.c
@@ -518,52 +518,8 @@
 in;%0A
-import javax.ws.rs.WebApplicationException;%0A
 impo
@@ -713,16 +713,16 @@
 anceOf;%0A
-
 import s
@@ -753,16 +753,64 @@
 ers.is;%0A
+import static org.hamcrest.Matchers.startsWith;%0A
 import s
@@ -3595,19 +3595,38 @@
 oid 
-returnsA422
+throwsAnInvalidEntityException
 ForI
@@ -4350,38 +4350,37 @@
    %7D catch (
-WebApplication
+InvalidEntity
 Exception e)
@@ -4414,243 +4414,62 @@
 .get
-Response().getStatus(),%0A                       is(422));%0A%0A            assertThat((String) e.getResponse().getEntity(),%0A                       is(%22The request entity had the following errors:%5Cn%22 +%0A                                  %22  * 
+Errors(),%0A                       is(ImmutableList.of(%22
 id m
@@ -4514,11 +4514,10 @@
  -1)
-%5Cn
 %22
+)
 ));%0A
@@ -4563,19 +4563,38 @@
 oid 
-returnsA400
+throwsAJsonProcessingException
 ForM
@@ -5185,30 +5185,30 @@
  catch (
-WebApplication
+JsonProcessing
 Exceptio
@@ -5246,28 +5246,15 @@
 .get
-Response().getStatus
+Message
 (),%0A
@@ -5280,14 +5280,108 @@
     
-is(400
+startsWith(%22Unexpected character ('d' (code 100)): was expecting comma to separate OBJECT entries%5Cn%22
 ));%0A

Fix JacksonMessageBodyProviderTest.