/*
 * $Id: TestRequestUtils.java 471754 2006-11-06 14:55:09Z husted $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.struts.util;

import java.util.HashMap;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.WebMock;
import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionFormBean;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.chain.Constants;
import org.apache.struts.config.ActionConfig;
import org.apache.struts.config.ControllerConfig;
import org.apache.struts.config.FormPropertyConfig;
import org.apache.struts.config.ForwardConfig;
import org.apache.struts.config.ModuleConfig;
import org.apache.struts.config.ModuleConfigFactory;
import org.apache.struts.mock.MockFormBean;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;


/**
 * <p>Unit tests for <code>org.apache.struts.util.RequestUtils</code>.</p>
 *
 * @version $Rev: 471754 $ $Date: 2005-08-14 17:24:39 -0400 (Sun, 14 Aug 2005)
 *          $
 */
public class TestRequestUtils {

    // ----------------------------------------------------------------- Basics

    // ------------------------------------------------------- Individual Tests

    // ---------------------------------------------------------- absoluteURL()

    /**
     * testAbsoluteURL
     */
    @Test
    public void testAbsoluteURL() {
        HttpServletRequest request = WebMock.createHttpServletRequest(
                        WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/action.do");
        Mockito.when(request.getScheme()).thenReturn("http");
        Mockito.when(request.getServerName()).thenReturn("localhost");
        Mockito.when(Integer.valueOf(request.getServerPort())).thenReturn(Integer.valueOf(8080));

        String url = RequestUtils.absoluteURL(request, "/foo/bar.jsp").toString();
        Assert.assertEquals(
                "absoluteURL is correct", "http://localhost:8080/myapp/foo/bar.jsp", url);
    }

    // ------------------------------------------------------------ actionURL()

    /**
     * create ModuleConfig
     * @return ModuleConfig
     */
    protected ModuleConfig createModuleConfig() {

        ModuleConfig moduleConfig = ModuleConfigFactory.createFactory().createModuleConfig("");

        // Forward "external" to "http://jakarta.apache.org/"
        moduleConfig.addForwardConfig(
                new ActionForward("external", "http://jakarta.apache.org/", false));
        // Forward "foo" to "/bar.jsp"
        moduleConfig.addForwardConfig(new ActionForward("foo", "/bar.jsp", false));
        // Forward "relative1" to "relative.jsp" non-context-relative
        moduleConfig.addForwardConfig(new ActionForward("relative1", "relative.jsp", false));
        // Forward "relative2" to "relative.jsp" context-relative
        moduleConfig.addForwardConfig(new ActionForward("relative2", "relative.jsp", false));

        // Form Bean "static" is a standard ActionForm subclass
        ActionFormBean formBean =
            new ActionFormBean("static", "org.apache.struts.mock.MockFormBean");
        moduleConfig.addFormBeanConfig(formBean);

        // Action "/static" uses the "static" form bean in request scope
        ActionMapping mapping = createActionMapping(
                "/static.jsp", "static", "/static", "request", "org.apache.struts.mock.MockAction");
        moduleConfig.addActionConfig(mapping);

        // Form Bean "dynamic" is a DynaActionForm with the same properties
        formBean = new ActionFormBean(
                "dynamic", "org.apache.struts.action.DynaActionForm");
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("booleanProperty", "boolean", "false"));
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("stringProperty", "java.lang.String", null));
        moduleConfig.addFormBeanConfig(formBean);

        // Action "/dynamic" uses the "dynamic" form bean in session scope
        mapping = createActionMapping(
             "/dynamic.jsp", "dynamic", "/dynamic", "session", "org.apache.struts.mock.MockAction");
        moduleConfig.addActionConfig(mapping);

