com.xebialabs.xlt.ci.server.XLTestServerImplTest.java Source code

Java tutorial

Introduction

Here is the source code for com.xebialabs.xlt.ci.server.XLTestServerImplTest.java

Source

package com.xebialabs.xlt.ci.server;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.http.HttpStatus;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;

import com.xebialabs.xlt.ci.TestSpecificationDescribable;
import com.xebialabs.xlt.ci.server.authentication.AuthenticationException;
import com.xebialabs.xlt.ci.server.authentication.UsernamePassword;
import com.xebialabs.xlt.ci.server.domain.TestSpecification;

import hudson.FilePath;
import hudson.util.ListBoxModel;

import static com.xebialabs.xlt.ci.server.XLTestServerImpl.createSensibleURL;
import static com.xebialabs.xlt.ci.server.XLTestServerImpl.removeTrailingSlashes;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.nullValue;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

public class XLTestServerImplTest {

    // TODO: this is from the demo data and not 'the whole truth'
    private static final String TEST_SPEC_RESPONSE = "{\"regressionTests\":{\"id\":\"regressionTests\",\"project\":{\"id\":\"DemoProject\",\"title\":\"Demo "
            + "Project\",\"type\":\"xlt.Project\"},\"qualification\":{\"description\":\"Description unavailable\",\"type\":\"xlt"
            + ".DefaultFunctionalTestsQualifier\"},\"title\":\"regressionTests\",\"type\":\"xlt.ShowCaseTestSpecification\"},"
            + "\"demoGatling\":{\"id\":\"demoGatling\",\"project\":{\"id\":\"DemoProject\",\"title\":\"Demo Project\",\"type\":\"xlt.Project\"},"
            + "\"qualification\":{\"description\":\"Description unavailable\",\"type\":\"xlt.DefaultPerformanceTestsQualifier\"},\"title\":\"demoGatling\","
            + "\"type\":\"xlt.ShowCaseTestSpecification\"},\"functionalTestsComponentA\":{\"id\":\"functionalTestsComponentA\","
            + "\"project\":{\"id\":\"DemoProject\",\"title\":\"Demo Project\",\"type\":\"xlt.Project\"},\"qualification\":{\"description\":\"Description "
            + "unavailable\",\"type\":\"xlt.DefaultFunctionalTestsQualifier\"},\"title\":\"functionalTestsComponentA\",\"type\":\"xlt"
            + ".ShowCaseTestSpecification\"},\"f3850327-69df-4f01-b063-e5d367a960f8\":{\"id\":\"f3850327-69df-4f01-b063-e5d367a960f8\","
            + "\"project\":{\"id\":\"DemoProject\",\"title\":\"Demo Project\",\"type\":\"xlt.Project\"},\"title\":\"allMyTests\",\"type\":\"xlt"
            + ".TestSpecificationSet\"},\"calculatorTestsComponentB\":{\"id\":\"calculatorTestsComponentB\",\"project\":{\"id\":\"DemoProject\","
            + "\"title\":\"Demo Project\",\"type\":\"xlt.Project\"},\"qualification\":{\"description\":\"Description unavailable\",\"type\":\"xlt"
            + ".DefaultFunctionalTestsQualifier\"},\"title\":\"calculatorTestsComponentB\",\"type\":\"xlt.ShowCaseTestSpecification\"},\"performance tests "
            + "(old) for Demo\":{\"id\":\"performance tests (old) for Demo\",\"project\":{\"id\":\"DemoProject\",\"title\":\"Demo Project\",\"type\":\"xlt"
            + ".Project\"},\"title\":\"performance tests (old) for Demo\",\"type\":\"xlt.ShowCaseTestSpecification\"}}";

    public static final String PLUGIN_VERSION = " 1.2.3-SNAPSHOT";

    private XLTestServerImpl xlTestServer;
    private MockWebServer xltestviewMock;

