package org.mockserver.serialization;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.apache.commons.io.IOUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockserver.logging.MockServerLogger;
import org.mockserver.model.Cookies;
import org.mockserver.model.Delay;
import org.mockserver.model.Headers;
import org.mockserver.model.HttpResponse;
import org.mockserver.serialization.model.*;

import java.io.IOException;
import java.util.Arrays;

import static java.util.concurrent.TimeUnit.SECONDS;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockserver.character.Character.NEW_LINE;
import static org.mockserver.file.FileReader.openStreamToFileFromClassPathOrPath;
import static org.mockserver.model.BinaryBody.binary;
import static org.mockserver.model.Cookie.cookie;
import static org.mockserver.model.Delay.minutes;
import static org.mockserver.model.Delay.seconds;
import static org.mockserver.model.Header.header;
import static org.mockserver.model.JsonBody.json;
import static org.mockserver.model.StringBody.exact;
import static org.mockserver.model.XmlBody.xml;
import static org.mockserver.validator.jsonschema.JsonSchemaValidator.OPEN_API_SPECIFICATION_URL;

/**
 * @author jamesdbloom
 */
@SuppressWarnings("deprecation")
public class HttpResponseSerializerIntegrationTest {

    private static final ObjectWriter OBJECT_WRITER = ObjectMapperFactory.createObjectMapper(true, false);
    @Rule
    public final ExpectedException thrown = ExpectedException.none();

    @Test
    public void shouldIgnoreExtraFields() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "    \"statusCode\": 123," + NEW_LINE +
            "    \"extra_field\": \"extra_value\"" + NEW_LINE +
            "}";

        // then
        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage("incorrect response json format for:" + NEW_LINE +
            "" + NEW_LINE +
            "  {" + NEW_LINE +
            "      \"statusCode\": 123," + NEW_LINE +
            "      \"extra_field\": \"extra_value\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "" + NEW_LINE +
            " schema validation errors:" + NEW_LINE +
            "" + NEW_LINE +
            "  1 error:" + NEW_LINE +
            "   - $.extra_field: is not defined in the schema and the schema does not allow additional properties" + NEW_LINE +
            "  " + NEW_LINE +
            "  " + OPEN_API_SPECIFICATION_URL.replaceAll(NEW_LINE, NEW_LINE + "  " ));

        // when
        new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);
    }

    @Test
    public void shouldDeserializeCompleteObject() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"statusCode\" : 123," + NEW_LINE +
            "  \"headers\" : {" + NEW_LINE +
            "    \"someHeaderName\" : [ \"someHeaderValue\" ]" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"cookies\" : {" + NEW_LINE +
            "    \"someCookieName\" : \"someCookieValue\"" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"body\" : \"somebody\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"SECONDS\"," + NEW_LINE +
            "    \"value\" : 5" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"connectionOptions\" : {" + NEW_LINE +
            "    \"suppressContentLengthHeader\" : true," + NEW_LINE +
            "    \"contentLengthHeaderOverride\" : 100," + NEW_LINE +
            "    \"suppressConnectionHeader\" : true," + NEW_LINE +
            "    \"chunkSize\" : 100," + NEW_LINE +
            "    \"keepAliveOverride\" : true," + NEW_LINE +
            "    \"closeSocket\" : true," + NEW_LINE +
            "    \"closeSocketDelay\" : {" + NEW_LINE +
            "      \"timeUnit\" : \"SECONDS\"," + NEW_LINE +
            "      \"value\" : 10" + NEW_LINE +
            "    }" + NEW_LINE +
            "  }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertEquals(new HttpResponseDTO()
            .setStatusCode(123)
            .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody")))
            .setHeaders(new Headers().withEntries(
                header("someHeaderName", "someHeaderValue")
            ))
            .setCookies(new Cookies().withEntries(
                cookie("someCookieName", "someCookieValue")
            ))
            .setDelay(new DelayDTO(seconds(5)))
            .setConnectionOptions(
                new ConnectionOptionsDTO()
                    .setContentLengthHeaderOverride(100)
                    .setSuppressConnectionHeader(true)
                    .setChunkSize(100)
                    .setCloseSocket(true)
                    .setCloseSocketDelay(new DelayDTO(new Delay(SECONDS, 10)))
                    .setKeepAliveOverride(true)
                    .setSuppressContentLengthHeader(true)
            )
            .buildObject(), httpResponse);
    }

    @Test
    public void shouldDeserializeStringBodyShorthand() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"body\" : \"somebody\"" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertEquals(new HttpResponseDTO()
            .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody")))
            .buildObject(), httpResponse);
    }

    @Test
    public void shouldDeserializeStringBodyWithType() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"STRING\"," + NEW_LINE +
            "    \"string\" : \"somebody\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertEquals(new HttpResponseDTO()
            .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody")))
            .buildObject(), httpResponse);
    }

    @Test
    public void shouldDeserializeJsonBody() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"JSON\"," + NEW_LINE +
            "    \"json\" : \"{ \\\"key\\\": \\\"value\\\" }\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        HttpResponse expected = new HttpResponseDTO()
            .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(json("{ \"key\": \"value\" }")))
            .buildObject();
        assertEquals(expected, httpResponse);
    }

    @Test
    public void shouldDeserializeBinaryBody() throws IOException {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"BINARY\"," + NEW_LINE +
            "    \"base64Bytes\" : \" \"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertEquals(new HttpResponseDTO()
            .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(binary(IOUtils.toByteArray(openStreamToFileFromClassPathOrPath("org/mockserver/serialization/forkme_right_red.png")))))
            .buildObject(), httpResponse);
    }

    @Test
    public void shouldDeserializePartialObject() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "    \"statusCode\": 123" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertEquals(new HttpResponseDTO()
            .setStatusCode(123)
            .buildObject(), httpResponse);
    }

    @Test
    public void shouldDeserializeAsHttpResponseField() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "    \"httpResponse\": {" + NEW_LINE +
            "        \"statusCode\": 123," + NEW_LINE +
            "        \"body\" : {" + NEW_LINE +
            "          \"type\" : \"STRING\"," + NEW_LINE +
            "          \"string\" : \"somebody\"" + NEW_LINE +
            "        }" + NEW_LINE +
            "    }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertEquals(new HttpResponseDTO()
            .setStatusCode(123)
            .setBody(new StringBodyDTO(exact("somebody")))
            .buildObject(), httpResponse);
    }

    @Test
    public void shouldSerializeCompleteObject() {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(
            new HttpResponseDTO()
                .setStatusCode(123)
                .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody")))
                .setHeaders(new Headers().withEntries(
                    header("someHeaderName", "someHeaderValue")
                ))
                .setCookies(new Cookies().withEntries(
                    cookie("someCookieName", "someCookieValue")
                ))
                .setDelay(new DelayDTO(minutes(1)))
                .setConnectionOptions(
                    new ConnectionOptionsDTO()
                        .setContentLengthHeaderOverride(100)
                        .setSuppressConnectionHeader(true)
                        .setChunkSize(100)
                        .setCloseSocket(true)
                        .setCloseSocketDelay(new DelayDTO(new Delay(SECONDS, 10)))
                        .setKeepAliveOverride(true)
                        .setSuppressContentLengthHeader(true)
                )
                .buildObject()
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"statusCode\" : 123," + NEW_LINE +
            "  \"headers\" : {" + NEW_LINE +
            "    \"someHeaderName\" : [ \"someHeaderValue\" ]" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"cookies\" : {" + NEW_LINE +
            "    \"someCookieName\" : \"someCookieValue\"" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"body\" : \"somebody\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MINUTES\"," + NEW_LINE +
            "    \"value\" : 1" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"connectionOptions\" : {" + NEW_LINE +
            "    \"chunkSize\" : 100," + NEW_LINE +
            "    \"closeSocket\" : true," + NEW_LINE +
            "    \"closeSocketDelay\" : {" + NEW_LINE +
            "      \"timeUnit\" : \"SECONDS\"," + NEW_LINE +
            "      \"value\" : 10" + NEW_LINE +
            "    }," + NEW_LINE +
            "    \"contentLengthHeaderOverride\" : 100," + NEW_LINE +
            "    \"keepAliveOverride\" : true," + NEW_LINE +
            "    \"suppressConnectionHeader\" : true," + NEW_LINE +
            "    \"suppressContentLengthHeader\" : true" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    @SuppressWarnings("RedundantArrayCreation")
    public void shouldSerializeArray() {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(
            new HttpResponse[]{
                new HttpResponseDTO()
                    .setStatusCode(123)
                    .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody_one")))
                    .setHeaders(new Headers().withEntries(
                        header("someHeaderName", "someHeaderValue")
                    ))
                    .setCookies(new Cookies().withEntries(
                        cookie("someCookieName", "someCookieValue")
                    ))
                    .setDelay(new DelayDTO(minutes(1)))
                    .buildObject(),
                new HttpResponseDTO()
                    .setStatusCode(456)
                    .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody_two")))
                    .setHeaders(new Headers().withEntries(
                        header("someHeaderName", "someHeaderValue")
                    ))
                    .setCookies(new Cookies().withEntries(
                        cookie("someCookieName", "someCookieValue")
                    ))
                    .setDelay(new DelayDTO(minutes(1)))
                    .buildObject()
            }
        );

        // then
        assertEquals("[ {" + NEW_LINE +
            "  \"statusCode\" : 123," + NEW_LINE +
            "  \"headers\" : {" + NEW_LINE +
            "    \"someHeaderName\" : [ \"someHeaderValue\" ]" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"cookies\" : {" + NEW_LINE +
            "    \"someCookieName\" : \"someCookieValue\"" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"body\" : \"somebody_one\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MINUTES\"," + NEW_LINE +
            "    \"value\" : 1" + NEW_LINE +
            "  }" + NEW_LINE +
            "}, {" + NEW_LINE +
            "  \"statusCode\" : 456," + NEW_LINE +
            "  \"headers\" : {" + NEW_LINE +
            "    \"someHeaderName\" : [ \"someHeaderValue\" ]" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"cookies\" : {" + NEW_LINE +
            "    \"someCookieName\" : \"someCookieValue\"" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"body\" : \"somebody_two\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MINUTES\"," + NEW_LINE +
            "    \"value\" : 1" + NEW_LINE +
            "  }" + NEW_LINE +
            "} ]", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeList() {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(
            Arrays.asList(
                new HttpResponseDTO()
                    .setStatusCode(123)
                    .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody_one")))
                    .setHeaders(new Headers().withEntries(
                        header("someHeaderName", "someHeaderValue")
                    ))
                    .setCookies(new Cookies().withEntries(
                        cookie("someCookieName", "someCookieValue")
                    ))
                    .setDelay(new DelayDTO(minutes(1)))
                    .buildObject(),
                new HttpResponseDTO()
                    .setStatusCode(456)
                    .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody_two")))
                    .setHeaders(new Headers().withEntries(
                        header("someHeaderName", "someHeaderValue")
                    ))
                    .setCookies(new Cookies().withEntries(
                        cookie("someCookieName", "someCookieValue")
                    ))
                    .setDelay(new DelayDTO(minutes(1)))
                    .buildObject()
            )
        );

        // then
        assertEquals("[ {" + NEW_LINE +
            "  \"statusCode\" : 123," + NEW_LINE +
            "  \"headers\" : {" + NEW_LINE +
            "    \"someHeaderName\" : [ \"someHeaderValue\" ]" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"cookies\" : {" + NEW_LINE +
            "    \"someCookieName\" : \"someCookieValue\"" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"body\" : \"somebody_one\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MINUTES\"," + NEW_LINE +
            "    \"value\" : 1" + NEW_LINE +
            "  }" + NEW_LINE +
            "}, {" + NEW_LINE +
            "  \"statusCode\" : 456," + NEW_LINE +
            "  \"headers\" : {" + NEW_LINE +
            "    \"someHeaderName\" : [ \"someHeaderValue\" ]" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"cookies\" : {" + NEW_LINE +
            "    \"someCookieName\" : \"someCookieValue\"" + NEW_LINE +
            "  }," + NEW_LINE +
            "  \"body\" : \"somebody_two\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MINUTES\"," + NEW_LINE +
            "    \"value\" : 1" + NEW_LINE +
            "  }" + NEW_LINE +
            "} ]", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeStringBodyViaDTO() {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(
            new HttpResponseDTO()
                .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(exact("somebody")))
                .buildObject()
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : \"somebody\"" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeJsonBodyViaDTO() {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(
            new HttpResponseDTO()
                .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(json("{ \"key\": \"value\" }")))
                .buildObject()
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"key\" : \"value\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeXmlBodyViaDTO() {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(
            new HttpResponseDTO()
                .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(xml("<some><xml></xml></some>")))
                .buildObject()
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"XML\"," + NEW_LINE +
            "    \"xml\" : \"<some><xml></xml></some>\"," + NEW_LINE +
            "    \"rawBytes\" : \"PHNvbWU+PHhtbD48L3htbD48L3NvbWU+\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeBinaryBodyViaDTO() throws IOException {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(
            new HttpResponseDTO()
                .setBody(BodyWithContentTypeDTO.createWithContentTypeDTO(binary(IOUtils.toByteArray(openStreamToFileFromClassPathOrPath("org/mockserver/serialization/forkme_right_red.png")))))
                .buildObject()
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"BINARY\"," + NEW_LINE +
            "    \"base64Bytes\" : \" \"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeStringBodyWithoutDefaultFields() throws JsonProcessingException {
        // when
        String jsonHttpResponse = OBJECT_WRITER.writeValueAsString(
            new HttpResponse()
                .withBody(exact("somebody"))
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : \"somebody\"" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeStringBodyWithDefaultFields() throws JsonProcessingException {
        // when
        String jsonHttpResponse = ObjectMapperFactory.createObjectMapper(true, true).writeValueAsString(
            new HttpResponse()
                .withBody(exact("somebody"))
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"STRING\"," + NEW_LINE +
            "    \"string\" : \"somebody\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeJsonBodyWithoutDefaultFields() throws JsonProcessingException {
        // when
        String jsonHttpResponse = OBJECT_WRITER.writeValueAsString(
            new HttpResponse()
                .withBody(json("{ \"key\": \"value\" }"))
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"key\" : \"value\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeJsonBodyWithDefaultFields() throws JsonProcessingException {
        // when
        String jsonHttpResponse = ObjectMapperFactory.createObjectMapper(true, true).writeValueAsString(
            new HttpResponse()
                .withBody(json("{ \"key\": \"value\" }"))
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"JSON\"," + NEW_LINE +
            "    \"json\" : {" + NEW_LINE +
            "      \"key\" : \"value\"" + NEW_LINE +
            "    }" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeXmlBody() throws JsonProcessingException {
        // when
        String jsonHttpResponse = OBJECT_WRITER.writeValueAsString(
            new HttpResponse()
                .withBody(xml("<some><xml></xml></some>"))
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"XML\"," + NEW_LINE +
            "    \"xml\" : \"<some><xml></xml></some>\"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializeBinaryBody() throws IOException {
        // when
        String jsonHttpResponse = OBJECT_WRITER.writeValueAsString(
            new HttpResponse()
                .withBody(binary(IOUtils.toByteArray(openStreamToFileFromClassPathOrPath("org/mockserver/serialization/forkme_right_red.png"))))
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"body\" : {" + NEW_LINE +
            "    \"type\" : \"BINARY\"," + NEW_LINE +
            "    \"base64Bytes\" : \" \"" + NEW_LINE +
            "  }" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldSerializePartialHttpResponse() {
        // when
        String jsonHttpResponse = new HttpResponseSerializer(new MockServerLogger()).serialize(new HttpResponseDTO()
            .setStatusCode(123)
            .buildObject()
        );

        // then
        assertEquals("{" + NEW_LINE +
            "  \"statusCode\" : 123" + NEW_LINE +
            "}", jsonHttpResponse);
    }

    @Test
    public void shouldDeserializeResponseWithUniformDistributionDelay() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"statusCode\" : 200," + NEW_LINE +
            "  \"body\" : \"test\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MILLISECONDS\"," + NEW_LINE +
            "    \"distribution\" : {" + NEW_LINE +
            "      \"type\" : \"UNIFORM\"," + NEW_LINE +
            "      \"min\" : 100," + NEW_LINE +
            "      \"max\" : 500" + NEW_LINE +
            "    }" + NEW_LINE +
            "  }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertThat(httpResponse.getDelay(), is(notNullValue()));
        assertThat(httpResponse.getDelay().getDistribution(), is(notNullValue()));
        assertThat(httpResponse.getDelay().getDistribution().getType(), is(org.mockserver.model.DelayDistribution.Type.UNIFORM));
        assertThat(httpResponse.getDelay().getDistribution().getMin(), is(100L));
        assertThat(httpResponse.getDelay().getDistribution().getMax(), is(500L));
        assertThat(httpResponse.getDelay().getTimeUnit(), is(java.util.concurrent.TimeUnit.MILLISECONDS));
    }

    @Test
    public void shouldDeserializeResponseWithLogNormalDistributionDelay() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"statusCode\" : 200," + NEW_LINE +
            "  \"body\" : \"test\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MILLISECONDS\"," + NEW_LINE +
            "    \"distribution\" : {" + NEW_LINE +
            "      \"type\" : \"LOG_NORMAL\"," + NEW_LINE +
            "      \"median\" : 200," + NEW_LINE +
            "      \"p99\" : 800" + NEW_LINE +
            "    }" + NEW_LINE +
            "  }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertThat(httpResponse.getDelay(), is(notNullValue()));
        assertThat(httpResponse.getDelay().getDistribution(), is(notNullValue()));
        assertThat(httpResponse.getDelay().getDistribution().getType(), is(org.mockserver.model.DelayDistribution.Type.LOG_NORMAL));
        assertThat(httpResponse.getDelay().getDistribution().getMedian(), is(200L));
        assertThat(httpResponse.getDelay().getDistribution().getP99(), is(800L));
    }

    @Test
    public void shouldDeserializeResponseWithGaussianDistributionDelay() {
        // given
        String requestBytes = "{" + NEW_LINE +
            "  \"statusCode\" : 200," + NEW_LINE +
            "  \"body\" : \"test\"," + NEW_LINE +
            "  \"delay\" : {" + NEW_LINE +
            "    \"timeUnit\" : \"MILLISECONDS\"," + NEW_LINE +
            "    \"distribution\" : {" + NEW_LINE +
            "      \"type\" : \"GAUSSIAN\"," + NEW_LINE +
            "      \"mean\" : 200," + NEW_LINE +
            "      \"stdDev\" : 50" + NEW_LINE +
            "    }" + NEW_LINE +
            "  }" + NEW_LINE +
            "}";

        // when
        HttpResponse httpResponse = new HttpResponseSerializer(new MockServerLogger()).deserialize(requestBytes);

        // then
        assertThat(httpResponse.getDelay(), is(notNullValue()));
        assertThat(httpResponse.getDelay().getDistribution(), is(notNullValue()));
        assertThat(httpResponse.getDelay().getDistribution().getType(), is(org.mockserver.model.DelayDistribution.Type.GAUSSIAN));
        assertThat(httpResponse.getDelay().getDistribution().getMean(), is(200L));
        assertThat(httpResponse.getDelay().getDistribution().getStdDev(), is(50L));
    }

    @Test
    public void shouldRoundTripSerializeDistributionDelay() throws JsonProcessingException {
        // given
        HttpResponse httpResponse = HttpResponse.response()
            .withStatusCode(200)
            .withBody("test")
            .withDelay(Delay.uniform(java.util.concurrent.TimeUnit.MILLISECONDS, 100, 500));

        // when
        String json = new HttpResponseSerializer(new MockServerLogger()).serialize(httpResponse);
        HttpResponse deserialized = new HttpResponseSerializer(new MockServerLogger()).deserialize(json);

        // then
        assertThat(deserialized.getDelay(), is(notNullValue()));
        assertThat(deserialized.getDelay().getDistribution(), is(notNullValue()));
        assertThat(deserialized.getDelay().getDistribution().getType(), is(org.mockserver.model.DelayDistribution.Type.UNIFORM));
        assertThat(deserialized.getDelay().getDistribution().getMin(), is(100L));
        assertThat(deserialized.getDelay().getDistribution().getMax(), is(500L));
        assertThat(deserialized.getDelay().getTimeUnit(), is(java.util.concurrent.TimeUnit.MILLISECONDS));
    }
}
