/*
 * $Id: TestActionConfig.java 480593 2006-11-29 15:17:52Z niallp $
 *
 * 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.config;

import java.util.Map;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * Unit tests for the <code>org.apache.struts.config.ActionConfig</code>
 * class.  Currently only contains code to test the methods that support
 * configuration inheritance.
 *
 * @version $Rev: 480593 $ $Date: 2005-05-25 19:35:00 -0400 (Wed, 25 May 2005)
 *          $
 */
public class TestActionConfig {

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

    /**
     * The ModuleConfig we'll use.
     */
    private ModuleConfig config = null;

    /**
     * The common base we'll use.
     */
    private ActionConfig baseConfig = null;

    // ----------------------------------------------------------- Constructors

    // --------------------------------------------------------- Public Methods

    /**
     * Set up instance variables required by this test case.
     */
    @Before
    public void setUp() {
        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

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

        // setup the base form
        this.baseConfig = new ActionConfig();
        this.baseConfig.setPath("/base");
        this.baseConfig.setType("org.apache.struts.actions.DummyAction");

        // set up success and failure forward
        ForwardConfig forward = new ForwardConfig("success", "/success.jsp", false);

        this.baseConfig.addForwardConfig(forward);

        forward = new ForwardConfig("failure", "/failure.jsp", false);
        forward.setProperty("forwardCount", "10");
        this.baseConfig.addForwardConfig(forward);

        // setup an exception handler
        ExceptionConfig exceptionConfig = new ExceptionConfig();

        exceptionConfig.setType("java.sql.SQLException");
        exceptionConfig.setKey("msg.exception.sql");
        exceptionConfig.setProperty("exceptionCount", "10");
        this.baseConfig.addExceptionConfig(exceptionConfig);

        // set some arbitrary properties
        this.baseConfig.setProperty("label", "base");
        this.baseConfig.setProperty("version", "1a");

        // register it to our config
        this.config.addActionConfig(this.baseConfig);
    }

    /**
     * Tear down instance variables required by this test case.
     */
    @After
    public void tearDown() {
        this.config = null;
        this.baseConfig = null;
    }

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

    /**
     * Basic check that shouldn't detect circular inheritance.
     */
    @Test
    public void testCheckCircularInheritance() {
        ActionConfig child = new ActionConfig();

        child.setPath("/child");
        child.setExtends("/base");

        ActionConfig grandChild = new ActionConfig();

        grandChild.setPath("/grandChild");
        grandChild.setExtends("/child");

        this.config.addActionConfig(child);
        this.config.addActionConfig(grandChild);

        Assert.assertTrue("Circular inheritance shouldn't have been detected",
            !grandChild.checkCircularInheritance(this.config.getActionConfigs()));
    }

    /**
     * Basic check that should detect circular inheritance.
     */
    @Test
    public void testCheckCircularInheritanceError() {
        ActionConfig child = new ActionConfig();

        child.setPath("/child");
        child.setExtends("/base");

        ActionConfig grandChild = new ActionConfig();

        grandChild.setPath("/grandChild");
        grandChild.setExtends("/child");

        // establish the circular relationship with base
        this.baseConfig.setExtends("/grandChild");

        this.config.addActionConfig(child);
        this.config.addActionConfig(grandChild);

        Assert.assertTrue("Circular inheritance should've been detected",
            grandChild.checkCircularInheritance(this.config.getActionConfigs()));
    }

    /**
     * Test that processExtends() makes sure that a base action's own
     * extension has been processed.
     */
    @Test
    public void testProcessExtendsActionExtends() {
        CustomActionConfig first = new CustomActionConfig();

        first.setPath("/first");

        CustomActionConfig second = new CustomActionConfig();

        second.setPath("/second");
        second.setExtends("/first");

        this.config.addActionConfig(first);
        this.config.addActionConfig(second);

        // set baseConfig to extend second
        this.baseConfig.setExtends("/second");

        this.baseConfig.processExtends(this.config.getActionConfigs());

        Assert.assertTrue("The first action's processExtends() wasn't called",
            first.isProcessExtendsCalled());
        Assert.assertTrue("The second action's processExtends() wasn't called",
            second.isProcessExtendsCalled());
    }

    /**
     * Make sure that correct exception is thrown if a base action can't be
     * found.
     */
    @Test
    public void testProcessExtendsMissingAction() {
        this.baseConfig.setExtends("/someMissingAction");

        try {
            this.baseConfig.processExtends(this.config.getActionConfigs());
            Assert.fail("An exception should be thrown if a super form can't be found.");
        } catch (final NullPointerException e) {
            e.printStackTrace();
            // succeed
        }
    }

    /**
     * Test a typical form bean configuration extension where various forwards
     * and exception handlers should be inherited from a base form. This
     * method checks all the subelements.
     */
    @Test
    public void testInheritFrom() {
        // create a basic subform
        ActionConfig subConfig = new ActionConfig();
        String subConfigPath = "subConfig";

        subConfig.setPath(subConfigPath);
        subConfig.setExtends("/base");

        // override success
        ForwardConfig forward = new ForwardConfig();

        forward.setName("success");
        forward.setPath("/newSuccess.jsp");
        forward.setRedirect(true);
        subConfig.addForwardConfig(forward);

        // add an exception handler
        ExceptionConfig handler = new ExceptionConfig();

        handler.setType("java.lang.NullPointerException");
        handler.setKey("msg.exception.npe");
        subConfig.addExceptionConfig(handler);

        // override arbitrary "label" property
        subConfig.setProperty("label", "sub");

        this.config.addActionConfig(subConfig);

        subConfig.inheritFrom(this.baseConfig);

        // check that our subConfig is still the one in the config
        Assert.assertSame("subConfig no longer in ModuleConfig", subConfig,
                this.config.findActionConfig("subConfig"));

        // check our configured sub config
        Assert.assertNotNull("Action type was not inherited", subConfig.getType());
        Assert.assertEquals("Wrong config path", subConfigPath, subConfig.getPath());
        Assert.assertEquals("Wrong config type", this.baseConfig.getType(), subConfig.getType());

        testInheritFrom2(subConfig);
    }

