package com.atlassian.bitbucket.installer;

import com.atlassian.bitbucket.Product;
import com.atlassian.bitbucket.installer.utils.InstallerUtils;
import com.atlassian.bitbucket.installer.validation.HardValidationException;
import com.atlassian.bitbucket.installer.validation.HomeDirectoryStructure;
import com.atlassian.bitbucket.installer.validation.PortValidator;
import com.atlassian.bitbucket.installer.validation.SoftValidationException;
import com.install4j.api.Util;
import com.install4j.api.context.InstallerContext;
import org.joda.time.DateTimeUtils;
import org.joda.time.LocalDateTime;
import org.junit.*;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import java.io.File;
import java.nio.file.Files;
import java.util.Locale;

import static com.atlassian.bitbucket.installer.InstallationType.*;
import static com.atlassian.bitbucket.installer.InstallerConstants.*;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.*;

public class InstallationDetailsTest {

    private static final String PRODUCT = Product.NAME;
    private static final String LOWER_PRODUCT = Product.NAME.toLowerCase(Locale.US);
    private static final String VERSION = "3.5.0";
    private static final DefaultDirectoryResolver defaultDirectoryResolver =
            new DefaultDirectoryResolver(Platform.current(), VERSION);

    @Rule
    public final MockitoRule mockitoInit = MockitoJUnit.rule();
    @Rule
    public final ExpectedException expectedException = ExpectedException.none();
    @Rule
    public final TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Mock
    private InstallerContext context;
    @Mock
    private PortValidator portValidator;
    @Mock
    private Tracker tracker;
    @Mock
    private InstallerUtils utils;

    @BeforeClass
    public static void setup() {
        DateTimeUtils.setCurrentMillisFixed(DateTimeUtils.currentTimeMillis());
    }

    @AfterClass
    public static void tearDown() {
        DateTimeUtils.setCurrentMillisSystem();
    }

    @Before
    public void setGlobalContext() {
        InstallationDetails.setContext(context);
    }

    @After
    public void clearGlobalContext() {
        InstallationDetails.setContext(null);
    }

    @Test
    public void testAdminDefaultsForLinux() throws Exception {
        assumeTrue("Testing Linux directory structure", Util.isLinux());
        String rootInstallDir = String.format("/opt/atlassian/%s/%s", LOWER_PRODUCT, VERSION);
        String rootDataDir = String.format("/var/atlassian/application-data/%s", LOWER_PRODUCT);
        testDefaults(rootInstallDir, rootDataDir, true);
    }

    @Test
    public void testAdminDefaultsForMacOS() throws Exception {
        assumeTrue("Testing Mac directory structure", Util.isMacOS());
        String rootInstallDir = String.format("%s/Atlassian/%s/%s", Util.getUserHome(), PRODUCT, VERSION);
        String rootDataDir = String.format("%s/Atlassian/ApplicationData/%s", Util.getUserHome(), PRODUCT);
        testDefaults(rootInstallDir, rootDataDir, true);
    }

    @Test
    public void testAdminDefaultsForWindows() throws Exception {
        assumeTrue("Testing Windows directory structure", Util.isWindows());
        String rootInstallDir = String.format("C:\\Atlassian\\%s\\%s", PRODUCT, VERSION);
        String rootDataDir = String.format("C:\\Atlassian\\ApplicationData\\%s", PRODUCT);
        testDefaults(rootInstallDir, rootDataDir, true);
    }

    @Test
    public void testRegularUserDefaultsForLinux() throws Exception {
        assumeTrue("Testing Linux directory structure", Util.isLinux());
        String rootInstallDir = String.format("%s/atlassian/%s/%s", Util.getUserHome(), PRODUCT.toLowerCase(), VERSION);
        String rootDataDir = String.format("%s/atlassian/application-data/%s", Util.getUserHome(), LOWER_PRODUCT);
        testDefaults(rootInstallDir, rootDataDir, false);
    }

