dk.netarkivet.viewerproxy.WebProxyTester.java Source code

Java tutorial

Introduction

Here is the source code for dk.netarkivet.viewerproxy.WebProxyTester.java

Source

/*
 * #%L
 * Netarchivesuite - harvester - test
 * %%
 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
 *             the National Library of France and the Austrian National Library.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */
package dk.netarkivet.viewerproxy;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URI;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;

import dk.netarkivet.common.CommonSettings;
import dk.netarkivet.common.exceptions.ArgumentNotValid;
import dk.netarkivet.common.utils.Settings;
import dk.netarkivet.common.utils.SystemUtils;
import dk.netarkivet.testutils.LogbackRecorder;
import dk.netarkivet.testutils.ReflectUtils;
import dk.netarkivet.testutils.StringAsserts;
import dk.netarkivet.viewerproxy.distribute.HTTPControllerServerTester;

/**
 * Test the WebProxy class which wraps a handler in a Jetty server. The testJettyIntegration test verifies the handler
 * <-> Jetty integration, where the remaining tests directly tests the WebProxy methods and inner classes.
 */
public class WebProxyTester {

    private URIResolver uriResolverMock;
    //private org.eclipse.jetty.server.Request requestMock;
    //private org.eclipse.jetty.server.Response responseMock;
    private org.mortbay.jetty.Request requestMock;
    private org.mortbay.jetty.Response responseMock;

    private WebProxy proxy;
    private int httpPort;

    @Before
    public void setUp() throws IOException {
        // Check port not in use (since this will fail all tests)

        httpPort = Settings.getInt(CommonSettings.HTTP_PORT_NUMBER);
        if (httpPort < 1025 || httpPort > 65535) {
            fail("Port must be in the range 1025-65535, not " + httpPort);
        }
        try {
            new Socket(InetAddress.getLocalHost(), httpPort);
            fail("Port already in use before unit test");
        } catch (IOException e) {
            // Expected
        }

        uriResolverMock = mock(URIResolver.class);
        /*
                requestMock = mock(org.eclipse.jetty.server.Request.class);
                responseMock = mock(org.eclipse.jetty.server.Response.class);
        */
        requestMock = mock(org.mortbay.jetty.Request.class);
        responseMock = mock(org.mortbay.jetty.Response.class);

    }

    @After
    public void tearDown() {
        if (proxy != null) {
            proxy.kill();
        }
        // Check port not in use (since this might break later tests)
        try {
            new Socket(InetAddress.getLocalHost(), httpPort);
            fail("Port still in use after killing server");
        } catch (IOException e) {
            // expected
        }
    }

    @Test
    public void testUriEncode() {
        String test_string = "{abcd{fgka}";
        assertEquals("Should recover original string after decoding", "%7Babcd%7Bfgka%7D",
                WebProxy.HttpRequest.uriEncode(test_string));
    }

    /**
     * Test the general integration of the WebProxy access through the running Jetty true:
     */
    @Test
    public void testJettyIntegration() throws Exception {
        TestURIResolver uriResolver = new TestURIResolver();
        proxy = new WebProxy(uriResolver);

        try {
            new Socket(InetAddress.getLocalHost(), httpPort);
        } catch (IOException e) {
            fail("Port not in use after starting server");
        }

        // GET request
        HttpClient client = new HttpClient();
        HostConfiguration hc = new HostConfiguration();
        String hostName = SystemUtils.getLocalHostName();
        hc.setProxy(hostName, httpPort);
        client.setHostConfiguration(hc);
        GetMethod get = new GetMethod("http://foo.bar/");
        client.executeMethod(get);

        assertEquals("Status code should be what URI resolver gives", 242, get.getStatusCode());
        assertEquals("Body should contain what URI resolver wrote", "Test", get.getResponseBodyAsString());
        get.releaseConnection();

        // POST request
        PostMethod post = new PostMethod("http://foo2.bar/");
        post.addParameter("a", "x");
        post.addParameter("a", "y");
        client.executeMethod(post);

        // Check request received by URIResolver
        assertEquals("URI resolver lookup should be called.", 2, uriResolver.lookupCount);
        assertEquals("URI resolver lookup should be called with right URI.", new URI("http://foo2.bar/"),
                uriResolver.lookupRequestArgument);
        assertEquals("Posted parameter should be received.", 1, uriResolver.lookupRequestParameteres.size());
        assertNotNull("Posted parameter should be received.", uriResolver.lookupRequestParameteres.get("a"));
        assertEquals("Posted parameter should be received.", 2,
                uriResolver.lookupRequestParameteres.get("a").length);
        assertEquals("Posted parameter should be received.", "x", uriResolver.lookupRequestParameteres.get("a")[0]);
        assertEquals("Posted parameter should be received.", "y", uriResolver.lookupRequestParameteres.get("a")[1]);
        assertEquals("Status code should be what URI resolver gives", 242, post.getStatusCode());
        assertEquals("Body should contain what URI resolver wrote", "Test", post.getResponseBodyAsString());
        post.releaseConnection();

        // Request with parameter and portno
        get = new GetMethod("http://foo2.bar:8090/?baz=boo");
        client.executeMethod(get);

        // Check request received by URIResolver
        assertEquals("URI resolver lookup should be called.", 3, uriResolver.lookupCount);
        assertEquals("URI resolver 2 lookup should be called with right URI.",
                new URI("http://foo2.bar:8090/?baz=boo"), uriResolver.lookupRequestArgument);
        assertEquals("Status code should be what URI resolver gives", 242, get.getStatusCode());
        assertEquals("Body should contain what URI resolver wrote", "Test", get.getResponseBodyAsString());
        get.releaseConnection();
    }