    /**
     * testInheritFrom2
     * @param subConfig ActionConfig
     */
    private void testInheritFrom2(final ActionConfig subConfig) {
        // check our forwards
        ForwardConfig[] forwards = subConfig.findForwardConfigs();

        Assert.assertEquals("Wrong forwards count", 2, forwards.length);

        ForwardConfig forward = subConfig.findForwardConfig("success");
        Assert.assertNotNull("'success' forward was not found", forward);
        Assert.assertEquals("Wrong path for success", "/newSuccess.jsp", forward.getPath());

        forward = subConfig.findForwardConfig("failure");

        ForwardConfig origForward = this.baseConfig.findForwardConfig("failure");

        Assert.assertNotNull("'failure' forward was not inherited", forward);
        Assert.assertEquals("Wrong type for 'failure'", origForward.getPath(), forward.getPath());
        Assert.assertEquals("Arbitrary property not copied",
            origForward.getProperty("forwardCount"),
            forward.getProperty("forwardCount"));

        // check our exceptions
        ExceptionConfig[] handlers = subConfig.findExceptionConfigs();

        Assert.assertEquals("Wrong exception config count", 2, handlers.length);

        ExceptionConfig handler = subConfig.findExceptionConfig("java.sql.SQLException");

        ExceptionConfig origHandler =
                this.baseConfig.findExceptionConfig("java.sql.SQLException");

        Assert.assertNotNull("'SQLException' handler was not found", handler);
        Assert.assertEquals("Wrong key for 'SQLException'", origHandler.getKey(), handler.getKey());
        Assert.assertEquals("Arbitrary property not copied",
            origHandler.getProperty("exceptionCount"),
            handler.getProperty("exceptionCount"));

        handler = subConfig.findExceptionConfig("java.lang.NullPointerException");
        Assert.assertNotNull("'NullPointerException' handler disappeared", handler);

        // check the arbitrary properties
        String version = subConfig.getProperty("version");

        Assert.assertEquals("Arbitrary property 'version' wasn't inherited", "1a", version);

        String label = subConfig.getProperty("label");

        Assert.assertEquals("Arbitrary property 'label' shouldn't have changed", "sub", label);
    }

    /**
     * Make sure that correct exception is thrown if a base action can't be
     * found.
     */
    @Test
    public void testInheritBoolean() {

        ActionConfig parentConfig = new ActionConfig();
        parentConfig.setPath("/parent");

        // Test if boolean is NOT set it IS inherited
        parentConfig.setValidate(true);
        parentConfig.setCancellable(true);
        ActionConfig childConfig = new ActionConfig();
        childConfig.inheritFrom(parentConfig);
        Assert.assertTrue("default validate inherit true", childConfig.getValidate());
        Assert.assertTrue("default cancellable inherit true", childConfig.getValidate());

        // Test if boolean is NOT set it IS inherited
        parentConfig.setValidate(false);
        parentConfig.setCancellable(false);
        childConfig = new ActionConfig();
        childConfig.inheritFrom(parentConfig);
        Assert.assertTrue("default validate inherit false", !childConfig.getValidate());
        Assert.assertTrue("default cancellable inherit false", !childConfig.getValidate());

        // Test if boolean IS set it is NOT inherited
        parentConfig.setValidate(true);
        parentConfig.setCancellable(true);
        childConfig = new ActionConfig();
        childConfig.setValidate(false);
        childConfig.setCancellable(false);
        childConfig.inheritFrom(parentConfig);
        Assert.assertTrue("set validate (not inherit true)", !childConfig.getValidate());
        Assert.assertTrue("set cancellable (not inherit false)", !childConfig.getValidate());

        // Test if boolean IS set it is NOT inherited
        parentConfig.setValidate(false);
        parentConfig.setCancellable(false);
        childConfig = new ActionConfig();
        childConfig.setValidate(true);
        childConfig.setCancellable(true);
        childConfig.inheritFrom(parentConfig);
        Assert.assertTrue("set validate (not inherit false)", childConfig.getValidate());
        Assert.assertTrue("set cancellable (not inherit false)", childConfig.getValidate());

    }

    /**
     * Used to detect that ActionConfig is making the right calls.
     */
    public static class CustomActionConfig extends ActionConfig {

        /** serialVersionUID */
        private static final long serialVersionUID = -911680615295036296L;

        /** processExtendsCalled */
        private boolean processExtendsCalled = false;

        /**
         * @see org.apache.struts.config.ActionConfig#processExtends(java.util.Map)
         */
        @Override
        public void processExtends(final Map<String, ActionConfig> moduleConfig) {
            super.processExtends(moduleConfig);
            this.processExtendsCalled = true;
        }

        /**
         * @return processExtendsCalled
         */
        public boolean isProcessExtendsCalled() {
            return this.processExtendsCalled;
        }
    }
}
