/*
 * $Id: TestMockBase.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.mock;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import org.apache.struts.Globals;
import org.apache.struts.action.ActionFormBean;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
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;

/**
 * <p>Convenience base class for unit tests of the <code>org.apache.struts.util</code>
 * package, and others that require a runtime environment similar to what the
 * Struts controller servlet sets up.  The <code>setUp()</code> method
 * establishes a consistent basic environment for the various tests.  The only
 * tests included in this class are simple validations that the basic
 * environment was set up correctly.</p>
 *
 * @version $Rev: 471754 $ $Date: 2005-08-14 17:24:39 -0400 (Sun, 14 Aug 2005)
 *          $
 */
public class TestMockBase extends TestCase {

    // ----------------------------------------------------- Instance Variables

    protected ModuleConfig moduleConfig = null;
    protected ModuleConfig moduleConfig2 = null;
    protected ModuleConfig moduleConfig3 = null;
    protected MockServletConfig config = null;
    protected MockServletContext context = null;
    protected MockPageContext page = null;
    protected MockPrincipal principal = null;
    protected MockHttpServletRequest request = null;
    protected MockHttpServletResponse response = null;
    protected MockHttpSession session = null;

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

    /**
     * @param name String
     */
    public TestMockBase(final String name) {
        super(name);
    }

    /**
     * @param args String[]
     */
    public static void main(final String[] args) {
        junit.textui.TestRunner.main(new String[] {TestMockBase.class.getName()});
    }

    /**
     * @return Test
     */
    public static Test suite() {
        return new TestSuite(TestMockBase.class);
    }

    // ----------------------------------------------------- Setup and Teardown

    /**
     * @see junit.framework.TestCase#setUp()
     */
    @Override
    public void setUp() {
        // Set up the servlet API objects for a test scenario
        this.context = new MockServletContext();
        this.config = new MockServletConfig(this.context);
        this.session = new MockHttpSession(this.context);
        this.request = new MockHttpServletRequest(this.session);
        this.principal =
            new MockPrincipal("username", new String[] {"admin", "manager"});
        this.request.setUserPrincipal(this.principal);
        this.response = new MockHttpServletResponse();
        this.page = new MockPageContext(this.config, this.request, this.response);

        // Set up application configurations for our supported modules
        setUpDefaultApp();
        setUpSecondApp();
        setUpThirdApp();

        // NOTE - we do not initialize the request attribute
        // for the selected module so that fallbacks to the
        // default module can be tested.  To select a module,
        // tests should set the request attribute Globals.MODULE_KEY
        // to the ModuleConfig instance for the selected module
    }