    /** Verify that the setURIResolver method changes the uriresolver correctly */
    @Test
    @Ignore
    public void testSetURIResolver() throws Exception {
        URIResolver uriResolverMock2 = mock(URIResolver.class);
        proxy = new WebProxy(uriResolverMock);
        proxy.setURIResolver(uriResolverMock2);
        // FIXME Compile-error when backporting to jetty 6.1.26 (Therefore is test ignored)
        //proxy.handle(null, null, requestMock, responseMock);

        verify(uriResolverMock2).lookup(any(Request.class), any(Response.class));
        verifyNoMoreInteractions(uriResolverMock, uriResolverMock2);
    }

    @Test
    public void testKill() throws Exception {
        proxy = new WebProxy(uriResolverMock);
        // Check port in use
        try {
            new Socket(InetAddress.getLocalHost(), httpPort);
        } catch (IOException e) {
            fail("Port not in use after starting server");
        }

        // Kill server
        proxy.kill();

        // Check port not in use
        try {
            new Socket(InetAddress.getLocalHost(), httpPort);
            fail("Port still in use after killing server");
        } catch (IOException e) {
            // expected
        }
    }

    @Test
    @Ignore
    public void testHandle() throws Exception {
        proxy = new WebProxy(uriResolverMock);
        String url = "http://foo.bar/";
        when(requestMock.getRequestURL()).thenReturn(new StringBuffer(url));
        // FIXME Compile-error when backporting to jetty 6.1.26 (Therefore is test ignored)
        //proxy.handle(null, null, requestMock, responseMock);

        verify(requestMock).setHandled(true);
        ArgumentCaptor<Request> requestArgument = ArgumentCaptor.forClass(Request.class);
        ArgumentCaptor<Response> responseArgument = ArgumentCaptor.forClass(Response.class);

        verify(uriResolverMock).lookup(requestArgument.capture(), responseArgument.capture());

        assertEquals(new URI("http://foo.bar/"), requestArgument.getValue().getURI());
        // The request/response in the delegated handle call should delegate method calls to the original
        // request/response objects"

        responseArgument.getValue().getOutputStream();
        verify(responseMock).getOutputStream();

        verifyNoMoreInteractions(uriResolverMock);
    }

