All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hp.octane.integrations.uft.MbtDiscoveryResultPreparerImpl Maven / Gradle / Ivy

There is a newer version: 2.24.3.5
Show newest version
package com.hp.octane.integrations.uft;

import com.hp.octane.integrations.dto.entities.Entity;
import com.hp.octane.integrations.dto.entities.EntityConstants;
import com.hp.octane.integrations.services.entities.EntitiesService;
import com.hp.octane.integrations.services.entities.QueryHelper;
import com.hp.octane.integrations.uft.items.AutomatedTest;
import com.hp.octane.integrations.uft.items.OctaneStatus;
import com.hp.octane.integrations.uft.items.UftTestAction;
import com.hp.octane.integrations.uft.items.UftTestDiscoveryResult;
import com.hp.octane.integrations.utils.SdkStringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Itay Karo on 26/08/2021
 */
public class MbtDiscoveryResultPreparerImpl implements DiscoveryResultPreparer {

    private static final Logger logger = LogManager.getLogger(MbtDiscoveryResultPreparerImpl.class);

    @Override
    public void prepareDiscoveryResultForDispatchInFullSyncMode(EntitiesService entitiesService, UftTestDiscoveryResult discoveryResult) {
        String condition = QueryHelper.conditionNot(QueryHelper.conditionEmpty(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD));
        List unitsFromServer = getUnitsFromServer(entitiesService, discoveryResult.getWorkspaceId(), condition);

        Map octaneUnitsMap = unitsFromServer.stream()
                .collect(Collectors.toMap(entity -> entity.getStringValue(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD).toLowerCase(),
                        action -> action,
                        (action1, action2) -> action1)); // remove duplicates TODO (Itay)- remove when repository path uniqueness validation will be added in octane

        removeExistingUnits(discoveryResult, octaneUnitsMap);
    }

    @Override
    public void prepareDiscoveryResultForDispatchInScmChangesMode(EntitiesService entitiesService, UftTestDiscoveryResult discoveryResult) {
        // prepare moved tests
        prepareMovedTests(discoveryResult);

        // handle deleted tests
        handleDeletedTests(entitiesService, discoveryResult);

        // handle added tests
        handleAddedTests(entitiesService, discoveryResult);

        // handle updated tests
        handleUpdatedTests(entitiesService, discoveryResult);

        // handle moved tests
        handleMovedTests(entitiesService, discoveryResult);

    }

    private List getUnitsFromServer(EntitiesService entitiesService, String workspaceId, String... conditions) {
        List fields = new ArrayList<>(Arrays.asList(EntityConstants.MbtUnit.ID_FIELD, EntityConstants.MbtUnit.NAME_FIELD, EntityConstants.MbtUnit.DESCRIPTION_FIELD, EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD));

        return entitiesService.getEntities(Long.parseLong(workspaceId), EntityConstants.MbtUnit.COLLECTION_NAME, Arrays.asList(conditions), fields);
    }

    private void removeExistingUnits(UftTestDiscoveryResult discoveryResult, Map octaneUnitsMap) {
        discoveryResult.getAllTests().forEach(automatedTest -> {
            automatedTest.getActions().forEach(action -> {
                if (Objects.nonNull(octaneUnitsMap.get(action.getRepositoryPath().toLowerCase()))) {
                    action.setOctaneStatus(OctaneStatus.NONE);
                }
            });
            removeItemsWithStatusNone(automatedTest.getActions());
        });

    }

    private void prepareMovedTests(UftTestDiscoveryResult result) {
        List newTests = result.getNewTests();
        List deletedTests = result.getDeletedTests();
        if (!newTests.isEmpty() && !deletedTests.isEmpty()) {
            logger.info("processing moved tests");
            Map dst2Test = newTests.stream()
                    .filter(automatedTest -> SdkStringUtils.isNotEmpty(automatedTest.getChangeSetDst()))
                    .collect(Collectors.toMap(AutomatedTest::getChangeSetDst, automatedTest -> automatedTest));

            Map deleted2newMovedTests = deletedTests.stream()
                    .filter(automatedTest -> SdkStringUtils.isNotEmpty(automatedTest.getChangeSetDst()) && dst2Test.containsKey(automatedTest.getChangeSetDst()))
                    .collect(Collectors.toMap(automatedTest -> automatedTest, automatedTest -> dst2Test.get(automatedTest.getChangeSetDst())));

            deleted2newMovedTests.forEach((deletedTest, newTest) -> {
                newTest.setIsMoved(true);
                newTest.setOldName(deletedTest.getName());
                newTest.setOldPackage(deletedTest.getPackage());
                newTest.setOctaneStatus(OctaneStatus.MODIFIED);

                result.getAllTests().remove(deletedTest);
            });

            logger.info("found {} tests that were moved", deleted2newMovedTests.size());
        }
    }