    private final UsernamePassword cred = Mockito.mock(UsernamePassword.class);
    private final Log4jStream log4jStream = new Log4jStream(null, "XLTestServerImpl");

    @BeforeMethod
    public void setup() throws IOException {
        xltestviewMock = new MockWebServer();
        xltestviewMock.start();

        when(cred.getUsername()).thenReturn("admin");
        when(cred.getPassword()).thenReturn("admin");

        xlTestServer = new XLTestServerImpl(String.format("http://127.0.0.1:%d", xltestviewMock.getPort()), null,
                cred) {
            @Override
            protected String getPluginVersion() {
                return PLUGIN_VERSION;
            }
        };
    }

    @AfterMethod(alwaysRun = true)
    public void teardown() throws IOException {
        xltestviewMock.shutdown();
    }

    @Test
    public void shouldCheckConnection() throws InterruptedException {
        xltestviewMock.enqueue(
                new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8").setBody("{}"));

        xlTestServer.checkConnection();

        RecordedRequest request = xltestviewMock.takeRequest();
        assertEquals(request.getRequestLine(), "GET /api/internal/data HTTP/1.1");
        assertEquals(request.getHeader("accept"), "application/json; charset=utf-8");
        assertEquals(request.getHeader("authorization"), "Basic YWRtaW46YWRtaW4=");
        assertEquals(request.getHeader("User-Agent"), "XL TestView Jenkins plugin 1.2.3-SNAPSHOT");
        assertEquals(request.getBody().readUtf8(), "");
    }

    @Test
    public void shouldLoadTestSpecifications() throws IOException, InterruptedException {
        xltestviewMock.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8")
                .setBody(TEST_SPEC_RESPONSE));

        Map<String, TestSpecification> testSpecs = xlTestServer.getTestSpecifications();
        assertEquals(testSpecs.size(), 6L);

        RecordedRequest request = xltestviewMock.takeRequest();
        assertEquals(request.getRequestLine(), "GET /api/internal/testspecifications/extended HTTP/1.1");
        assertEquals(request.getHeader("accept"), "application/json; charset=utf-8");
        assertEquals(request.getHeader("authorization"), "Basic YWRtaW46YWRtaW4=");
        assertEquals(request.getBody().readUtf8(), "");
    }

    @Test
    public void fillsTestSpecificationIdItems() {
        xltestviewMock.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8")
                .setBody(TEST_SPEC_RESPONSE));

        Map<String, TestSpecification> testSpecifications = xlTestServer.getTestSpecifications();
        assertEquals(testSpecifications.size(), 6);

        ListBoxModel result = TestSpecificationDescribable.TestSpecificationDescriptor
                .getSpecificationOptions(testSpecifications);

