/** * Copyright (C) 2015-2019 Expedia, Inc. * * Licensed 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 com.hotels.heat.core.handlers; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.hotels.heat.core.checks.BasicMultipleChecks; import com.hotels.heat.core.specificexception.HeatException; import com.hotels.heat.core.utils.DataExtractionSupport; import com.hotels.heat.core.utils.log.LoggingUtils; import com.hotels.heat.core.validations.ArithmeticalValidator; import com.hotels.heat.core.validations.StringValidator; import io.restassured.response.Response; /** * Base utility for operations inside test checks. */ public class OperationHandler { public static final String OPERATION_JSON_ELEMENT = "operation"; public static final String FORMAT_OF_TYPE_CHECK_JSON_ELEMENT = "formatOfTypeCheck"; public static final String JSON_ELEM_DESCRIPTION = "description"; public static final String JSON_ELEM_EXPECTED_VALUE = "expectedValue"; public static final String JSON_ELEM_ACTUAL_VALUE = "actualValue"; public static final String JSON_ELEM_REFERRING_OBJECT = "referringObjectName"; private ArithmeticalValidator aritmeticalValidator; private StringValidator stringValidator; private String checkDescription; private boolean isBlocking = true; private final LoggingUtils logUtils; private Object responses; private DataExtractionSupport dataExtractionSupport; private final AssertionHandler assertionHandler; private Map fieldsToCheck; private Map<Integer, Map<String, String>> retrievedParameters; /** * Handler for all possible operations (single mode, compare mode, flow mode). * @param fieldsToCheck it is the map representing the single check block from the json input file. * @param response it is the response retrieved after the request to the service under test */ public OperationHandler(Map fieldsToCheck, Object response) { this.retrievedParameters = new HashMap(); this.logUtils = TestSuiteHandler.getInstance().getLogUtils(); this.responses = response; this.dataExtractionSupport = new DataExtractionSupport(this.logUtils); this.fieldsToCheck = fieldsToCheck; this.assertionHandler = new AssertionHandler(); } /** * Executes an operation of a single Check. * @return the outcome of the exectution */ public boolean execute() { boolean isExecutionOk = true; if (!fieldsToCheck.containsKey(JSON_ELEM_ACTUAL_VALUE) || !fieldsToCheck.containsKey(JSON_ELEM_EXPECTED_VALUE)) { throw new HeatException("json input format not supported! '" + fieldsToCheck.toString() + "'"); } if (isComparingBlock()) { isExecutionOk &= multipleModeOperationExecution(); } else { isExecutionOk &= singleModeOperationExecution(); } return isExecutionOk; } private boolean isComparingBlock() { return fieldsToCheck.get(JSON_ELEM_ACTUAL_VALUE) instanceof Map && ((Map) fieldsToCheck.get(JSON_ELEM_ACTUAL_VALUE)).containsKey(JSON_ELEM_ACTUAL_VALUE) && ((Map) fieldsToCheck.get(JSON_ELEM_EXPECTED_VALUE)).containsKey(JSON_ELEM_ACTUAL_VALUE); } private boolean multipleModeOperationExecution() { boolean isExecutionOk = false; String operation = loadOperationToExecuteOrDefault(ArithmeticalValidator.MATH_OPERATOR_EQUAL_TO); String fieldCheckFormat = loadFieldCheckFormatOrDefault(BasicMultipleChecks.DEFAULT_FORMAT_OF_TYPE_CHECK); String firstObj = retrieveObj(JSON_ELEM_ACTUAL_VALUE, fieldsToCheck, (Map<String, Response>) responses); String secondObj = retrieveObj(JSON_ELEM_EXPECTED_VALUE, fieldsToCheck, (Map<String, Response>) responses); checkDescription = fieldsToCheck.get(JSON_ELEM_DESCRIPTION).toString(); isExecutionOk = mathOrStringChecks(operation, firstObj, secondObj, fieldCheckFormat); return isExecutionOk; } private String retrieveObj(String objName, Map<String, Object> singleCheckMap, Map<String, Response> mapServiceIdResponse) { Map<String, Object> objMapRetrieved = (Map<String, Object>) singleCheckMap.get(objName); String strRetrieved = ""; try { if (objMapRetrieved.containsKey(JSON_ELEM_ACTUAL_VALUE)) { if (objMapRetrieved.containsKey(JSON_ELEM_REFERRING_OBJECT)) { Response rsp = mapServiceIdResponse.get(objMapRetrieved.get(JSON_ELEM_REFERRING_OBJECT).toString()); DataExtractionSupport dataSupport = new DataExtractionSupport(this.logUtils); strRetrieved = dataSupport.process(objMapRetrieved.get(JSON_ELEM_ACTUAL_VALUE), rsp, retrievedParameters); } else { if (!objMapRetrieved.get(JSON_ELEM_ACTUAL_VALUE).toString().contains(PlaceholderHandler.PLACEHOLDER_SYMBOL_BEGIN)) { strRetrieved = objMapRetrieved.get(JSON_ELEM_ACTUAL_VALUE).toString(); } else { throw new HeatException(this.logUtils.getExceptionDetails() + "Input Json does not contain 'referringObjectName' field"); } } } else { throw new HeatException(this.logUtils.getExceptionDetails() + "Input Json does not contain 'actualValue' field"); } } catch (Exception oEx) { throw new HeatException(this.logUtils.getExceptionDetails() + "Exception occurred: '" + oEx.getLocalizedMessage() + "'"); } return strRetrieved; } private boolean singleModeOperationExecution() { boolean isExecutionOk = false; Class<?> aClass = fieldsToCheck.get(JSON_ELEM_EXPECTED_VALUE).getClass(); if (aClass.equals(String.class) || aClass.equals(HashMap.class)) { String expectedValue = dataExtractionSupport.process(fieldsToCheck.get(JSON_ELEM_EXPECTED_VALUE), (Response) responses, retrievedParameters); if (PlaceholderHandler.PLACEHOLDER_PRESENT.equals(expectedValue) || PlaceholderHandler.PLACEHOLDER_NOT_PRESENT.equals(expectedValue)) { isExecutionOk = checkJsonPathPresence(expectedValue); } else { Object actualValue = fieldsToCheck.get(JSON_ELEM_ACTUAL_VALUE); isExecutionOk = checkGenericFields(actualValue, expectedValue); } } else if (aClass.equals(ArrayList.class)) { isExecutionOk = containsCheck(); } return isExecutionOk; } private boolean checkGenericFields(Object actualValue, String expectedValue) { boolean isExecutionOk; String processedActualValue = dataExtractionSupport.process(actualValue, (Response) responses, retrievedParameters); String operationToExecute = loadOperationToExecuteOrDefault(StringValidator.STRING_OPERATOR_EQUALS_TO); loadCheckDescription(); String operationDescription = checkDescription + " --> '" + processedActualValue + "' '" + operationToExecute + "' '" + expectedValue + "'"; this.logUtils.debug("{}: actualValue '{}' (processed '{}') / operation '{}' / expectedValue '{}'", checkDescription, actualValue, processedActualValue, operationToExecute, expectedValue); try { String fieldCheckFormat = loadFieldCheckFormatOrDefault("int"); isExecutionOk = mathOrStringChecks(operationToExecute, processedActualValue, expectedValue, fieldCheckFormat); } catch (Exception oEx) { logUtils.error("Exception: class {}, cause {}, message {}", oEx.getClass(), oEx.getCause(), oEx.getLocalizedMessage()); operationDescription = "<" + operationDescription + ">"; throw new HeatException(logUtils.getExceptionDetails() + "It is not possible to execute the check " + operationDescription); } return isExecutionOk; } private void loadCheckDescription() { if (fieldsToCheck.containsKey(JSON_ELEM_DESCRIPTION)) { checkDescription = "{" + (String) fieldsToCheck.get(JSON_ELEM_DESCRIPTION) + "}"; } } private boolean mathOrStringChecks(String operationToExecute, String processedActualValue, String expectedValue, String fieldCheckFormat) { boolean isExecutionOk; if (isItMathematicalCheck(operationToExecute)) { aritmeticalValidator = new ArithmeticalValidator(logUtils); isExecutionOk = aritmeticalValidator.mathematicalChecks(isBlocking, operationToExecute, processedActualValue, expectedValue, checkDescription, fieldCheckFormat); } else { stringValidator = new StringValidator(logUtils); isExecutionOk = stringValidator.stringEqualChecks(isBlocking, operationToExecute, processedActualValue, expectedValue, logUtils.getTestCaseDetails() + " " + checkDescription + " -->"); } return isExecutionOk; } private boolean containsCheck() { boolean isExecutionOk; // TODO: verify if the cast to Response is correct. Maybe it is unuseful to pass it with the cast... maybe it can be simply not passed String actualValue = dataExtractionSupport.process(fieldsToCheck.get(JSON_ELEM_ACTUAL_VALUE), (Response) responses, retrievedParameters); String operationToExecute = loadOperationToExecuteOrDefault(StringValidator.STRING_OPERATOR_CONTAINS); // get each element of the array and placeholderProcessString it with the placeholderProcessString handler List<String> expectedElementList = (List<String>) fieldsToCheck.get(JSON_ELEM_EXPECTED_VALUE); List<String> processedList = new ArrayList(); PlaceholderHandler placeholderHandler = new PlaceholderHandler(); placeholderHandler.setResponse((Response) responses); // TODO: bisogna passare al placeholder la response!!!!! expectedElementList.forEach(listElement -> { processedList.add((String) placeholderHandler.placeholderProcessString(listElement)); }); loadCheckDescription(); String assertionString = checkDescription + "-->"; boolean isContainsCheckOk = true; switch (operationToExecute) { case StringValidator.STRING_OPERATOR_CONTAINS: for (String element : processedList) { logUtils.debug("{} actualValue ('{}') has to contain '{}'", assertionString, actualValue, element); isContainsCheckOk = isContainsCheckOk && assertionHandler.assertion(false, "assertTrue", "actualValue ('" + actualValue + "') has to contain '" + element + "'", actualValue.contains(element)); } isExecutionOk = assertionHandler.assertion(isBlocking, "assertTrue", assertionString, isContainsCheckOk); break; case StringValidator.STRING_OPERATOR_NOT_CONTAINS: for (String element : processedList) { logUtils.debug("{} actualValue ('{}') has not to contain '{}'", assertionString, actualValue, element); isContainsCheckOk = isContainsCheckOk && assertionHandler.assertion(false, "assertFalse", "actualValue ('" + actualValue + "') has not to contain '" + element + "'", actualValue.contains(element)); } isExecutionOk = assertionHandler.assertion(isBlocking, "assertTrue", assertionString, isContainsCheckOk); break; default: isExecutionOk = false; logUtils.error("Unsupported operation in case of expected values as array"); throw new HeatException("Unsupported operation in case of expected values as array"); } return isExecutionOk; } private String loadOperationToExecuteOrDefault(String defaultValue) { String operationToExecute = defaultValue; if (fieldsToCheck.containsKey(OPERATION_JSON_ELEMENT)) { operationToExecute = (String) fieldsToCheck.get(OPERATION_JSON_ELEMENT); } return operationToExecute; } private String loadFieldCheckFormatOrDefault(String defaultValue) { String fieldFormat = defaultValue; if (fieldsToCheck.containsKey(FORMAT_OF_TYPE_CHECK_JSON_ELEMENT)) { fieldFormat = fieldsToCheck.get(FORMAT_OF_TYPE_CHECK_JSON_ELEMENT).toString(); } return fieldFormat; } private boolean checkJsonPathPresence(String expectedValue) { boolean isExecutionOk = false; String actualValue = fieldsToCheck.get(JSON_ELEM_ACTUAL_VALUE).toString(); // the check can be executed ONLY if the actual value is a "${path"-style placeholder if (actualValue.contains(PlaceholderHandler.PATH_PLACEHOLDER)) { PlaceholderHandler placeholderHandler = TestSuiteHandler.getInstance().getEnvironmentHandler().getPlaceholderHandler(); placeholderHandler.setResponse((Response) responses); String jsonPathResponse = placeholderHandler.getPathVar(actualValue); this.logUtils.debug("The output of jsonPath: '{}' is {}", actualValue, jsonPathResponse); boolean isPathPresent = jsonPathResponse != null && !jsonPathResponse.isEmpty(); if (PlaceholderHandler.PLACEHOLDER_NOT_PRESENT.equals(expectedValue)) { isExecutionOk = assertionHandler.assertion(isBlocking, "assertFalse", logUtils.getTestCaseDetails() + "json path '" + actualValue + "' has not to be present ", isPathPresent); } else if (PlaceholderHandler.PLACEHOLDER_PRESENT.equals(expectedValue)) { isExecutionOk = assertionHandler.assertion(isBlocking, "assertTrue", logUtils.getTestCaseDetails() + "json path '" + actualValue + "' has to be present", isPathPresent); } } else { isExecutionOk = false; logUtils.error("the check can be executed ONLY if the actual value is a \"" + PlaceholderHandler.PLACEHOLDER_SYMBOL_BEGIN + "path\"-style placeholder"); } return isExecutionOk; } private boolean isItMathematicalCheck(String operation) { return operation.matches("[=|<|>]") || ">=".equals(operation) || "<=".equals(operation); } public void setOperationBlocking(boolean isBlocking) { this.isBlocking = isBlocking; } public void setFlowOutputParameters(Map<Integer, Map<String, String>> retrievedParameters) { this.retrievedParameters = retrievedParameters; } public LoggingUtils getLogUtils() { return logUtils; } }