    @Test
    public void testRegularUserDefaultsForMacOS() throws Exception {
        assumeTrue("Testing Mac directory structure", Util.isMacOS());
        String rootInstallDir = String.format("%s/Atlassian/%s/%s", Util.getUserHome(), PRODUCT, VERSION);
        String rootDataDir = String.format("%s/Atlassian/ApplicationData/%s", Util.getUserHome(), PRODUCT);
        testDefaults(rootInstallDir, rootDataDir, false);
    }

    @Test
    public void testRegularUserDefaultsForWindows() throws Exception {
        assumeTrue("Testing Windows directory structure", Util.isWindows());
        String rootInstallDir = String.format("%s\\Atlassian\\%s\\%s", Util.getUserHome(), PRODUCT, VERSION);
        String rootDataDir = String.format("%s\\Atlassian\\ApplicationData\\%s", Util.getUserHome(), PRODUCT);
        testDefaults(rootInstallDir, rootDataDir, false);
    }

    @Test
    public void testRegularUserCannotInstallService() throws Exception {
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_SERVICE_ADMIN);

        new InstallationDetails(VERSION, false).setInstallService(true);
    }

    @Test
    public void testShouldSetHomeDirToBitbucketHomeIfItExists() throws Exception {
        String homeDirPath = "/opt/atlassian/bitbucket";
        DefaultDirectoryResolver mockDirectoryResolver = mock(DefaultDirectoryResolver.class);

        File homeDir = mock(File.class);
        when(mockDirectoryResolver.resolveHomeDirectory(true)).thenReturn(homeDir);
        when(homeDir.exists()).thenReturn(true);
        when(homeDir.getAbsolutePath()).thenReturn(homeDirPath);

        InstallationDetails installationDetails =
                new InstallationDetails(mockDirectoryResolver, true, portValidator);
        when(context.getVariable(KEY_INSTALLATION_TYPE)).thenReturn(UPGRADE.name());
        when(context.getVariable(KEY_NEW_INSTALL)).thenReturn(false);

        installationDetails.initHomeDirectory();

        verify(context).setVariable(KEY_APP_BITBUCKET_HOME, homeDirPath);
        assertEquals(homeDir, installationDetails.getHomeDirectory());
    }

    @Test
    public void testShouldSetHomeDirToStashHomeIfItExistsAndBitbucketHomeDoesNotExist() throws Exception {
        String homeDirPath = "/opt/atlassian/stash";

        DefaultDirectoryResolver mockDirectoryResolver = mock(DefaultDirectoryResolver.class);

        File homeDir = mock(File.class);
        when(mockDirectoryResolver.resolveHomeDirectory(true)).thenReturn(homeDir);
        when(homeDir.exists()).thenReturn(false);

        File stashHomeDir = mock(File.class);
        when(stashHomeDir.exists()).thenReturn(true);
        when(stashHomeDir.getAbsolutePath()).thenReturn(homeDirPath);

        when(mockDirectoryResolver.resolveHomeDirectory(true, "Stash")).thenReturn(stashHomeDir);

        InstallationDetails installationDetails =
                new InstallationDetails(mockDirectoryResolver, true, portValidator);
        when(context.getVariable(KEY_INSTALLATION_TYPE)).thenReturn(UPGRADE.name());
        when(context.getVariable(KEY_NEW_INSTALL)).thenReturn(false);

        installationDetails.initHomeDirectory();

        verify(context).setVariable(KEY_APP_BITBUCKET_HOME, homeDirPath);
        assertEquals(stashHomeDir, installationDetails.getHomeDirectory());
    }

    @Test
    public void testShouldSetHomeDirToNullIfItBothStashAndBitbucketHomeDoNotExist() throws Exception {
        DefaultDirectoryResolver mockDirectoryResolver = mock(DefaultDirectoryResolver.class);

        File homeDir = mock(File.class);
        when(mockDirectoryResolver.resolveHomeDirectory(true)).thenReturn(homeDir);
        when(homeDir.exists()).thenReturn(false);

        File stashHomeDir = mock(File.class);
        when(stashHomeDir.exists()).thenReturn(false);
        when(mockDirectoryResolver.resolveHomeDirectory(true, "Stash")).thenReturn(stashHomeDir);

        InstallationDetails installationDetails =
                new InstallationDetails(mockDirectoryResolver, true, portValidator);
        when(context.getVariable(KEY_INSTALLATION_TYPE)).thenReturn(UPGRADE.name());
        when(context.getVariable(KEY_NEW_INSTALL)).thenReturn(false);

        installationDetails.initHomeDirectory();

        verify(context).setVariable(KEY_APP_BITBUCKET_HOME, "");
        assertNull(installationDetails.getHomeDirectory());
    }

    @Test
    public void testHomeDirectoryPathMustNotHaveSpaces() throws Exception {
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_DIRECTORY_SPACES);

        File directory = temporaryFolder.newFolder("path with spaces");
        new InstallationDetails(VERSION, false).setHomeDirectory(directory, false);
    }

    @Test
    public void testInstallationDirectoryPathMustNotHaveSpaces() throws Exception {
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_DIRECTORY_SPACES);

        File directory = temporaryFolder.newFolder("path with spaces");
        new InstallationDetails(VERSION, false).setInstallationDirectory(directory);
    }

    @Test
    public void testNewInstallationsMustUseEmptyInstallDirectory() throws Exception {
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_DIRECTORY_USED);

        temporaryFolder.newFile("thi_is_a_temp_file.txt");

        new InstallationDetails(VERSION, false).setInstallationDirectory(temporaryFolder.getRoot());
    }

    @Test
    public void testUpgradesMustUseEmptyInstallDirectory() throws Exception {
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_DIRECTORY_USED);

        temporaryFolder.newFile("this_is_a_temp_file.txt");

        InstallationDetails installation = new InstallationDetails(VERSION, false);
        installation.setInstallationDirectory(temporaryFolder.getRoot());
    }

    @Test
    public void testShouldAllowHomeDirectoryThatLooksSimilarToInstallDirectory() throws Exception {
        InstallationDetails installationDetails = new InstallationDetails(VERSION, false);
        File installDir = temporaryFolder.newFolder("name-of-dir");
        File homeDir = temporaryFolder.newFolder("name-of-dir-with-something-on-the-end");

        installationDetails.setInstallationDirectory(installDir);
        installationDetails.setHomeDirectory(homeDir, false);
    }

    @Test
    public void testShouldRejectHomeDirectoryContainedWithinInstallDirectory() throws Exception {
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_SUBDIRECTORY);

        InstallationDetails installationDetails = new InstallationDetails(VERSION, false);
        File installDir = temporaryFolder.newFolder("name-of-dir");
        File homeDir = new File(installDir, "containing-folder");

        installationDetails.setInstallationDirectory(installDir);
        installationDetails.setHomeDirectory(homeDir, false);
    }

    @Test
    public void testShouldRejectHomeDirectoryThatIsEqualToInstallDirectory() throws Exception {
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_SUBDIRECTORY);

        InstallationDetails installationDetails = new InstallationDetails(VERSION, false);
        File installDir = temporaryFolder.newFolder("name-of-dir");

        installationDetails.setInstallationDirectory(installDir);
        installationDetails.setHomeDirectory(installDir, false);
    }

    @Test
    public void testPortValidationFailure() throws Exception {
        setElasticsearchServiceInstall(true);
        expectedException.expect(HardValidationException.class);
        expectedException.expectMessage(HardValidationException.ERROR_GENERIC);
        doThrow(new HardValidationException(HardValidationException.ERROR_GENERIC)).when(portValidator)
                .validate(7990, 8006, true);

        new InstallationDetails(defaultDirectoryResolver, false, portValidator)
                .setApplicationPorts(7990, 8006, true);
    }

    @Test
    public void testPortValidationWithElasticsearchPortInUseRetainsPorts() {
        doThrow(new SoftValidationException(SoftValidationException.ERROR_PORT_ELASTICSEARCH_HTTP_USED)).when(portValidator)
                .validate(7980, 8006, true);

        InstallationDetails installationDetails = new InstallationDetails(defaultDirectoryResolver, false, portValidator);
        try {
            installationDetails.setApplicationPorts(7980, 8006, true);
        } catch (SoftValidationException e) {
            assertThat(e.getMessage(), is(SoftValidationException.ERROR_PORT_ELASTICSEARCH_HTTP_USED));
        }

        installationDetails.updateContext(context);

        verify(context).setVariable(KEY_HTTP_PORT, String.valueOf(7980));
    }

    @Test
    public void testInstallationId() throws Exception {
        String installationId =
                String.format("%s%s", Product.NAME, LocalDateTime.now().toString(
                        InstallationDetails.DATE_TIME_FORMATTER));

        assertEquals(installationId, new InstallationDetails(VERSION, false).getInstallationId());
    }

    @Test
    public void testUpdateContext() throws Exception {
        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, false, portValidator);

        File installationDirectory = temporaryFolder.newFolder("install");
        installationDetails.setInstallationDirectory(installationDirectory);

        File homeDirectory = temporaryFolder.newFolder("home");
        installationDetails.setHomeDirectory(homeDirectory, false);

        installationDetails.setInstallService(false);

        installationDetails.setApplicationPorts(7991, 8007, true);

        installationDetails.updateContext(context);

        String installationId = String.format("%s%s", Product.NAME, LocalDateTime.now().toString(
                InstallationDetails.DATE_TIME_FORMATTER));

        verify(context).setInstallationDirectory(installationDirectory);
        verify(context).setVariable(KEY_APP_BITBUCKET_HOME, homeDirectory.getAbsolutePath());
        verify(context).setVariable(KEY_HTTP_PORT, String.valueOf(7991));
        verify(context).setVariable(KEY_CONTROL_PORT, String.valueOf(8007));
        verify(context).setVariable(KEY_APP_INSTALL_SERVICE, false);
        verify(context).setVariable(KEY_INSTALLATION_ID, installationId);
    }

    @Test
    public void testShouldReturnFalseIfUserChooseNoAtLowPortNumber() throws Exception {
        assertTrue(testCheckUserSelectAndPortNumber(true, true, true));
        assertFalse(testCheckUserSelectAndPortNumber(true, true, false));

        assertTrue(testCheckUserSelectAndPortNumber(true, false, true));
        assertFalse(testCheckUserSelectAndPortNumber(true, false, false));

        assertTrue(testCheckUserSelectAndPortNumber(false, false, true));
        assertTrue(testCheckUserSelectAndPortNumber(false, false, false));
    }

    @Test
    public void testHomeDirectoryV1ValidationOnUpgrade() throws Exception {
        testHomeDirectoryValidationOnUpgrade(temporaryFolder.newFolder(), HomeDirectoryStructure.V1_0);
    }

    @Test
    public void testHomeDirectoryV2ValidationOnUpgrade() throws Exception {
        testHomeDirectoryValidationOnUpgrade(temporaryFolder.newFolder(), HomeDirectoryStructure.V2_0);
    }

    @Test
    public void testInvalidUpgradeHomeDirectory() throws Exception {
        expectedException.expect(HardValidationException.class);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);

        assertFalse(installationDetails.isUpgrade());
        setInstallationType(UPGRADE);

        File homeDirectory = temporaryFolder.newFolder();
        Files.createDirectories(new File(homeDirectory, "garbage-file").toPath());
        installationDetails.setHomeDirectory(homeDirectory, false);
    }

    @Test
    public void testEmptyUpgradeHomeDirectory() throws Exception {
        expectedException.expect(HardValidationException.class);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);

        assertFalse(installationDetails.isUpgrade());
        setInstallationType(UPGRADE);

        File homeDirectory = temporaryFolder.newFolder();
        installationDetails.setHomeDirectory(homeDirectory, false);
    }

    @Test
    public void testExtraItemsInUpgradeHomeDirectory() throws Exception {
        File homeDirectory = temporaryFolder.newFolder();
        Files.createDirectories(new File(homeDirectory, "garbage-file").toPath());
        testHomeDirectoryValidationOnUpgrade(homeDirectory, HomeDirectoryStructure.V2_0);
    }

    @Test
    public void testExistingUpgradeTypeParsedAsInstall() {
        set3xUpgradeType();
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfInstallType(installationDetails, true);
    }

    @Test
    public void testExistingInstallTypeParsedAsInstall() {
        set3xInstallType();
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfInstallType(installationDetails, true);
    }
    @Test
    public void testExistingBadTypeParsedAsInstall() {
        set3xBadType();
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfInstallType(installationDetails, true);
    }
    @Test
    public void testUpgradeTypeParsedAsUpgrade() {
        setInstallationType(UPGRADE);
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfUpgradeType(installationDetails);
    }

    @Test
    public void testInstallTypeParsedAsInstall() {
        setInstallationType(INSTALL);
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfInstallType(installationDetails, true);
    }

    @Test
    public void testDataCenterInstallTypeParsedAsDataCenterInstall() {
        setInstallationType(DATA_CENTER_INSTALL);
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfDataCenterInstallType(installationDetails);
    }

    @Test
    public void testDataCenterUpgradeTypeParsedAsDataCenterUpgrade() {
        setInstallationType(DATA_CENTER_UPGRADE);
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfDataCenterUpgradeType(installationDetails);
    }

    @Test
    public void testMirrorInstallTypeParsedAsMirrorInstall() {
        setInstallationType(MIRROR_INSTALL);
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfMirrorType(installationDetails);
    }

    @Test
    public void testMirrorUpgradeTypeParsedAsMirrorUpgrade() {
        setInstallationType(MIRROR_UPGRADE);
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        assertIsOfMirrorUpgradeType(installationDetails);
    }

    @Test
    public void testBadInstallTypeParsedAsInstall() {
        setInstallationType("badType");
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);

        assertIsOfInstallType(installationDetails, true);
    }

    @Test
    public void testElasticsearchInstallServiceFalsePreventsElasticsearchInstallation() {
        setInstallationType(INSTALL);
        setElasticsearchServiceInstall(false);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);

        installationDetails.updateContext(context);

        assertIsOfInstallType(installationDetails, false);
    }

    @Test
    public void testUpgradeInstallationTypeSetsUpgradeWhenWasUpgrade() {
        set3xUpgradeType();
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        installationDetails.upgradeInstallationType();

        verify(context).setVariable(KEY_INSTALLATION_TYPE, UPGRADE.name());
    }

    @Test
    public void testUpgradeInstallationTypeSetsInstallWhenWasInstall() {
        set3xInstallType();
        setElasticsearchServiceInstall(true);

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        installationDetails.upgradeInstallationType();

        verify(context).setVariable(KEY_INSTALLATION_TYPE, INSTALL.name());
    }

    @Test
    public void testTrackerCalledWithCorrectEvent() {
        //given
        setInstallationType(MIRROR_UPGRADE);
        InstallationDetails installationDetails =
                new InstallationDetailsWithMockedTracker(defaultDirectoryResolver, true, portValidator);

        //when
        installationDetails.trackInstallationType();

        verify(tracker).track(context, MIRROR_UPGRADE.getEventName());
    }

    @Test
    public void testUpgradeInstallationTypeSetsInstallWhenInvalid() {
        set3xInstallType();

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        installationDetails.upgradeInstallationType();

        verify(context).setVariable(KEY_INSTALLATION_TYPE, INSTALL.name());
    }

    @Test
    public void testUpgradeInstallationTypeIsNoOpWithNonInteger() {
        set3xType("someBadString");

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        installationDetails.upgradeInstallationType();

        verify(context, never()).setVariable(eq(KEY_INSTALLATION_TYPE), any());
    }

    @Test
    public void testGetSharedHomeDirectory() {
        File home = temporaryFolder.getRoot();
        File expectedShared = new File(home, "shared");

        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);
        installationDetails.setHomeDirectory(home, true);

        assertEquals(expectedShared, installationDetails.getHomeSharedDirectory());
    }

    private void testHomeDirectoryValidationOnUpgrade(File homeDirectory, HomeDirectoryStructure structure)
            throws Exception {
        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, true, portValidator);

        assertFalse(installationDetails.isUpgrade());
        setInstallationType(UPGRADE);
        assertTrue(installationDetails.isUpgrade());

        for (String folder : structure.getContents()) {
            Files.createDirectories(new File(homeDirectory, folder).toPath());
        }

        installationDetails.setHomeDirectory(homeDirectory, false);
    }

    private boolean testCheckUserSelectAndPortNumber(boolean portTooLow, boolean hasFullAdminRight,
                                                     boolean userSelection) {
        InstallationDetails installationDetails =
                new InstallationDetails(defaultDirectoryResolver, false, portValidator);
        installationDetails.setUtils(utils);

        when(portValidator.isPortNumberLow(any(Integer.class), any(Integer.class), any(Boolean.class)))
                .thenReturn(portTooLow);
        when(utils.showConfirmDialog(any(String.class), any(String.class), any(String.class), any(Boolean.class)))
                .thenReturn(userSelection);

        return installationDetails.setApplicationPorts(0, 0, hasFullAdminRight);
    }

    private void testDefaults(String installerRootDir, String homeRootDir, boolean isAdmin) {
        InstallationDetails installationDetails =
                new InstallationDetails(VERSION, isAdmin);

        File installationDirectory = new File(installerRootDir);
        assertEquals(installationDirectory, installationDetails.getInstallationDirectory());

        File homeDirectory = new File(homeRootDir);
        assertEquals(homeDirectory, installationDetails.getHomeDirectory());

        assertEquals(7990, installationDetails.getHttpPort());
        assertEquals(8006, installationDetails.getControlPort());

        assertEquals(isAdmin, installationDetails.isInstallService());
    }

    private void setInstallationType(InstallationType type) {
        if (type.isUpgrade()) {
            when(context.getVariable(InstallerConstants.KEY_NEW_INSTALL)).thenReturn(false);
        } else {
            when(context.getVariable(InstallerConstants.KEY_NEW_INSTALL)).thenReturn(true);
        }

        setInstallationType(type.name());
    }

    private void setInstallationType(String type) {
        when(context.getVariable(InstallerConstants.KEY_INSTALLATION_TYPE)).thenReturn(type);
    }

    private void set3xInstallType() {
        when(context.getVariable(InstallerConstants.KEY_INSTALLATION_TYPE)).thenReturn(0);
    }

    private void set3xUpgradeType() {
        when(context.getVariable(InstallerConstants.KEY_INSTALLATION_TYPE)).thenReturn(1);
    }

    private void set3xBadType() {
        when(context.getVariable(InstallerConstants.KEY_INSTALLATION_TYPE)).thenReturn(3);
    }

    private void set3xType(Object o) {
        when(context.getVariable(InstallerConstants.KEY_INSTALLATION_TYPE)).thenReturn(o);
    }

    private void setElasticsearchServiceInstall(boolean shouldInstallElasticsearch) {
        when(context.getVariable(InstallerConstants.KEY_ELASTICSEARCH_INSTALL_SERVICE)).thenReturn(shouldInstallElasticsearch);
        when(context.getBooleanVariable(InstallerConstants.KEY_ELASTICSEARCH_INSTALL_SERVICE)).thenReturn(shouldInstallElasticsearch);
    }

    private static void assertIsOfInstallType(InstallationDetails installationDetails, boolean shouldInstallElasticsearch) {
        assertEquals(INSTALL, installationDetails.getInstallType());
        assertTrue(installationDetails.isNewInstall());
        assertFalse(installationDetails.isUpgrade());
        assertTrue(installationDetails.isStandard());
        assertFalse(installationDetails.isDataCenter());
        assertFalse(installationDetails.isMirror());
        assertEquals(shouldInstallElasticsearch, installationDetails.shouldInstallBundledElasticsearch());
    }

    private static void assertIsOfUpgradeType(InstallationDetails installationDetails) {
        assertEquals(UPGRADE, installationDetails.getInstallType());
        assertFalse(installationDetails.isNewInstall());
        assertTrue(installationDetails.isUpgrade());
        assertTrue(installationDetails.isStandard());
        assertFalse(installationDetails.isDataCenter());
        assertFalse(installationDetails.isMirror());
        assertTrue(installationDetails.shouldInstallBundledElasticsearch());
    }

    private static void assertIsOfDataCenterInstallType(InstallationDetails installationDetails) {
        assertEquals(DATA_CENTER_INSTALL, installationDetails.getInstallType());
        assertTrue(installationDetails.isNewInstall());
        assertFalse(installationDetails.isUpgrade());
        assertFalse(installationDetails.isStandard());
        assertTrue(installationDetails.isDataCenter());
        assertFalse(installationDetails.isMirror());
        assertFalse(installationDetails.shouldInstallBundledElasticsearch());
    }

    private static void assertIsOfDataCenterUpgradeType(InstallationDetails installationDetails) {
        assertEquals(DATA_CENTER_UPGRADE, installationDetails.getInstallType());
        assertFalse(installationDetails.isNewInstall());
        assertTrue(installationDetails.isUpgrade());
        assertFalse(installationDetails.isStandard());
        assertTrue(installationDetails.isDataCenter());
        assertFalse(installationDetails.isMirror());
        assertFalse(installationDetails.shouldInstallBundledElasticsearch());
    }

    private static void assertIsOfMirrorType(InstallationDetails installationDetails) {
        assertEquals(MIRROR_INSTALL, installationDetails.getInstallType());
        assertTrue(installationDetails.isNewInstall());
        assertFalse(installationDetails.isUpgrade());
        assertFalse(installationDetails.isStandard());
        assertFalse(installationDetails.isDataCenter());
        assertTrue(installationDetails.isMirror());
        assertFalse(installationDetails.shouldInstallBundledElasticsearch());
    }

    private static void assertIsOfMirrorUpgradeType(InstallationDetails installationDetails) {
        assertEquals(MIRROR_UPGRADE, installationDetails.getInstallType());
        assertFalse(installationDetails.isNewInstall());
        assertTrue(installationDetails.isUpgrade());
        assertFalse(installationDetails.isStandard());
        assertFalse(installationDetails.isDataCenter());
        assertTrue(installationDetails.isMirror());
        assertFalse(installationDetails.shouldInstallBundledElasticsearch());
    }

    private class InstallationDetailsWithMockedTracker extends InstallationDetails {

        public InstallationDetailsWithMockedTracker(String version, boolean isAdmin) {
            super(version, isAdmin);
        }

        InstallationDetailsWithMockedTracker(DefaultDirectoryResolver defaultDirectoryResolver,
                                             boolean isAdmin, PortValidator portValidator) {
            super(defaultDirectoryResolver, isAdmin, portValidator);
        }

        @Override
        Tracker getTracker() {
            return tracker;
        }
    }
}