    /**
     * Test the error response generation.
     */
    @Test
    @Ignore
    public void testCreateErrorResponse() throws Exception {
        LogbackRecorder lr = LogbackRecorder.startRecorder();
        proxy = new WebProxy(uriResolverMock);
        HTTPControllerServerTester.TestResponse response = new HTTPControllerServerTester.TestResponse();
        URI uri = new URI("http://www.statsbiblioteket.dk/");
        String ExceptionMessage = "ExceptionTestMessage";
        Exception e = new ArgumentNotValid(ExceptionMessage);
        Method m = ReflectUtils.getPrivateMethod(WebProxy.class, "createErrorResponse", URI.class, Response.class,
                Throwable.class);
        m.invoke(proxy, uri, response, e);
        // LogUtils.flushLogs(WebProxy.class.getName());
        /*
         * FileAsserts.assertFileMatches("Should have logged a warning", "WARNING:.*" + uri + ".*\n.*" +
         * ExceptionMessage, LOG_FILE);
         */
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(out);
        e.printStackTrace(pw);
        pw.close();
        String eStr = new String(out.toByteArray());
        // lr.assertLogMatches("Should have logged a warning", "Exception for : " + uri + "\n" + eStr);
        lr.assertLogContains("Should have logged a warning", "Exception for : " + uri + "\n" + eStr);
        lr.reset();

        String result = response.getOutputStream().toString();
        StringAsserts.assertStringNotContains("Should not contain null", "null", result);
        StringAsserts.assertStringContains("Should contain the URI", uri.toString(), result);
        StringAsserts.assertStringContains("Should contain the Exception", ArgumentNotValid.class.getName(),
                result);
        StringAsserts.assertStringContains("Should contain the Exception msg", ExceptionMessage, result);
        StringAsserts.assertStringContains("Should contain the Exception trace", "testCreateErrorResponse", result);
        response.reset();
        m.invoke(proxy, null, response, e);
        // LogUtils.flushLogs(WebProxy.class.getName());
        /*
         * FileAsserts.assertFileMatches("Should have logged a warning", "WARNING:.*" + null + ".*\n.*" +
         * ExceptionMessage, LOG_FILE);
         */
        lr.assertLogMatches("Should have logged a warning",
                "Exception for : .*" + null + ".*\n.*" + ExceptionMessage);
        lr.reset();
        result = response.getOutputStream().toString();
        StringAsserts.assertStringContains("Should contain null for the URI", "null", result);
        StringAsserts.assertStringContains("Should contain the Exception", ArgumentNotValid.class.getName(),
                result);
        StringAsserts.assertStringContains("Should contain the Exception msg", ExceptionMessage, result);
        StringAsserts.assertStringContains("Should contain the Exception trace", "testCreateErrorResponse", result);
        response.reset();
        m.invoke(proxy, uri, response, null);
        // LogUtils.flushLogs(WebProxy.class.getName());
        /*
         * FileAsserts.assertFileMatches("Should have logged a warning", "(?m)WARNING:.*" + uri + "$", LOG_FILE);
         */
        lr.assertLogMatches("Should have logged a warning", "Exception for : .*" + uri);
        lr.reset();
        result = response.getOutputStream().toString();
        StringAsserts.assertStringContains("Should contain the URI", uri.toString(), result);
        StringAsserts.assertStringContains("Should contain null for exception", "null\n", result);
        m.invoke(proxy, uri, null, e);
        // TODO Remove when @Ignore is fixed.
        /*
         * FileAsserts.assertFileMatches("Should have logged a warning", "WARNING:.*Error writing error response" + ".*"
         * + uri + ".*" + e.getClass().getName() + ".*" + ExceptionMessage, LOG_FILE);
         */
        lr.assertLogMatches("Should have logged a warning", ".*Error writing error response" + ".*" + uri + ".*"
                + e.getClass().getName() + ".*" + ExceptionMessage);
        lr.reset();
        lr.stopRecorder();
    }

    /**
     * Constructs an instance of the inner class WebProxy.HttpRequest with a url containing "{" and checks that the
     * resulting uri escapes the curly brackets correctly.
     *
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    @Test
    public void testGetUri() throws Exception {
        HttpServletRequest servletRequest = mock(HttpServletRequest.class);
        when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("http://somedomain.dk?id=%7B12345%7D"));
        WebProxy.HttpRequest http_request = new WebProxy.HttpRequest(servletRequest);
        URI uri = http_request.getURI();
        assertEquals("Expect uri to be escaped", "http://somedomain.dk?id=%7B12345%7D", uri.toString());
    }

    public static class TestURIResolver implements URIResolver {
        int lookupCount = 0;
        int totalCount = 0;
        URI lookupRequestArgument;
        private Map<String, String[]> lookupRequestParameteres;

        public int lookup(Request request, Response response) {
            lookupCount++;
            totalCount++;
            lookupRequestArgument = request.getURI();
            lookupRequestParameteres = request.getParameterMap();
            try {
                response.getOutputStream().write("Test".getBytes());
            } catch (IOException e) {
                fail("URI resolver cannot write to stream");
            }
            response.setStatus(242);
            return 42;
        }
    }

}