/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2013 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library 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 library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.addon.test;

import junit.framework.Assert;
import org.ow2.jonas.addon.deploy.api.deployable.IAddonDeployable;
import org.ow2.jonas.addon.deploy.api.deployer.IAddonDeployer;
import org.ow2.jonas.addon.deploy.api.deployer.IAddonDeployerLog;
import org.ow2.jonas.addon.deploy.api.deployer.IConfDeployer;
import org.ow2.jonas.addon.deploy.api.util.IAddonLogEntry;
import org.ow2.jonas.addon.deploy.impl.deployer.AddonDeployerImpl;
import org.ow2.jonas.addon.deploy.impl.util.AddonDeployerLog;
import org.ow2.jonas.addon.deploy.impl.util.AddonUtil;
import org.ow2.util.ee.deploy.api.deployable.IDeployable;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.testng.annotations.Test;

import java.io.File;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;

/**
 * Log and addon's state test cases
 * @author Jeremy Cazaux
 */
public class LogAndStateTestCase extends BaseTestCase {

    @Test
    public void testThatLogAndStateAreConsistentWithAddonOperations() throws Exception {

        //get the deployable
        IDeployable deployable = getDeployable(BasicTestCase.SAMPLE_ADDON);

        //install the addon
        IAddonDeployable installed = IAddonDeployable.class.cast(this.deployer.install(deployable, false));

        //check the addon log file
        IAddonDeployerLog addonLog = getNewAddonDeployerLogInstance();
        Assert.assertEquals(addonLog.getEntries().size(), 1);
        IAddonLogEntry logEntry = IAddonLogEntry.class.cast(addonLog.getEntries().get(0));
        Assert.assertEquals(BasicTestCase.SAMPLE_ADDON.getAbsoluteFile(), logEntry.getOriginal());
        Assert.assertTrue(logEntry.getOriginal().exists());
        Assert.assertTrue(logEntry.getCopy().exists());
        checkLogState(addonLog, BasicTestCase.SAMPLE_ADDON, new File(installed.getArchive().getURL().getFile()),
                BasicTestCase.SAMPLE_NAME, IAddonDeployable.INSTALLED);

        //check the state
        Assert.assertEquals(installed.getState(), IAddonDeployable.INSTALLED);


        //call the resolve operation
        this.deployer.resolve();

        //check the state
        Assert.assertEquals(installed.getState(), IAddonDeployable.RESOLVED);

        //check the addon log file
        addonLog = getNewAddonDeployerLogInstance();
        Assert.assertEquals(addonLog.getEntries().size(), 1);
        checkLogState(addonLog, BasicTestCase.SAMPLE_ADDON, new File(installed.getArchive().getURL().getFile()),
                BasicTestCase.SAMPLE_NAME, IAddonDeployable.RESOLVED);


        //start the addon
        this.deployer.start(BasicTestCase.SAMPLE_NAME);

        //check the state
        Assert.assertEquals(installed.getState(), IAddonDeployable.ACTIVE);

        //check the addon log file
        addonLog = getNewAddonDeployerLogInstance();
        Assert.assertEquals(addonLog.getEntries().size(), 1);
        checkLogState(addonLog, BasicTestCase.SAMPLE_ADDON, new File(installed.getArchive().getURL().getFile()),
                BasicTestCase.SAMPLE_NAME, IAddonDeployable.ACTIVE);


        //stop the addon
        this.deployer.stop(BasicTestCase.SAMPLE_NAME);

        //check the state
        Assert.assertEquals(installed.getState(), IAddonDeployable.RESOLVED);

        //check the addon log file
        addonLog = getNewAddonDeployerLogInstance();
        Assert.assertEquals(addonLog.getEntries().size(), 1);
        checkLogState(addonLog, BasicTestCase.SAMPLE_ADDON, new File(installed.getArchive().getURL().getFile()),
                BasicTestCase.SAMPLE_NAME, IAddonDeployable.RESOLVED);


        //install a second addon
        IAddonDeployable installed2 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_1), false));

        //check the addon log file
        addonLog = getNewAddonDeployerLogInstance();
        Assert.assertEquals(addonLog.getEntries().size(), 2);
        checkLogState(addonLog, BasicTestCase.SAMPLE_ADDON, new File(installed.getArchive().getURL().getFile()),
                BasicTestCase.SAMPLE_NAME, IAddonDeployable.RESOLVED);
        checkLogState(addonLog, ResolverTestCase.ADDON_1, new File(installed2.getArchive().getURL().getFile()),
                ResolverTestCase.ADDON_1_NAME, IAddonDeployable.INSTALLED);


        //uninstall the first addon
        this.deployer.uninstall(BasicTestCase.SAMPLE_NAME);

        //check the state
        Assert.assertEquals(installed.getState(), IAddonDeployable.UNINSTALLED);

        //check the addon log file
        addonLog = getNewAddonDeployerLogInstance();
        Assert.assertEquals(addonLog.getEntries().size(), 1);


        //uninstall the second addon
        this.deployer.uninstall(ResolverTestCase.ADDON_1_NAME);

        //check the state
        Assert.assertEquals(installed2.getState(), IAddonDeployable.UNINSTALLED);

        //check the addon log file
        addonLog = getNewAddonDeployerLogInstance();
        Assert.assertEquals(addonLog.getEntries().size(), 0);
    }

    private void checkLogState(final IAddonDeployerLog addonLog, final File original, final File copy, final String name,
                               int state) throws Exception {
        //check the addon log in memory
        IAddonLogEntry logEntry = IAddonLogEntry.class.cast(addonLog.getEntry(original.getAbsoluteFile()));
        Assert.assertNotNull(logEntry);
        Assert.assertEquals(logEntry.getState(), state);
        Assert.assertEquals(logEntry.getName(), name);
        Assert.assertEquals(logEntry.getOriginal(), original.getAbsoluteFile());
        Assert.assertEquals(logEntry.getCopy(), copy.getAbsoluteFile());
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatWeCannotStartAnAddonWithAnUninstalledState() throws Exception {
        this.deployer.start(BasicTestCase.SAMPLE_NAME);
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatWeCannotStopAnAddonWithAnUninstalledState() throws Exception {
        this.deployer.stop(BasicTestCase.SAMPLE_NAME);
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatWeCannotUndeployAnAddonWithAnUninstalledState() throws Exception {
        this.deployer.undeploy(getDeployable(BasicTestCase.SAMPLE_ADDON));
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatWeCannotUninstallAnAddonWithAnActiveState() throws Exception {
        IAddonDeployable addon = getDeployable(BasicTestCase.SAMPLE_ADDON);
        this.deployer.install(addon, false);
        this.deployer.start(BasicTestCase.SAMPLE_NAME);
        this.deployer.uninstall(BasicTestCase.SAMPLE_NAME);
    }

    public IAddonDeployerLog getNewAddonDeployerLogInstance() throws Exception {
        return new AddonDeployerLog(new File(AddonUtil.getAddonLogFile(this.serverProperties)));
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatTheStateOfAnAddonIsStillEqualsToUninstallIfTheInstallOperationHasFailed() throws Exception {
        this.confDeployer = mock(IConfDeployer.class);
        IAddonDeployer deployer = getAddonDeployerInstance();

        doThrow(DeployerException.class).when(this.confDeployer).install(any(IAddonDeployable.class));

        int state = IAddonDeployable.UNINSTALLED;
        try {
            state = (IAddonDeployable.class.cast(deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false))).getState();
        } finally {
            Assert.assertEquals(0, this.deployerLog.getEntries().size());
            Assert.assertEquals(IAddonDeployable.UNINSTALLED, state);
        }
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatTheStateOfAnAddonIsStillEqualsToInstalledIfThereIsAtLeastOneRequirementWhichIsNotSatisfied() throws Exception {
        //mock the addon deployer
        AddonDeployerImpl deployer = spy(AddonDeployerImpl.class.cast(getAddonDeployerInstance()));
        doThrow(DeployerException.class).when(deployer).checkRequirements(any(IAddonDeployable.class), eq(false));

        IAddonDeployable addon = null;
        try {
            //install the addon
            addon = IAddonDeployable.class.cast(deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));
            Assert.assertEquals(IAddonDeployable.INSTALLED, addon.getState());

            //start the addon
            deployer.start(addon.getMetadata().getName());
        }  finally {
            Assert.assertEquals(IAddonDeployable.INSTALLED, addon.getState());
            Assert.assertEquals(IAddonDeployable.INSTALLED, IAddonLogEntry.class.cast(this.deployerLog.getEntries().get(0)).getState());
        }
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatTheStateOfAnAddonIsEqualsToResolvedIfTheStartOperationHasFailedAndIfThereIsNoRequirementsIssue() throws Exception {
        //mock the configuration deployer and build the addon deployer
        this.confDeployer = mock(IConfDeployer.class);
        IAddonDeployer deployer = getAddonDeployerInstance();

        //install the addon
        IAddonDeployable addon = IAddonDeployable.class.cast(deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));

        //no requirements issue
        deployer.resolve();
        int state = addon.getState();
        Assert.assertEquals(IAddonDeployable.RESOLVED, state);

        doThrow(DeployerException.class).when(this.confDeployer).start(anyString());

        //start the addon
        try {
            deployer.start(addon.getMetadata().getName());
        } finally {
            Assert.assertEquals(IAddonDeployable.RESOLVED, addon.getState());
            Assert.assertEquals(IAddonDeployable.RESOLVED, IAddonLogEntry.class.cast(this.deployerLog.getEntries().get(0)).getState());
        }
    }

    @Test
    public void testThatTheStateOfTheAddonIsEqualsToResolvedIfTheStopOperationHasFailedAndIfThereIsNoRequirementsIssues() throws Exception {
        //mock the configuration deployer and build the addon deployer
        this.confDeployer = mock(IConfDeployer.class);
        IAddonDeployer deployer = getAddonDeployerInstance();

        //install the addon
        IAddonDeployable addon = IAddonDeployable.class.cast(deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));

        //start the addon
        deployer.start(addon.getMetadata().getName());

        //no requirements issue
        Assert.assertEquals(IAddonDeployable.ACTIVE, addon.getState());

        doThrow(DeployerException.class).when(this.confDeployer).stop(anyString());

        //stop the addon
        try {
            deployer.stop(addon.getMetadata().getName());
        } finally {
            Assert.assertEquals(IAddonDeployable.RESOLVED, addon.getState());
            Assert.assertEquals(IAddonDeployable.RESOLVED, IAddonLogEntry.class.cast(this.deployerLog.getEntries().get(0)).getState());
        }
    }

    @Test
    public void testThatTheStateOfTheAddonIsEqualsToUninstallIfTheUninstallOperationHasFailed() throws Exception {
        //mock the configuration deployer and build the addon deployer
        this.confDeployer = mock(IConfDeployer.class);
        IAddonDeployer deployer = getAddonDeployerInstance();

        //install the addon
        IAddonDeployable addon = IAddonDeployable.class.cast(deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));

        //start the addon
        deployer.start(addon.getMetadata().getName());

        //stop the addon
        deployer.stop(addon.getMetadata().getName());
        Assert.assertEquals(IAddonDeployable.RESOLVED, IAddonLogEntry.class.cast(this.deployerLog.getEntries().get(0)).getState());

        doThrow(DeployerException.class).when(this.confDeployer).uninstall(anyString());

        //uninstall the addon
        try {
            deployer.uninstall(addon.getMetadata().getName());
        } finally {
            Assert.assertEquals(IAddonDeployable.UNINSTALLED, addon.getState());
            Assert.assertEquals(0, deployerLog.getEntries().size());
        }
    }
}