    // for deleted tests, we will not delete the relevant units. instead, we will reset some of their attributes
    private void handleDeletedTests(EntitiesService entitiesService, UftTestDiscoveryResult discoveryResult) {
        List deletedTests = discoveryResult.getDeletedTests();

        if (CollectionUtils.isEmpty(deletedTests)) {
            return;
        }

        logger.info("processing deleted tests. found {} tests", deletedTests.size());

        // create a condition for each test to fetch its units by the old test name
        String condition = QueryHelper.orConditions(deletedTests.stream()
                .map(test -> getActionPathPrefixCondition(test, false))
                .toArray(String[]::new));

        List unitsFromServer = getUnitsFromServer(entitiesService, discoveryResult.getWorkspaceId(), condition);

        // since the test was already deleted from the scm, the automated test will not contain any uft actions. so, we
        // need to map each unit from octane to the automated test and create a marker uft action with only the unit id
        // so later we will be able to update the unit entities
        deletedTests.forEach(automatedTest -> {
            String actionPathPrefix = UftTestDiscoveryUtils.getTestPathPrefix(automatedTest, false);
            // find all the unit entities that belong to this test
            List unitEntitiesOfTest = unitsFromServer.stream()
                    .filter(entity -> entity.getStringValue(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD).startsWith(actionPathPrefix))
                    .collect(Collectors.toList());
            unitsFromServer.removeAll(unitEntitiesOfTest);
            // convert unit entities to test actions
            List actions = unitEntitiesOfTest.stream().map(entity -> {
                UftTestAction action = convertToAction(entity);
                action.setOctaneStatus(OctaneStatus.DELETED);
                return action;
            }).collect(Collectors.toList());
            automatedTest.setActions(actions);
        });
    }

    private String getActionPathPrefixCondition(AutomatedTest test, boolean orgPath) {
        String actionPathPrefix = UftTestDiscoveryUtils.getTestPathPrefix(test, orgPath);
        return QueryHelper.conditionStartWith(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD, actionPathPrefix);
    }

    private void handleAddedTests(EntitiesService entitiesService, UftTestDiscoveryResult discoveryResult) {
        List newTests = discoveryResult.getNewTests();

        if (CollectionUtils.isEmpty(newTests)) {
            return;
        }

        logger.info("processing new tests. found {} tests", newTests.size());

        Set newActionsRepositoryPaths = newTests.stream()
                .filter(automatedTest -> !automatedTest.getIsMoved()) // not moved test
                .map(AutomatedTest::getActions)
                .flatMap(Collection::stream)
                .map(UftTestAction::getRepositoryPath)
                .collect(Collectors.toSet());

        String condition = QueryHelper.conditionIn(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD, newActionsRepositoryPaths, false);
        List unitsFromServer = getUnitsFromServer(entitiesService, discoveryResult.getWorkspaceId(), condition);

        Map octaneUnitsMap = unitsFromServer.stream()
                .collect(Collectors.toMap(entity -> entity.getStringValue(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD).toLowerCase(),
                        action -> action,
                        (action1, action2) -> action1)); // remove duplicates TODO (Itay)- remove when repository path uniqueness validation will be added in octane

        removeExistingUnits(discoveryResult, octaneUnitsMap);
    }

    private void handleUpdatedTests(EntitiesService entitiesService, UftTestDiscoveryResult discoveryResult) {
        List updatedTests = discoveryResult.getUpdatedTests().stream()
                .filter(automatedTest -> !automatedTest.getIsMoved())
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(updatedTests)) {
            return;
        }

        logger.info("processing updated tests. found {} tests", updatedTests.size());

        // there are 4 cases:
        // 1. new action- action will exist in the automated test but not in octane
        // 2. delete action- action will exist in octane but not in the automated test
        // 3. updated action- action will exist both in the automated test and in octane and will differ in the logical
        // name and/or description
        // 4. not modified action- action in the automated test is equal to the unit in octane

