/** * 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.checks; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeMap; import org.testng.ITestContext; import com.hotels.heat.core.environment.EnvironmentHandler; import com.hotels.heat.core.handlers.TestSuiteHandler; import com.hotels.heat.core.runner.TestBaseRunner; import com.hotels.heat.core.specificexception.HeatException; import com.hotels.heat.core.utils.RestAssuredRequestMaker; import com.hotels.heat.core.utils.TestCaseUtils; import com.hotels.heat.core.utils.TestRequest; import com.hotels.heat.core.utils.log.LoggingUtils; import io.restassured.http.Method; import io.restassured.response.Response; /** * Basic utility class in comparing check tests (Flow and Compare mode). * */ public class BasicMultipleChecks { public static final String DEFAULT_FORMAT_OF_TYPE_CHECK = "string"; private static final String E2E_FLOW_STEPS_JSON_ELEMENT = "e2eFlowSteps"; private static final String OBJECTS_TO_COMPARE_JSON_ELEMENT = "objectsToCompare"; private static final String OBJECT_NAME_JSON_ELEMENT = "objectName"; private static final String WEBAPP_NAME_JSON_ELEMENT = "webappName"; private static final String URL_JSON_ELEMENT = "url"; private final boolean isRunnableTest = true; private final Map<Integer, String> steps; private final Map<String, String> paths; private final Map<String, String> httpMethods; private final LoggingUtils logUtils; private final ITestContext context; private Map<String, Object> inputJsonObjs = new HashMap(); private RestAssuredRequestMaker restAssuredMsg; public BasicMultipleChecks(ITestContext context) { this.logUtils = TestSuiteHandler.getInstance().getLogUtils(); this.context = context; this.steps = new TreeMap(); this.paths = new HashMap(); this.httpMethods = new HashMap(); } public LoggingUtils getLogUtils() { return this.logUtils; } public ITestContext getContext() { return this.context; } /** * Flat the representation of test case's parameters in order to better analyze them. * @param testCaseParamsInput the input parameters to define a test case */ public void compactInfoToCompare(Map<String, Object> testCaseParamsInput) { EnvironmentHandler eh = TestSuiteHandler.getInstance().getEnvironmentHandler(); List<Object> objectsToCompareList; if (testCaseParamsInput.containsKey(OBJECTS_TO_COMPARE_JSON_ELEMENT)) { objectsToCompareList = (List<Object>) testCaseParamsInput.get(OBJECTS_TO_COMPARE_JSON_ELEMENT); } else if (testCaseParamsInput.containsKey(E2E_FLOW_STEPS_JSON_ELEMENT)) { objectsToCompareList = (List<Object>) testCaseParamsInput.get(E2E_FLOW_STEPS_JSON_ELEMENT); } else { throw new HeatException(this.logUtils.getExceptionDetails() + "it is not possible to retrieve a list of request objects"); } this.logUtils.debug("objectsToCompareList size = {}", objectsToCompareList.size()); Iterator<Object> multipleListIterator = objectsToCompareList.iterator(); while (multipleListIterator.hasNext()) { elaborateObjects(multipleListIterator); } } private void elaborateObjects(Iterator<Object> multipleListIterator) { EnvironmentHandler eh = TestSuiteHandler.getInstance().getEnvironmentHandler(); Map<String, Object> compareSingleObj = (Map<String, Object>) multipleListIterator.next(); String webappName = (String) compareSingleObj.get(WEBAPP_NAME_JSON_ELEMENT); String singleBlockName = (String) compareSingleObj.get(OBJECT_NAME_JSON_ELEMENT); String url = (String) compareSingleObj.get(URL_JSON_ELEMENT); String webappPath = ""; if (eh != null) { webappPath = eh.getEnvironmentUrl(webappName); if (webappPath == null || "".equals(webappPath)) { this.logUtils.error("test not runnable"); throw new HeatException(this.logUtils.getExceptionDetails() + "test not runnable: webapp path not valid"); } else if (webappPath == null) { this.context.setAttribute(this.context.getAttribute(TestBaseRunner.ATTR_TESTCASE_ID).toString(), TestBaseRunner.STATUS_SKIPPED); } } else { this.logUtils.error("environment handler null"); throw new HeatException(this.logUtils.getExceptionDetails() + "test not runnable: environment handler not valid"); } String httpMethod; if (!compareSingleObj.containsKey(TestCaseUtils.JSON_FIELD_HTTP_METHOD) || compareSingleObj.get(TestCaseUtils.JSON_FIELD_HTTP_METHOD) == null) { httpMethod = TestRequest.HTTP_METHOD_DEFAULT.name(); } else { httpMethod = (String) compareSingleObj.get(TestCaseUtils.JSON_FIELD_HTTP_METHOD); } String finalPath = webappPath + url; paths.put(singleBlockName, finalPath); httpMethods.put(singleBlockName, httpMethod); inputJsonObjs.put(singleBlockName, compareSingleObj); if (compareSingleObj.containsKey(TestCaseUtils.JSON_FIELD_STEP_NUMBER)) { try { Integer stepNumber = Integer.parseInt((String) compareSingleObj.get(TestCaseUtils.JSON_FIELD_STEP_NUMBER)); steps.put(stepNumber, singleBlockName); } catch (NumberFormatException nfe) { throw new HeatException(this.logUtils.getExceptionDetails() + "test not runnable: 'stepNumber' field isn't an Integer value"); } } } /** * This method retrieves info from the JSON input objects. * * @param testCaseParamsInput the input parameters to define a test case * @return Map webapp name, response from the specified webapp */ public Map<String, Response> retrieveInfo(Map<String, Object> testCaseParamsInput) { Map<String, Response> respRetrieved = new HashMap(); try { compactInfoToCompare(testCaseParamsInput); if (isRunnableTest) { this.logUtils.trace("number of blocks to load: {}", httpMethods.size()); httpMethods.entrySet().stream().map((entry) -> entry.getKey()).forEach((serviceId) -> { Response rsp = retrieveSingleBlockRsp(serviceId); if (rsp == null) { this.logUtils.debug("response for '{}' : null", serviceId); } else { this.logUtils.debug("response for '{}': '{}'", serviceId, rsp.asString()); } respRetrieved.put(serviceId, rsp); }); } } catch (Exception oEx) { this.logUtils.debug("Exception message: '{}'", oEx.getLocalizedMessage()); } return respRetrieved; } private Response retrieveSingleBlockRsp(String serviceId) { Map singleInputJsonObj = (Map) inputJsonObjs.get(serviceId); return retrieveSingleBlockRsp(serviceId, singleInputJsonObj); } /** * Perform a test call of a single block and return the related Response object. * * @param serviceId identifier of the test case * @param singleInputJsonObj parameters of test case as Map * @return the Response object */ protected Response retrieveSingleBlockRsp(String serviceId, Map singleInputJsonObj) { EnvironmentHandler eh = TestSuiteHandler.getInstance().getEnvironmentHandler(); Method webappHttpMethod = Method.valueOf(httpMethods.get(serviceId)); String webappPath = paths.get(serviceId); this.logUtils.trace("path of block '{}': '{}'", serviceId, webappPath); if (restAssuredMsg == null) { throw new HeatException(this.logUtils.getExceptionDetails() + "restAssuredMsg obj null"); } restAssuredMsg.setBasePath(eh.getEnvironmentUrl((String) singleInputJsonObj.get(WEBAPP_NAME_JSON_ELEMENT))); TestRequest testRequest = restAssuredMsg.buildRequestByParams(webappHttpMethod, singleInputJsonObj); testRequest.getHeadersParams().put("X-Heat-Test-Id", context.getName() + "." + context.getAttribute(TestBaseRunner.ATTR_TESTCASE_ID)); Optional.ofNullable(singleInputJsonObj.get(TestCaseUtils.JSON_FIELD_STEP_NUMBER)) .map(Object::toString) .ifPresent(step -> testRequest.getHeadersParams().put("X-Heat-Test-Step", step)); Response rsp = restAssuredMsg.executeTestRequest(testRequest); return rsp; } /** * expects is a method that analyses all required data. * @param isBlocking indicates if this kind of expectation causes the stopping of testing * @param testCaseParams the input parameters to define a test case * @param mapServiceIdResponse a map containing the responses to be compared */ public void expects(boolean isBlocking, Map testCaseParams, Map<String, Response> mapServiceIdResponse) { if (testCaseParams.containsKey("expects")) { List<Object> expectedObjList = (List<Object>) testCaseParams.get("expects"); expectedObjList.forEach(item-> { Map<String, Object> singleBlockCheck = (Map<String, Object>) item; String checkStepDescription = getCheckDescription(singleBlockCheck); boolean conditionOk = true; if (singleBlockCheck.containsKey("condition")) { this.logUtils.debug("THERE IS A CONDITION"); conditionOk = conditionVerification((ArrayList<Object>) singleBlockCheck.get("condition"), mapServiceIdResponse, checkStepDescription + "(condition)"); } if (conditionOk) { singleBlockCheck(isBlocking, singleBlockCheck, mapServiceIdResponse, checkStepDescription); } else { this.logUtils.debug("condition not verified for: '{}'", checkStepDescription); } }); } } private String getCheckDescription(Map fieldCheck) { String description = ""; try { description = (String) fieldCheck.get("description"); } catch (Exception oEx) { this.logUtils.error("Exception: class {}, cause {}, message {}", oEx.getClass(), oEx.getCause(), oEx.getLocalizedMessage()); throw new HeatException(this.logUtils.getExceptionDetails() + "It is not possible to retrieve the description of the check"); } return description; } private boolean conditionVerification(ArrayList<Object> conditionArray, Map<String, Response> mapServiceIdResponse, String checkStepDescription) { boolean isConditionVerified = true; this.logUtils.debug("There are {} conditions to verify", conditionArray.size()); this.logUtils.debug("{}", conditionArray.toString()); Iterator itr = conditionArray.iterator(); while (itr.hasNext()) { isConditionVerified = isConditionVerified && singleBlockCheck(false, (Map<String, Object>) itr.next(), mapServiceIdResponse, checkStepDescription); } this.logUtils.debug("{} Condition verified '{}'", checkStepDescription, isConditionVerified); return isConditionVerified; } private boolean singleBlockCheck(boolean isBlocking, Map<String, Object> blockToCheck, Map<String, Response> mapServiceIdResponse, String checkStepDescription) { boolean isCheckOk = true; this.logUtils.trace("{} block: {}", checkStepDescription, blockToCheck.toString()); BasicChecks basicChecks = new BasicChecks(this.context); isCheckOk = basicChecks.executeCheck(isBlocking, blockToCheck, mapServiceIdResponse); return isCheckOk; } public void setRestAssuredRequestMaker(RestAssuredRequestMaker requestMakerInput) { restAssuredMsg = requestMakerInput; } public boolean getIsRunnable() { return isRunnableTest; } public Map<Integer, String> getSteps() { return steps; } public Map<String, String> getHttpMethods() { return httpMethods; } public Map<String, Object> getInputJsonObjs() { return inputJsonObjs; } }