        // Form Bean "/dynamic0" is a DynaActionForm with initializers
        formBean = new ActionFormBean("dynamic0", "org.apache.struts.action.DynaActionForm");
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("booleanProperty", "boolean", "true"));
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("stringProperty", "java.lang.String", "String Property"));
        // 4 should be ignored
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("intArray1", "int[]", "{1,2,3}", 4));
        // 5 should be respected
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("intArray2", "int[]", null, 5));
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("principal", "org.apache.struts.mock.MockPrincipal", null));
        // 2 should be ignored
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("stringArray1", "java.lang.String[]", "{aaa,bbb,ccc}", 2));
        // 3 should be respected
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("stringArray2", "java.lang.String[]", null, 3));
        moduleConfig.addFormBeanConfig(formBean);

        // Action "/dynamic0" uses the "dynamic0" form bean in request scope
        mapping = createActionMapping(
                null, "dynamic0", "/dynamic0", "request", "org.apache.struts.mock.MockAction");
        moduleConfig.addActionConfig(mapping);

        // Action "/noform" has no form bean associated with it
        mapping = createActionMapping(
                null, null, "/noform", null, "org.apache.struts.mock.MockAction");
        moduleConfig.addActionConfig(mapping);

        addConfigure(moduleConfig);

        return moduleConfig;
    }

    /**
     * create ModuleConfig
     * @return ModuleConfig
     */
    protected ModuleConfig createModuleConfig2() {

        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

        ModuleConfig moduleConfig2 = factoryObject.createModuleConfig("/2");

        // Forward "external" to "http://jakarta.apache.org/"
        moduleConfig2.addForwardConfig(
                new ActionForward("external", "http://jakarta.apache.org/", false));

        // Forward "foo" to "/baz.jsp" (different from default)
        moduleConfig2.addForwardConfig(
                new ActionForward("foo", "/baz.jsp", false));

        // Forward "relative1" to "relative.jsp" non-context-relative
        moduleConfig2.addForwardConfig(
                new ActionForward("relative1", "relative.jsp", false));

        // Forward "relative2" to "relative.jsp" context-relative
        moduleConfig2.addForwardConfig(
                new ActionForward("relative2", "relative.jsp", false));

        // Form Bean "static" is a standard ActionForm subclass (same as default)
        ActionFormBean formBean =
                new ActionFormBean("static", "org.apache.struts.mock.MockFormBean");
        moduleConfig2.addFormBeanConfig(formBean);

        // Action "/static" uses the "static" form bean in request scope (same as default)
        ActionMapping mapping = createActionMapping(
                "/static.jsp", "static", "/static", "request", "org.apache.struts.mock.MockAction");
        moduleConfig2.addActionConfig(mapping);

        // Form Bean "dynamic2" is a DynaActionForm with the same properties
        formBean = new ActionFormBean("dynamic2", "org.apache.struts.action.DynaActionForm");
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("booleanProperty", "boolean", "false"));
        formBean.addFormPropertyConfig(
                new FormPropertyConfig("stringProperty", "java.lang.String", null));
        moduleConfig2.addFormBeanConfig(formBean);

        // Action "/dynamic2" uses the "dynamic2" form bean in session scope
        mapping = createActionMapping("/dynamic2.jsp", "dynamic2",
                "/dynamic2", "session", "org.apache.struts.mock.MockAction");
        moduleConfig2.addActionConfig(mapping);

        // Action "/noform" has no form bean associated with it (same as default)
        mapping = createActionMapping(
                null, null, "/noform", null, "org.apache.struts.mock.MockAction");
        moduleConfig2.addActionConfig(mapping);

        addConfigure(moduleConfig2);

        return moduleConfig2;
    }

    /**
     * create ModuleConfig
     * @return ModuleConfig
     */
    protected ModuleConfig createModuleConfig3() {
        final ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

        final ModuleConfig moduleConfig3 = factoryObject.createModuleConfig("/3");

        // Instantiate the controller configuration for this app
        final ControllerConfig controller = new ControllerConfig();
        // Configure the properties we will be testing
        controller.setForwardPattern("/forwarding$M$P");
        controller.setInputForward(true);
        controller.setPagePattern("/paging$M$P");

        moduleConfig3.setControllerConfig(controller);

        addConfigure(moduleConfig3);

        return moduleConfig3;
    }

    /**
     * create ActionMapping
     *
     * @param input Input
     * @param name Name
     * @param path Path
     * @param scope Scope
     * @param type Type
     * @return ActionMapping
     */
    protected ActionMapping createActionMapping(final String input, final String name,
            final String path, final String scope, final String type) {
        ActionMapping mapping = new ActionMapping();
        mapping.setInput(input);
        mapping.setName(name);
        mapping.setPath(path);
        mapping.setScope(scope);
        mapping.setType(type);
        return mapping;
    }

    /**
     * add Configure
     *
     * @param moduleConfig ModuleConfig
     */
    protected void addConfigure(final ModuleConfig moduleConfig) {
        // Configure global forward declarations
        // No redirect, same module
        moduleConfig.addForwardConfig(
                new ForwardConfig("moduleForward", "/module/forward", false));
        // Redirect, same module
        moduleConfig.addForwardConfig(
                new ForwardConfig("moduleRedirect", "/module/redirect", true));
        // No redirect Specify module
        moduleConfig.addForwardConfig(
                new ForwardConfig("contextForward", "/forward", false, "/context"));
        // Redirect Specify module
        moduleConfig.addForwardConfig(
                new ForwardConfig("contextRedirect", "/redirect", true, "/context"));
        // No redirect, same module
        moduleConfig.addForwardConfig(
                new ForwardConfig("moduleNoslash", "module/noslash", false));
        // No redirect Specify module
        moduleConfig.addForwardConfig(
                new ForwardConfig("contextNoslash", "noslash", false, "/context"));
    }

    /**
     * Default application -- extension mapping
     */
    @Test
    public void testActionURL1() {

        ModuleConfig moduleConfig = createModuleConfig();

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/foo.do");
        request.setAttribute(Globals.MODULE_KEY, moduleConfig);

        String url = RequestUtils.actionURL(request,
                    moduleConfig.findActionConfig("/dynamic"), "*.do");

        Assert.assertNotNull("URL was returned", url);
        Assert.assertEquals("URL value", "/dynamic.do", url);
    }

    /**
     * Second application -- extension mapping
     */
    @Test
    public void testActionURL2() {

        ModuleConfig moduleConfig2 = createModuleConfig2();

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/2/foo.do");
        request.setAttribute(Globals.MODULE_KEY, moduleConfig2);

        String url = RequestUtils.actionURL(request,
                    moduleConfig2.findActionConfig("/dynamic2"), "*.do");

        Assert.assertNotNull("URL was returned", url);
        Assert.assertEquals("URL value", "/2/dynamic2.do", url);
    }

    /**
     * Default application -- path mapping
     */
    @Test
    public void testActionURL3() {

        ModuleConfig moduleConfig = createModuleConfig();

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/do/foo");
        request.setAttribute(Globals.MODULE_KEY, moduleConfig);

        String url = RequestUtils.actionURL(request,
                    moduleConfig.findActionConfig("/dynamic"), "/do/*");

        Assert.assertNotNull("URL was returned", url);
        Assert.assertEquals("URL value", "/do/dynamic", url);
    }

    // ----------------------------------------------------- createActionForm()

    /**
     * Default module -- No ActionForm should be created
     */
    @Test
    public void testCreateActionForm1a() {
        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/noform.do");

        ModuleConfig moduleConfig = createModuleConfig();

        ActionConfig mapping = moduleConfig.findActionConfig("/noform");
        Assert.assertNotNull("Found /noform mapping", mapping);

        ActionForm form = RequestUtils.createActionForm(request, mapping, moduleConfig, null);
        Assert.assertNull("No ActionForm returned", form);
    }

    /**
     * Second module -- No ActionForm should be created
     */
    @Test
    public void testCreateActionForm1b() {
        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/2/noform.do");

        ModuleConfig moduleConfig2 = createModuleConfig2();

        ActionConfig mapping = moduleConfig2.findActionConfig("/noform");
        Assert.assertNotNull("Found /noform mapping", mapping);

        ActionForm form = RequestUtils.createActionForm(request, mapping, moduleConfig2, null);
        Assert.assertNull("No ActionForm returned", form);
    }

    /**
     * Default module -- Standard ActionForm should be created
     */
    @Test
    public void testCreateActionForm2a() {
        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/static.do");

        ModuleConfig moduleConfig = createModuleConfig();

        ActionConfig mapping = moduleConfig.findActionConfig("/static");

        Assert.assertNotNull("Found /static mapping", mapping);
        Assert.assertNotNull("Mapping has non-null name", mapping.getName());
        Assert.assertEquals("Mapping has correct name", "static", mapping.getName());
        Assert.assertNotNull("AppConfig has form bean " + mapping.getName(),
                moduleConfig.findFormBeanConfig(mapping.getName()));

        ActionForm form = RequestUtils.createActionForm(request, mapping, moduleConfig, null);

        Assert.assertNotNull("ActionForm returned", form);
        Assert.assertTrue("ActionForm of correct type", form instanceof MockFormBean);
    }

    /**
     * Second module -- Standard ActionForm should be created
     */
    @Test
    public void testCreateActionForm2b() {
        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/2/static.do");

        ModuleConfig moduleConfig2 = createModuleConfig2();

        ActionConfig mapping = moduleConfig2.findActionConfig("/static");

        Assert.assertNotNull("Found /static mapping", mapping);
        Assert.assertNotNull("Mapping has non-null name", mapping.getName());
        Assert.assertEquals("Mapping has correct name", "static", mapping.getName());
        Assert.assertNotNull("AppConfig has form bean " + mapping.getName(),
                moduleConfig2.findFormBeanConfig(mapping.getName()));

        ActionForm form = RequestUtils.createActionForm(request, mapping, moduleConfig2, null);

        Assert.assertNotNull("ActionForm returned", form);
        Assert.assertTrue("ActionForm of correct type", form instanceof MockFormBean);
    }

    // ----------------------------------------------------------- forwardURL()

    /**
     * Default module (default forwardPattern)
     */
    @Test
    public void testForwardURL1() {

        ModuleConfig moduleConfig = createModuleConfig();

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/action.do");
        request.setAttribute(Globals.MODULE_KEY, moduleConfig);

        // redirect=false, module=null
        ForwardConfig forward = moduleConfig.findForwardConfig("moduleForward");
        Assert.assertNotNull("moduleForward found", forward);
        String result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleForward value", "/module/forward", result);

        // redirect=true, module=null
        forward = moduleConfig.findForwardConfig("moduleRedirect");
        Assert.assertNotNull("moduleRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleRedirect value", "/module/redirect", result);

        // redirect=false, module=/context
        forward = moduleConfig.findForwardConfig("contextForward");
        Assert.assertNotNull("contextForward found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextForward value", "/context/forward", result);

        // redirect=true, module=/context
        forward = moduleConfig.findForwardConfig("contextRedirect");
        Assert.assertNotNull("contextRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextRedirct value", "/context/redirect", result);

        // noslash, module=null
        forward = moduleConfig.findForwardConfig("moduleNoslash");
        Assert.assertNotNull("moduleNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleNoslash value", "/module/noslash", result);

        // noslash, module=/
        forward = moduleConfig.findForwardConfig("contextNoslash");
        Assert.assertNotNull("contextNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextNoslash value", "/context/noslash", result);
    }

    /**
     * Second module (default forwardPattern)
     */
    @Test
    public void testForwardURL2() {

        ModuleConfig moduleConfig2 = createModuleConfig2();

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/2/action.do");
        request.setAttribute(Globals.MODULE_KEY, moduleConfig2);

        // redirect=false, module=null
        ForwardConfig forward = moduleConfig2.findForwardConfig("moduleForward");
        Assert.assertNotNull("moduleForward found", forward);
        String result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleForward value", "/2/module/forward", result);

        // redirect=true, module=null
        forward = moduleConfig2.findForwardConfig("moduleRedirect");
        Assert.assertNotNull("moduleRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleRedirect value", "/2/module/redirect", result);

        // redirect=false, module=/context
        forward = moduleConfig2.findForwardConfig("contextForward");
        Assert.assertNotNull("contextForward found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextForward value", "/context/forward", result);

        // redirect=true, module=/context
        forward = moduleConfig2.findForwardConfig("contextRedirect");
        Assert.assertNotNull("contextRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextRedirct value", "/context/redirect", result);

        // noslash, module=null
        forward = moduleConfig2.findForwardConfig("moduleNoslash");
        Assert.assertNotNull("moduleNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleNoslash value", "/2/module/noslash", result);

        // noslash, module=/
        forward = moduleConfig2.findForwardConfig("contextNoslash");
        Assert.assertNotNull("contextNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextNoslash value", "/context/noslash", result);
    }

    /**
     * Third module (custom forwardPattern)
     */
    @Test
    public void testForwardURL3() {

        ModuleConfig moduleConfig3 = createModuleConfig3();

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/3/action.do");
        request.setAttribute(Globals.MODULE_KEY, moduleConfig3);

        // redirect=false, module=null
        ForwardConfig forward = moduleConfig3.findForwardConfig("moduleForward");
        Assert.assertNotNull("moduleForward found", forward);
        String result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleForward value", "/forwarding/3/module/forward", result);

        // redirect=true, module=null
        forward = moduleConfig3.findForwardConfig("moduleRedirect");
        Assert.assertNotNull("moduleRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleRedirect value", "/forwarding/3/module/redirect", result);

        // redirect=false, module=/context
        forward = moduleConfig3.findForwardConfig("contextForward");
        Assert.assertNotNull("contextForward found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextForward value", "/forwarding/context/forward", result);

        // redirect=true, module=/context
        forward = moduleConfig3.findForwardConfig("contextRedirect");
        Assert.assertNotNull("contextRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextRedirct value", "/forwarding/context/redirect", result);

        // noslash, module=null
        forward = moduleConfig3.findForwardConfig("moduleNoslash");
        Assert.assertNotNull("moduleNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("moduleNoslash value", "/forwarding/3/module/noslash", result);

        // noslash, module=/
        forward = moduleConfig3.findForwardConfig("contextNoslash");
        Assert.assertNotNull("contextNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, null);
        Assert.assertEquals("contextNoslash value", "/forwarding/context/noslash", result);
    }

    /**
     * Cross module forwards
     */
    @Test
    public void testForwardURLa() {

        ModuleConfig moduleConfig3 = createModuleConfig3();

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/action.do");
        request.setAttribute(Globals.MODULE_KEY, moduleConfig3);

        // redirect=false, contextRelative=false, link to module 3
        ForwardConfig forward = moduleConfig3.findForwardConfig("moduleForward");
        Assert.assertNotNull("moduleForward found", forward);
        String result = RequestUtils.forwardURL(request, forward, moduleConfig3);
        Assert.assertEquals("moduleForward value", "/forwarding/3/module/forward", result);

        // redirect=true, contextRelative=false, link to module 3
        forward = moduleConfig3.findForwardConfig("moduleRedirect");
        Assert.assertNotNull("moduleRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, moduleConfig3);
        Assert.assertEquals("moduleRedirect value", "/forwarding/3/module/redirect", result);

        // redirect=false, module=/context
        forward = moduleConfig3.findForwardConfig("contextForward");
        Assert.assertNotNull("contextForward found", forward);
        result = RequestUtils.forwardURL(request, forward, moduleConfig3);
        Assert.assertEquals("contextForward value", "/forwarding/context/forward", result);

        // redirect=true, module=/context
        forward = moduleConfig3.findForwardConfig("contextRedirect");
        Assert.assertNotNull("contextRedirect found", forward);
        result = RequestUtils.forwardURL(request, forward, moduleConfig3);
        Assert.assertEquals("contextRedirct value", "/forwarding/context/redirect", result);

        // noslash, contextRelative=false, link to module 3
        forward = moduleConfig3.findForwardConfig("moduleNoslash");
        Assert.assertNotNull("moduleNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, moduleConfig3);
        Assert.assertEquals("moduleNoslash value", "/forwarding/3/module/noslash", result);

        // noslash, module=/
        forward = moduleConfig3.findForwardConfig("contextNoslash");
        Assert.assertNotNull("contextNoslash found", forward);
        result = RequestUtils.forwardURL(request, forward, moduleConfig3);
        Assert.assertEquals("contextNoslash value", "/forwarding/context/noslash", result);
    }

    // ----------------------------------------------------------- requestURL()

    /**
     * testRequestURL
     */
    @Test
    public void testRequestURL() {
        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/foo.do");
        Mockito.when(request.getScheme()).thenReturn("http");
        Mockito.when(request.getServerName()).thenReturn("localhost");
        Mockito.when(Integer.valueOf(request.getServerPort())).thenReturn(Integer.valueOf(8080));
        Mockito.when(request.getRequestURI()).thenReturn("/myapp/foo.do");

        String url = RequestUtils.requestURL(request).toString();

        Assert.assertNotNull("URL was returned", url);
        Assert.assertEquals("URL value", "http://localhost:8080/myapp/foo.do", url);
    }

    // ---------------------------------------------------- selectApplication()

    /**
     * Map to the default module -- direct
     */
    @Test
    public void testSelectApplication1a() {
        ServletContext context = WebMock.createServletContext();
        context.setAttribute(Globals.MODULE_KEY, createModuleConfig());

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/noform.do");

        ModuleUtils.getInstance().selectModule(request, context);

        ModuleConfig actual = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);

        Assert.assertNotNull("Selected a module", actual);
        Assert.assertEquals("Selected correct module", "", actual.getPrefix());
    }

    /**
     * Map to the second module -- direct
     */
    @Test
    public void testSelectApplication1b() {
        String[] prefixes = {"/1", "/2"};

        ServletContext context = WebMock.createServletContext();
        context.setAttribute(Globals.MODULE_KEY + "/2", createModuleConfig2());
        context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/2/noform.do");

        ModuleUtils.getInstance().selectModule(request, context);

        ModuleConfig actual = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);

        Assert.assertNotNull("Selected a module", actual);
        Assert.assertEquals("Selected correct module", "/2", actual.getPrefix());
    }

    /**
     * Map to the default module -- include
     */
    @Test
    public void testSelectApplication2a() {
        String[] prefixes = {"/1", "/2"};

        ServletContext context = WebMock.createServletContext();
        context.setAttribute(Globals.MODULE_KEY, createModuleConfig());
        context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/2/noform.do");
        request.setAttribute(Constants.INCLUDE_SERVLET_PATH, "/noform.do");

        ModuleUtils.getInstance().selectModule(request, context);

        ModuleConfig actual = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);

        Assert.assertNotNull("Selected an application", actual);
        Assert.assertEquals("Selected correct application", "", actual.getPrefix());
    }

    /**
     * Map to the second module -- include
     */
    @Test
    public void testSelectApplication2b() {
        String[] prefixes = {"/1", "/2"};

        ServletContext context = WebMock.createServletContext();
        context.setAttribute(Globals.MODULE_KEY + "/2", createModuleConfig2());
        context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/noform.do");
        request.setAttribute(Constants.INCLUDE_SERVLET_PATH, "/2/noform.do");

        ModuleUtils.getInstance().selectModule(request, context);

        ModuleConfig actual = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);

        Assert.assertNotNull("Selected a module", actual);
        Assert.assertEquals("Selected correct module", "/2", actual.getPrefix());
    }

    // ------------------------------------------------------------ serverURL()

    /**
     * Basic test on values in mock objects
     */
    @Test
    public void testServerURL() {

        HttpServletRequest request = WebMock.createHttpServletRequest(
                WebMock.createHttpSession(), new HashMap<>(), new HashMap<>());
        Mockito.when(request.getContextPath()).thenReturn("/myapp");
        Mockito.when(request.getServletPath()).thenReturn("/noform.do");
        Mockito.when(request.getScheme()).thenReturn("http");
        Mockito.when(request.getServerName()).thenReturn("localhost");
        Mockito.when(Integer.valueOf(request.getServerPort())).thenReturn(Integer.valueOf(8080));

        String url = RequestUtils.serverURL(request).toString();

        Assert.assertNotNull("serverURL is present", url);
        Assert.assertEquals("serverURL value", "http://localhost:8080", url);
    }
}