        Map scmPathToActionMap = updatedTests.stream().map(AutomatedTest::getActions)
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(action -> extractScmPathFromActionPath(action.getRepositoryPath()), action -> action));

        // create a condition for each test to fetch its units by the old test name
        String condition = QueryHelper.orConditions(updatedTests.stream()
                .map(test -> getActionPathPrefixCondition(test, false))
                .toArray(String[]::new));

        List unitsFromServer = getUnitsFromServer(entitiesService, discoveryResult.getWorkspaceId(), condition);
        Map scmPathToEntityMap = unitsFromServer.stream()
                .collect(Collectors.toMap(entity -> extractScmPathFromActionPath(entity.getStringValue(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD)),
                        action -> action,
                        (action1, action2) -> action1)); // remove duplicates TODO (Itay)- remove when repository path uniqueness validation will be added in octane

        handleUpdatedTestAddedActionCase(scmPathToActionMap, scmPathToEntityMap);

        handleUpdatedTestDeletedActionCase(scmPathToActionMap, scmPathToEntityMap, updatedTests);

        handleUpdatedTestUpdatedActionCase(scmPathToActionMap, scmPathToEntityMap);

        // just a validation
        if (!scmPathToActionMap.isEmpty() || !scmPathToEntityMap.isEmpty()) {
            logger.warn("not all of the existing units or actions were processed");
        }

    }

    private void handleMovedTests(EntitiesService entitiesService, UftTestDiscoveryResult discoveryResult) {
        List movedTests = discoveryResult.getUpdatedTests().stream()
                .filter(AutomatedTest::getIsMoved)
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(movedTests)) {
            return;
        }

        // create a condition for each test to fetch its units by the old test name
        String condition = QueryHelper.orConditions(movedTests.stream()
                .map(test -> getActionPathPrefixCondition(test, true))
                .toArray(String[]::new));

        List unitsFromServer = getUnitsFromServer(entitiesService, discoveryResult.getWorkspaceId(), condition);

        // now, we need to match between the original units and the automated test by the original action path prefix.
        // then, we will store a mapping between what the new action path should be and the unit id and update each unit
        // with the id and update the status to modified
        Map actionPathToUnitIdMap = new HashMap<>();
        movedTests.forEach(automatedTest -> {
            // match units from octane to automated test by the original repository path prefix
            String originalActionPathPrefix = UftTestDiscoveryUtils.getTestPathPrefix(automatedTest, true);
            String newActionPathPrefix = UftTestDiscoveryUtils.getTestPathPrefix(automatedTest, false);
            List entities = unitsFromServer.stream()
                    .filter(entity -> entity.getStringValue(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD).startsWith(originalActionPathPrefix))
                    .collect(Collectors.toList());

            // for each entity, replace the original repository path prefix with the new one
            entities.forEach(entity -> {
                String key = entity.getStringValue(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD).replace(originalActionPathPrefix, newActionPathPrefix);
                actionPathToUnitIdMap.put(key, entity.getId());
            });

            // remove the matched entities to remove duplications
            unitsFromServer.removeAll(entities);
        });

        // when we reach here all the units from octane should have been removed
        if (CollectionUtils.isNotEmpty(unitsFromServer)) {
            logger.warn("not all units from octane were mapped to moved tests");
        }

        // scan all moved tests and update the actions with the ids from octane
        movedTests.forEach(automatedTest -> automatedTest.getActions().forEach(action -> {
            action.setId(actionPathToUnitIdMap.get(action.getRepositoryPath()));
            action.setOctaneStatus(OctaneStatus.MODIFIED);
            action.setMoved(true);
            action.setOldTestName(automatedTest.getOldName());
            action.getParameters().forEach(parameter -> parameter.setOctaneStatus(OctaneStatus.NONE));
            removeItemsWithStatusNone(action.getParameters());
        }));
    }

    // the action path is in the form of \\:.
    // this method extracts the scm path (from the beginning until the ':' character)
    private String extractScmPathFromActionPath(String repositoryPath) {
        int index = repositoryPath.indexOf(":");
        if (index == -1) {
            return repositoryPath;
        } else {
            return repositoryPath.substring(0, index).toLowerCase();
        }
    }

    // handle case 1 for added actions
    private void handleUpdatedTestAddedActionCase(Map scmPathToActionMap, Map scmPathToEntityMap) {
        Collection addedActions = CollectionUtils.removeAll(scmPathToActionMap.keySet(), scmPathToEntityMap.keySet());
        if (CollectionUtils.isNotEmpty(addedActions)) {
            logger.info("found {} updated tests for added action", addedActions.size());

            addedActions.forEach(s -> {
                scmPathToActionMap.get(s).setOctaneStatus(OctaneStatus.NEW); // not required, just for readability
                scmPathToActionMap.remove(s);
            });
        }
    }

    // handle case 2 for deleted actions
    private void handleUpdatedTestDeletedActionCase(Map scmPathToActionMap, Map scmPathToEntityMap, List updatedTests) {
        Collection deletedActions = CollectionUtils.removeAll(scmPathToEntityMap.keySet(), scmPathToActionMap.keySet());
        if (CollectionUtils.isNotEmpty(deletedActions)) {
            Set updatedTestsCounter = new HashSet<>();
            deletedActions.forEach(s -> {
                String scmTestPath = extractScmTestPath(s);
                if (Objects.isNull(scmTestPath)) {
                    logger.warn("repository path {} of unit id {} name {} is not valid and will be discarded", s, scmPathToEntityMap.get(s).getId(), scmPathToEntityMap.get(s).getName());
                    scmPathToEntityMap.remove(s);
                } else {
                    // try to match between the automated test and the units to be deleted. since the action was already deleted
                    // from the scm, we need to update octane. the handling is the same as handling a deleted test. we need
                    // to mark the deleted actions and provide only the unit id
                    updatedTests.forEach(automatedTest -> {
                        String calculatedTestPath = UftTestDiscoveryUtils.getTestPathPrefix(automatedTest, false).toLowerCase();
                        // match found. add a marker action to the automated test
                        if (calculatedTestPath.equals(scmTestPath)) {
                            Entity entity = scmPathToEntityMap.get(s);
                            UftTestAction action = convertToAction(entity);
                            action.setOctaneStatus(OctaneStatus.DELETED);
                            automatedTest.getActions().add(action);
                            updatedTestsCounter.add(automatedTest);
                        }
                    });
                    scmPathToEntityMap.remove(s);

                    logger.info("found {} updated tests for deleted action", updatedTestsCounter.size());
                }
            });
        }
    }

    // handle case 3 for updated actions
    private void handleUpdatedTestUpdatedActionCase(Map scmPathToActionMap, Map scmPathToEntityMap) {
        // updated action candidates
        Collection sameActions = CollectionUtils.retainAll(scmPathToEntityMap.keySet(), scmPathToActionMap.keySet());
        if (CollectionUtils.isNotEmpty(sameActions)) {
            sameActions.forEach(scmPath -> {
                UftTestAction action = scmPathToActionMap.get(scmPath);
                // current functionality does not listen to name and description changes. in order to support this, uncomment
                // the section below
/*
                Entity entity = scmPathToEntityMap.get(scmPath);
                if(!isEquals(action, entity)) {
                    action.setId(entity.getId());
                    action.setOctaneStatus(OctaneStatus.MODIFIED);
                } else {
*/
                action.setOctaneStatus(OctaneStatus.NONE);
                action.getParameters().forEach(parameter -> parameter.setOctaneStatus(OctaneStatus.NONE)); // currently do not support parameter changes
                // }
                scmPathToActionMap.remove(scmPath);
                scmPathToEntityMap.remove(scmPath);
            });
        }
    }

    private boolean isEquals(UftTestAction action, Entity unitEntity) {
        String unitName = unitEntity.getStringValue(EntityConstants.MbtUnit.NAME_FIELD);
        String unitDescription = unitEntity.getStringValue(EntityConstants.MbtUnit.DESCRIPTION_FIELD);
        if (SdkStringUtils.isNotEmpty(unitDescription)) { // clear html tags from octane
            unitDescription = removeHtmlTagsFromString(unitDescription);
        }

        return !unitName.toLowerCase().contains(action.getName().toLowerCase()) && // action name was not action number like Action4
                action.getLogicalName().equalsIgnoreCase(unitName) &&
                ((SdkStringUtils.isEmpty(action.getDescription()) && SdkStringUtils.isEmpty(unitDescription)) ||
                        (SdkStringUtils.isNotEmpty(action.getDescription()) && SdkStringUtils.isNotEmpty(unitDescription) && action.getDescription().equalsIgnoreCase(unitDescription)));
    }

    // a valid path is of the form \\:. an invalid
    // form can be caused if a user manually entered an scm path in-correctly
    // this method extracts the test path from the repostory path. if not valid returns null
    private String extractScmTestPath(String scmPath) {
        scmPath = extractScmPathFromActionPath(scmPath);
        int index = scmPath.lastIndexOf('\\');
        if (index == -1) {
            return null;
        } else {
            String scmTestPath = scmPath.substring(0, index);
            String actionNumber = scmPath.substring(index + 1, scmPath.length() - 1);
            // the last part of the test path should contain the action name like "action10"
            if (actionNumber.startsWith("action")) {
                return scmTestPath;
            } else {
                return null;
            }
        }
    }

    private String removeHtmlTagsFromString(String str) {
        if (SdkStringUtils.isEmpty(str)) {
            return str;
        }

        str = str.replace("

", "\n"); return str.replaceAll("\\<.*?>", ""); } private UftTestAction convertToAction(Entity entity) { UftTestAction action = new UftTestAction(); action.setId(entity.getId()); action.setName(entity.getName()); action.setLogicalName(entity.getName()); action.setRepositoryPath(String.valueOf(entity.getField(EntityConstants.MbtUnit.REPOSITORY_PATH_FIELD))); return action; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy