package net.kencochrane.raven.connection;

import mockit.Expectations;
import mockit.Injectable;
import mockit.NonStrictExpectations;
import mockit.Verifications;
import net.kencochrane.raven.Raven;
import net.kencochrane.raven.event.Event;
import net.kencochrane.raven.marshaller.Marshaller;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.util.UUID;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class HttpConnectionTest {
    private final String publicKey = UUID.randomUUID().toString();
    private final String secretKey = UUID.randomUUID().toString();
    private HttpConnection httpConnection;
    @Injectable
    private HttpsURLConnection mockUrlConnection;
    @Injectable
    private Marshaller mockMarshaller;
    @Injectable
    private URL mockUrl;
    @Injectable
    private OutputStream mockOutputStream;
    @Injectable
    private InputStream mockInputStream;

    @BeforeMethod
    public void setUp() throws Exception {
        new NonStrictExpectations() {{
            mockUrl.openConnection();
            result = mockUrlConnection;
            mockUrlConnection.getOutputStream();
            result = mockOutputStream;
            mockUrlConnection.getInputStream();
            result = mockInputStream;

            httpConnection = new HttpConnection(mockUrl, publicKey, secretKey);
            httpConnection.setMarshaller(mockMarshaller);
        }};
    }

    @Test
    public void testTimeout(@Injectable final Event mockEvent) throws Exception {
        final int timeout = 12;
        httpConnection.setTimeout(timeout);

        httpConnection.send(mockEvent);

        new Verifications() {{
            mockUrlConnection.setConnectTimeout(timeout);
        }};
    }

    @Test
    public void testByPassSecurityDefaultsToFalse(@Injectable final Event mockEvent) throws Exception {
        httpConnection.send(mockEvent);

        new Verifications() {{
            mockUrlConnection.setHostnameVerifier((HostnameVerifier) any);
            times = 0;
        }};
    }

    @Test
    public void testByPassSecurity(@Injectable final Event mockEvent,
                                   @Injectable("fakehostna.me") final String mockHostname,
                                   @Injectable final SSLSession mockSslSession) throws Exception {
        httpConnection.setBypassSecurity(true);

        httpConnection.send(mockEvent);

        new Verifications() {{
            HostnameVerifier hostnameVerifier;
            mockUrlConnection.setHostnameVerifier(hostnameVerifier = withCapture());
            assertThat(hostnameVerifier.verify(mockHostname, mockSslSession), is(true));
        }};
    }

    @Test
    public void testDontByPassSecurity(@Injectable final Event mockEvent) throws Exception {
        httpConnection.setBypassSecurity(false);

        httpConnection.send(mockEvent);

        new Verifications() {{
            mockUrlConnection.setHostnameVerifier((HostnameVerifier) any);
            times = 0;
        }};
    }

    @Test
    public void testContentMarshalled(@Injectable final Event mockEvent) throws Exception {
        httpConnection.send(mockEvent);

        new Verifications() {{
            mockMarshaller.marshall(mockEvent, (OutputStream) any);
        }};
    }

    @Test
    public void testAuthHeaderSent(@Injectable final Event mockEvent) throws Exception {
        httpConnection.send(mockEvent);


        new Verifications() {{
            mockUrlConnection.setRequestProperty("User-Agent", Raven.NAME);

            String expectedAuthRequest = "Sentry sentry_version=5,"
                    + "sentry_client=" + Raven.NAME + ","
                    + "sentry_key=" + publicKey + ","
                    + "sentry_secret=" + secretKey;
            mockUrlConnection.setRequestProperty("X-Sentry-Auth", expectedAuthRequest);
        }};
    }

    @Test(expectedExceptions = {ConnectionException.class})
    public void testHttpErrorThrowsAnException(@Injectable final Event mockEvent) throws Exception {
        final String httpErrorMessage = UUID.randomUUID().toString();
        new NonStrictExpectations() {{
            mockUrlConnection.getOutputStream();
            result = new IOException();
            mockUrlConnection.getErrorStream();
            result = new ByteArrayInputStream(httpErrorMessage.getBytes());
        }};

        httpConnection.doSend(mockEvent);
    }

    @Test
    public void testApiUrlCreation(@Injectable final URI sentryUri) throws Exception {
        final String uri = "http://host/sentry/";
        final String projectId = UUID.randomUUID().toString();
        new Expectations() {{
            sentryUri.toString();
            result = "http://host/sentry/";
        }};

        URL sentryApiUrl = HttpConnection.getSentryApiUrl(sentryUri, projectId);

        assertThat(sentryApiUrl.toString(), is(uri + "api/" + projectId + "/store/"));
    }
}

@@ -647,31 +647,8 @@
 URL;
-%0Aimport java.util.UUID;
 %0A%0Aim
@@ -773,16 +773,32 @@
 nTest %7B%0A
+    @Injectable%0A
     priv
@@ -830,103 +830,139 @@
 y = 
-UUID.randomUUID().toString();%0A    private final String secretKey = UUID.randomUUID().toString()
+%226cc48e8f-380c-44cc-986b-f566247a2af5%22;%0A    @Injectable%0A    private final String secretKey = %22e30cca23-3f97-470b-a8c2-e29b33dd25e0%22
 ;%0A  
@@ -4439,36 +4439,46 @@
 e = 
-UUID.randomUUID().toString()
+%2293e3ddb1-c4f3-46c3-9900-529de83678b7%22
 ;%0A  
@@ -4973,36 +4973,46 @@
 d = 
-UUID.randomUUID().toString()
+%22293b4958-71f8-40a9-b588-96f004f64463%22
 ;%0A  

Remove need for UUID in unit test