        assertEquals(result.size(), 5); // because we don't count the one superset in the list
    }

    @Test
    public void shouldImport() throws Exception {
        xltestviewMock.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8")
                .setBody("{ \"testRunId\": \"testrunid\" }"));
        FilePath fp = new FilePath(new File(this.getClass().getResource("/demo_test_results").getPath()));

        Map<String, Object> metadata = createMetadata();

        xlTestServer.uploadTestRun("testspecid", fp, "**/*.xml", null, metadata, log4jStream);

        RecordedRequest request = xltestviewMock.takeRequest();
        verifyUploadRequest(request);
    }

    @Test(expectedExceptions = AuthenticationException.class, expectedExceptionsMessageRegExp = "User 'admin' and the supplied password are unable to log in")
    public void shouldHandleAuthenticationErrorDuringImport() throws Exception {
        xltestviewMock.enqueue(new MockResponse().setResponseCode(401));
        FilePath fp = new FilePath(new File(this.getClass().getResource("/demo_test_results").getPath()));

        Map<String, Object> metadata = createMetadata();

        xlTestServer.uploadTestRun("testspecid", fp, "**/*.xml", null, metadata, log4jStream);
    }

    @Test(expectedExceptions = PaymentRequiredException.class, expectedExceptionsMessageRegExp = "The XL TestView server does not have a valid license")
    public void shouldHandleLicenseErrorDuringImport() throws Exception {
        xltestviewMock.enqueue(new MockResponse().setResponseCode(402));
        FilePath fp = new FilePath(new File(this.getClass().getResource("/demo_test_results").getPath()));

        Map<String, Object> metadata = createMetadata();

        xlTestServer.uploadTestRun("testspecid", fp, "**/*.xml", null, metadata, log4jStream);
    }

    @Test(expectedExceptions = ConnectionException.class, expectedExceptionsMessageRegExp = "Cannot find test specification 'testspecid. Please check if the XL TestView server is running and the test specification exists.")
    public void shouldHandleConnectionErrorDuringImport() throws Exception {
        xltestviewMock.enqueue(new MockResponse().setResponseCode(404));
        FilePath fp = new FilePath(new File(this.getClass().getResource("/demo_test_results").getPath()));

        Map<String, Object> metadata = createMetadata();

        xlTestServer.uploadTestRun("testspecid", fp, "**/*.xml", null, metadata, log4jStream);
    }

    @Test
    public void shouldImportWithTrailingSlash() throws IOException, InterruptedException, MessagingException {
        String TRAILING_SLASH = "/";

        XLTestServerImpl slashedXlTestServer = new XLTestServerImpl(
                String.format("http://127.0.0.1:%d" + TRAILING_SLASH, xltestviewMock.getPort()), null, cred);

        xltestviewMock.enqueue(new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8")
                .setBody("{ \"testRunId\": \"testrunid\" }"));
        FilePath fp = new FilePath(new File(this.getClass().getResource("/demo_test_results").getPath()));

        Map<String, Object> metadata = createMetadata();

        slashedXlTestServer.uploadTestRun("testspecid", fp, "**/*.xml", null, metadata, log4jStream);

        RecordedRequest request = xltestviewMock.takeRequest();
        verifyUploadRequest(request);
    }

    @Test
    public void shouldCheckConnectionWithTrailingSlash()
            throws IOException, InterruptedException, MessagingException {
        String TRAILING_SLASH = "/";

        XLTestServerImpl slashedXlTestServer = new XLTestServerImpl(
                String.format("http://127.0.0.1:%d" + TRAILING_SLASH, xltestviewMock.getPort()), null, cred);

        xltestviewMock.enqueue(
                new MockResponse().addHeader("Content-Type", "application/json; charset=utf-8").setBody("{}"));

        slashedXlTestServer.checkConnection();

        RecordedRequest request = xltestviewMock.takeRequest();
        assertEquals(request.getRequestLine(), "GET /api/internal/data HTTP/1.1");
        assertEquals(request.getHeader("accept"), "application/json; charset=utf-8");
        assertEquals(request.getHeader("authorization"), "Basic YWRtaW46YWRtaW4=");
        assertEquals(request.getBody().readUtf8(), "");
    }

    @Test
    public void createSensibleURLCanDealWithSlashes()
            throws IOException, InterruptedException, MessagingException, URISyntaxException {
        assertThat(createSensibleURL("/api/foo/bar", new URL("http://www.myawesomeci.com/xltest/")),
                equalTo(new URL("http://www.myawesomeci.com/xltest/api/foo/bar")));
        assertThat(createSensibleURL("api/foo/bar", new URL("http://www.myawesomeci.com/xltest/")),
                equalTo(new URL("http://www.myawesomeci.com/xltest/api/foo/bar")));
        assertThat(createSensibleURL("/api/foo/bar", new URL("http://www.myawesomeci.com/xltest")),
                equalTo(new URL("http://www.myawesomeci.com/xltest/api/foo/bar")));
        assertThat(createSensibleURL("/api/foo/bar", new URL("http://www.myawesomeci.com/xltest//")),
                equalTo(new URL("http://www.myawesomeci.com/xltest/api/foo/bar")));
        assertThat(createSensibleURL("/api/foo/bar", new URL("http://www.myawesomeci.com/xltest/////")),
                equalTo(new URL("http://www.myawesomeci.com/xltest/api/foo/bar")));
    }

    @Test(expectedExceptions = ConnectionException.class, expectedExceptionsMessageRegExp = "URL is invalid or server is not running")
    public void shouldFailCheckConnectionWithNotFound()
            throws IOException, InterruptedException, MessagingException {
        xltestviewMock.enqueue(new MockResponse().setResponseCode(HttpStatus.SC_NOT_FOUND));

        xlTestServer.checkConnection();

        xltestviewMock.takeRequest();
    }

    @Test(expectedExceptions = AuthenticationException.class, expectedExceptionsMessageRegExp = "User 'admin' and the supplied password are unable to log in")
    public void shouldFailCheckConnectionWithBadCredentials()
            throws IOException, InterruptedException, MessagingException {
        xltestviewMock.enqueue(new MockResponse().setResponseCode(HttpStatus.SC_UNAUTHORIZED));

        xlTestServer.checkConnection();
    }

    @Test(expectedExceptions = PaymentRequiredException.class, expectedExceptionsMessageRegExp = "The XL TestView server does not have a valid license")
    public void shouldFailCheckConnectionWithInvalidLicense()
            throws IOException, InterruptedException, MessagingException {
        xltestviewMock.enqueue(new MockResponse().setResponseCode(HttpStatus.SC_PAYMENT_REQUIRED));

        xlTestServer.checkConnection();
    }

    private void verifyUploadRequest(final RecordedRequest request) throws IOException, MessagingException {
        assertEquals(request.getRequestLine(), "POST /api/internal/import/testspecid HTTP/1.1");
        assertEquals(request.getHeader("accept"), "application/json; charset=utf-8");
        assertEquals(request.getHeader("authorization"), "Basic YWRtaW46YWRtaW4=");
        assertThat(request.getHeader("Content-Length"), is(nullValue()));
        assertThat(request.getHeader("Transfer-Encoding"), is("chunked"));
        assertThat(request.getChunkSizes().get(0), greaterThan(0));
        assertThat(request.getChunkSizes().size(), greaterThan(0));

        assertTrue(request.getBodySize() > 0);

        ByteArrayDataSource bads = new ByteArrayDataSource(request.getBody().inputStream(), "multipart/mixed");
        MimeMultipart mp = new MimeMultipart(bads);
        assertTrue(request.getBodySize() > 0);

        assertEquals(mp.getCount(), 2);
        assertEquals(mp.getContentType(), "multipart/mixed");

        // TODO could do additional checks on metadata content
        BodyPart bodyPart1 = mp.getBodyPart(0);
        assertEquals(bodyPart1.getContentType(), "application/json; charset=utf-8");

        BodyPart bodyPart2 = mp.getBodyPart(1);
        assertEquals(bodyPart2.getContentType(), "application/zip");
    }

    private Map<String, Object> createMetadata() {
        Map<String, Object> metadata = new LinkedHashMap<String, Object>();
        metadata.put("source", "jenkins");
        metadata.put("serverUrl", "http://my-jenkins");
        metadata.put("buildResult", "success");
        metadata.put("buildNumber", "10");
        metadata.put("jobUrl", "http://my-jenkins/job/sub-dir/my-job");
        metadata.put("jobName", "sub-dir/my-job");
        metadata.put("buildUrl", "http://my-jenkins/job/test/14");
        metadata.put("executedOn", "slave1");
        metadata.put("buildParameters", new HashMap());
        return metadata;
    }

    private static final class Log4jStream extends PrintStream {
        private final Logger LOG;

        public Log4jStream(final OutputStream out, String logName) {
            super(new NullOutputStream());
            LOG = LoggerFactory.getLogger(logName);

        }

        @Override
        public PrintStream printf(final String format, final Object... args) {
            LOG.debug(String.format(format, args));

            return null;
        }
    }
}