    protected void setUpDefaultApp() {

        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

        this.moduleConfig = factoryObject.createModuleConfig("");

        this.context.setAttribute(Globals.MODULE_KEY, this.moduleConfig);

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

        // Forward "foo" to "/bar.jsp"
        this.moduleConfig.addForwardConfig(new ActionForward("foo", "/bar.jsp", false));

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

        // Forward "relative2" to "relative.jsp" context-relative
        this.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");
        this.moduleConfig.addFormBeanConfig(formBean);

        // Action "/static" uses the "static" form bean in request scope
        ActionMapping mapping = new ActionMapping();
        mapping.setInput("/static.jsp");
        mapping.setName("static");
        mapping.setPath("/static");
        mapping.setScope("request");
        mapping.setType("org.apache.struts.mock.MockAction");
        this.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));
        this.moduleConfig.addFormBeanConfig(formBean);

        // Action "/dynamic" uses the "dynamic" form bean in session scope
        mapping = new ActionMapping();
        mapping.setInput("/dynamic.jsp");
        mapping.setName("dynamic");
        mapping.setPath("/dynamic");
        mapping.setScope("session");
        mapping.setType("org.apache.struts.mock.MockAction");
        this.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));
        this.moduleConfig.addFormBeanConfig(formBean);

        // Action "/dynamic0" uses the "dynamic0" form bean in request scope
        mapping = new ActionMapping();
        mapping.setName("dynamic0");
        mapping.setPath("/dynamic0");
        mapping.setScope("request");
        mapping.setType("org.apache.struts.mock.MockAction");
        this.moduleConfig.addActionConfig(mapping);

        // Action "/noform" has no form bean associated with it
        mapping = new ActionMapping();
        mapping.setPath("/noform");
        mapping.setType("org.apache.struts.mock.MockAction");
        this.moduleConfig.addActionConfig(mapping);

        // Configure global forward declarations
        // No redirect, same module
        this.moduleConfig.addForwardConfig(new ForwardConfig("moduleForward",
                "/module/forward", false));
        // Redirect, same module
        this.moduleConfig.addForwardConfig(new ForwardConfig("moduleRedirect",
                "/module/redirect", true));
        // No redirect Specify module
        this.moduleConfig.addForwardConfig(new ForwardConfig("contextForward",
                "/forward", false, "/context"));
        // Redirect Specify module
        this.moduleConfig.addForwardConfig(new ForwardConfig("contextRedirect",
                "/redirect", true, "/context"));
        // No redirect, same module
        this.moduleConfig.addForwardConfig(new ForwardConfig("moduleNoslash",
                "module/noslash", false));
        // No redirect Specify module
        this.moduleConfig.addForwardConfig(new ForwardConfig("contextNoslash",
                "noslash", false, "/context"));
    }

    protected void setUpSecondApp() {

        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

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

        this.context.setAttribute(Globals.MODULE_KEY + "/2", this.moduleConfig2);

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

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

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

        // Forward "relative2" to "relative.jsp" context-relative
        this.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");
        this.moduleConfig2.addFormBeanConfig(formBean);

        // Action "/static" uses the "static" form bean in request scope (same as default)
        ActionMapping mapping = new ActionMapping();
        mapping.setInput("/static.jsp");
        mapping.setName("static");
        mapping.setPath("/static");
        mapping.setScope("request");
        mapping.setType("org.apache.struts.mock.MockAction");
        this.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));
        this.moduleConfig2.addFormBeanConfig(formBean);

        // Action "/dynamic2" uses the "dynamic2" form bean in session scope
        mapping = new ActionMapping();
        mapping.setInput("/dynamic2.jsp");
        mapping.setName("dynamic2");
        mapping.setPath("/dynamic2");
        mapping.setScope("session");
        mapping.setType("org.apache.struts.mock.MockAction");
        this.moduleConfig2.addActionConfig(mapping);

        // Action "/noform" has no form bean associated with it (same as default)
        mapping = new ActionMapping();
        mapping.setPath("/noform");
        mapping.setType("org.apache.struts.mock.MockAction");
        this.moduleConfig2.addActionConfig(mapping);

        // Configure global forward declarations
        // No redirect, same module
        this.moduleConfig2.addForwardConfig(new ForwardConfig("moduleForward",
                "/module/forward", false));
        // Redirect, same module
        this.moduleConfig2.addForwardConfig(new ForwardConfig("moduleRedirect",
                "/module/redirect", true));
        // No redirect Specify module
        this.moduleConfig2.addForwardConfig(new ForwardConfig("contextForward",
                "/forward", false, "/context"));
        // Redirect Specify module
        this.moduleConfig2.addForwardConfig(new ForwardConfig("contextRedirect",
                "/redirect", true, "/context"));
        // No redirect, same module
        this.moduleConfig2.addForwardConfig(new ForwardConfig("moduleNoslash",
                "module/noslash", false));
        // No redirect Specify module
        this.moduleConfig2.addForwardConfig(new ForwardConfig("contextNoslash",
                "noslash", false, "/context"));
    }

    // Set up third app for testing URL mapping
    protected void setUpThirdApp() {
        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

        this.moduleConfig3 = factoryObject.createModuleConfig("/3");

        this.context.setAttribute(Globals.MODULE_KEY + "/3", this.moduleConfig3);

        // Instantiate the controller configuration for this app
        ControllerConfig controller = new ControllerConfig();

        this.moduleConfig3.setControllerConfig(controller);

        // Configure the properties we will be testing
        controller.setForwardPattern("/forwarding$M$P");
        controller.setInputForward(true);
        controller.setPagePattern("/paging$M$P");

        // Configure global forward declarations
        // No redirect, same module
        this.moduleConfig3.addForwardConfig(new ForwardConfig("moduleForward",
                "/module/forward", false));
        // Redirect, same module
        this.moduleConfig3.addForwardConfig(new ForwardConfig("moduleRedirect",
                "/module/redirect", true));
        // No redirect Specify module
        this.moduleConfig3.addForwardConfig(new ForwardConfig("contextForward",
                "/forward", false, "/context"));
        // Redirect Specify module
        this.moduleConfig3.addForwardConfig(new ForwardConfig("contextRedirect",
                "/redirect", true, "/context"));
        // No redirect, same module
        this.moduleConfig3.addForwardConfig(new ForwardConfig("moduleNoslash",
                "module/noslash", false));
        // No redirect specify module
        this.moduleConfig3.addForwardConfig(new ForwardConfig("contextNoslash",
                "noslash", false, "/context"));
    }

    /**
     * @see junit.framework.TestCase#tearDown()
     */
    @Override
    public void tearDown() {
        this.moduleConfig3 = null;
        this.moduleConfig2 = null;
        this.moduleConfig = null;
        this.config = null;
        this.context = null;
        this.page = null;
        this.principal = null;
        this.request = null;
        this.response = null;
        this.session = null;
    }

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

    /**
     * Test the basic setup of the mock object environment
     */
    public void testUtilBaseEnvironment() {
        // Validate the servlet API objects and inter-relationships
        assertNotNull("config is present", this.config);
        assertNotNull("context is present", this.context);
        assertNotNull("page is present", this.page);
        assertNotNull("principal is present", this.principal);
        assertNotNull("request is present", this.request);
        assertNotNull("response is present", this.response);
        assertNotNull("session is present", this.session);
        assertEquals("page-->config", this.config, this.page.getServletConfig());
        assertEquals("config-->context", this.context, this.config.getServletContext());
        assertEquals("page-->context", this.context, this.page.getServletContext());
        assertEquals("page-->request", this.request, this.page.getRequest());
        assertEquals("page-->response", this.response, this.page.getResponse());
        assertEquals("page-->session", this.session, this.page.getSession());
        assertEquals("request-->principal", this.principal, this.request.getUserPrincipal());
        assertEquals("request-->session", this.session, this.request.getSession());
        assertEquals("session-->context", this.context, this.session.getServletContext());

        // Validate the configuration for the default module
        assertNotNull("moduleConfig is present", this.moduleConfig);
        assertEquals("context-->moduleConfig", this.moduleConfig,
                this.context.getAttribute(Globals.MODULE_KEY));

        // Validate the configuration for the second module
        assertNotNull("moduleConfig2 is present", this.moduleConfig2);
        assertEquals("context-->moduleConfig2", this.moduleConfig2,
                this.context.getAttribute(Globals.MODULE_KEY + "/2"));
    }
}
