示例#1
0
    def extract_response(self, extractors):
        """ extract value from requests.Response and store in OrderedDict.

        Args:
            extractors (list):

                [
                    {"resp_status_code": "status_code"},
                    {"resp_headers_content_type": "headers.content-type"},
                    {"resp_content": "content"},
                    {"resp_content_person_first_name": "content.person.name.first_name"}
                ]

        Returns:
            OrderDict: variable binds ordered dict

        """
        if not extractors:
            return {}

        logger.log_debug("start to extract from response object.")
        extracted_variables_mapping = OrderedDict()
        extract_binds_order_dict = utils.ensure_mapping_format(extractors)

        for key, field in extract_binds_order_dict.items():
            extracted_variables_mapping[key] = self.extract_field(field)

        return extracted_variables_mapping
示例#2
0
 def log_print(req_resp_dict, r_type):
     msg = "\n================== {} details ==================\n".format(
         r_type)
     for key, value in req_resp_dict[r_type].items():
         if isinstance(value, dict):
             msg += "{:<16} : {}\n".format(
                 key,
                 json.dumps(value,
                            sort_keys=True,
                            separators=(',', ':'),
                            ensure_ascii=False))
         else:
             # url decoder
             msg += "{:<16} : {}\n".format(
                 key, unquote(repr(value), 'utf-8'))
     logger.log_debug(msg)
示例#3
0
 def _send_request_safe_mode(self, method, url, **kwargs):
     """
     Send a HTTP request, and catch any exception that might occur due to connection problems.
     Safe mode has been removed from requests 1.x.
     """
     try:
         msg = "processed request:\n"
         msg += "> {method} {url}\n".format(method=method, url=url)
         msg += "> kwargs: {kwargs}".format(kwargs=kwargs)
         logger.log_debug(msg)
         return requests.Session.request(self, method, url, **kwargs)
     except (MissingSchema, InvalidSchema, InvalidURL):
         raise
     except RequestException as ex:
         resp = ApiResponse()
         resp.error = ex
         resp.status_code = 0  # with this status_code, content returns None
         resp.request = Request(method, url).prepare()
         return resp
示例#4
0
def render_html_report(summary, report_template=None, report_dir=None):
    """ render html report with specified report name and template

    Args:
        report_template (str): specify html report template path
        report_dir (str): specify html report save directory

    """
    if not report_template:
        report_template = os.path.join(
            os.path.abspath(os.path.dirname(__file__)), "templates",
            "report_template.html")
        logger.log_debug("No html report template specified, use default.")
    else:
        logger.log_info(
            "render with html report template: {}".format(report_template))

    logger.log_info("Start to render Html report ...")

    report_dir = report_dir or os.path.join(os.getcwd(), "reports")
    if not os.path.isdir(report_dir):
        os.makedirs(report_dir)

    start_at_timestamp = int(summary["time"]["start_at"])
    summary["time"]["start_datetime"] = datetime.fromtimestamp(
        start_at_timestamp).strftime('%Y-%m-%d %H:%M:%S')

    report_path = os.path.join(
        report_dir, "{}.html".format(summary["time"]["start_datetime"]))

    with io.open(report_template, "r", encoding='utf-8') as fp_r:
        template_content = fp_r.read()
        with io.open(report_path, 'w', encoding='utf-8') as fp_w:
            rendered_content = Template(template_content,
                                        extensions=["jinja2.ext.loopcontrols"
                                                    ]).render(summary)
            fp_w.write(rendered_content)

    logger.log_info("Generated Html report: {}".format(report_path))

    return report_path
示例#5
0
    def do_hook_actions(self, actions, hook_type):
        """ call hook actions.

        Args:
            actions (list): each action in actions list maybe in two format.

                format1 (dict): assignment, the value returned by hook function will be assigned to variable.
                    {"var": "${func()}"}
                format2 (str): only call hook functions.
                    ${func()}

            hook_type (enum): setup/teardown

        """
        logger.log_debug("call {} hook actions.".format(hook_type))
        for action in actions:

            if isinstance(action, dict) and len(action) == 1:
                # format 1
                # {"var": "${func()}"}
                var_name, hook_content = list(action.items())[0]
                hook_content_eval = self.session_context.eval_content(
                    hook_content)
                logger.log_debug("assignment with hook: {} = {} => {}".format(
                    var_name, hook_content, hook_content_eval))
                self.session_context.update_test_variables(
                    var_name, hook_content_eval)
            else:
                # format 2
                logger.log_debug("call hook function: {}".format(action))
                # TODO: check hook function if valid
                self.session_context.eval_content(action)
示例#6
0
    def extract_field(self, field):
        """ extract value from requests.Response.
        """
        if not isinstance(field, basestring):
            err_msg = u"Invalid extractor! => {}\n".format(field)
            logger.log_error(err_msg)
            raise exceptions.ParamsError(err_msg)

        msg = "extract: {}".format(field)

        if text_extractor_regexp_compile.match(field):
            value = self._extract_field_with_regex(field)
        else:
            value = self._extract_field_with_delimiter(field)

        if is_py2 and isinstance(value, unicode):
            value = value.encode("utf-8")

        msg += "\t=> {}".format(value)
        logger.log_debug(msg)

        return value
示例#7
0
def unset_os_environ(variables_mapping):
    """ set variables mapping to os.environ
    """
    for variable in variables_mapping:
        os.environ.pop(variable)
        logger.log_debug("Unset OS environment variable: {}".format(variable))
示例#8
0
def set_os_environ(variables_mapping):
    """ set variables mapping to os.environ
    """
    for variable in variables_mapping:
        os.environ[variable] = variables_mapping[variable]
        logger.log_debug("Set OS environment variable: {}".format(variable))
示例#9
0
    def _run_test(self, test_dict):
        """ run single teststep.

        Args:
            test_dict (dict): teststep info
                {
                    "name": "teststep description",
                    "skip": "skip this test unconditionally",
                    "times": 3,
                    "variables": [],            # optional, override
                    "request": {
                        "url": "http://127.0.0.1:5000/api/users/1000",
                        "method": "POST",
                        "headers": {
                            "Content-Type": "application/json",
                            "authorization": "$authorization",
                            "random": "$random"
                        },
                        "json": {"name": "user", "password": "******"}
                    },
                    "extract": {},              # optional
                    "validate": [],             # optional
                    "setup_hooks": [],          # optional
                    "teardown_hooks": []        # optional
                }

        Raises:
            exceptions.ParamsError
            exceptions.ValidationFailure
            exceptions.ExtractFailure

        """
        global tmp_extracted_variables
        # clear meta data first to ensure independence for each test
        self.__clear_test_data()

        # check skip
        self._handle_skip_feature(test_dict)

        # prepare
        test_dict = utils.lower_test_dict_keys(test_dict)
        test_variables = test_dict.get("variables", {})
        # override variables use former extracted_variables
        test_variables.update(tmp_extracted_variables)
        self.session_context.init_test_variables(test_variables)

        # teststep name
        test_name = self.session_context.eval_content(test_dict.get(
            "name", ""))

        # parse test request
        raw_request = test_dict.get('request', {})
        parsed_test_request = self.session_context.eval_content(raw_request)
        self.session_context.update_test_variables("request",
                                                   parsed_test_request)

        # prepend url with base_url unless it's already an absolute URL
        url = parsed_test_request.pop('url')
        base_url = self.session_context.eval_content(
            test_dict.get("base_url", ""))
        parsed_url = utils.build_url(base_url, url)

        # setup hooks
        setup_hooks = test_dict.get("setup_hooks", [])
        if setup_hooks:
            self.do_hook_actions(setup_hooks, "setup")

        try:
            method = parsed_test_request.pop('method')
            parsed_test_request.setdefault("verify", self.verify)
            group_name = parsed_test_request.pop("group", None)
        except KeyError:
            raise exceptions.ParamsError("URL or METHOD missed!")

        # TODO: move method validation to json schema
        valid_methods = [
            "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"
        ]
        if method.upper() not in valid_methods:
            err_msg = u"Invalid HTTP method! => {}\n".format(method)
            err_msg += "Available HTTP methods: {}".format(
                "/".join(valid_methods))
            logger.log_error(err_msg)
            raise exceptions.ParamsError(err_msg)

        logger.log_info("{method} {url}".format(method=method, url=parsed_url))
        logger.log_debug(
            "request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request))

        # request
        resp = self.http_client_session.request(method,
                                                parsed_url,
                                                name=(group_name or test_name),
                                                **parsed_test_request)
        resp_obj = response.ResponseObject(resp)

        # extract
        extractors = test_dict.get("extract", {})
        if extractors:
            extracted_variables_mapping = resp_obj.extract_response(extractors)
            tmp_extracted_variables.update(extracted_variables_mapping)
            self.session_context.update_session_variables(
                extracted_variables_mapping)
        self.session_context.update_session_variables(tmp_extracted_variables)

        # validate
        validators = test_dict.get("validate") or test_dict.get(
            "validators") or []
        try:
            self.session_context.validate(validators, resp_obj)
        except (exceptions.ParamsError, exceptions.ValidationFailure,
                exceptions.ExtractFailure):
            err_msg = "{} DETAILED REQUEST & RESPONSE {}\n".format(
                "*" * 32, "*" * 32)

            # log request
            err_msg += "====== request details ======\n"
            err_msg += "url: {}\n".format(parsed_url)
            err_msg += "method: {}\n".format(method)
            err_msg += "headers: {}\n".format(
                parsed_test_request.pop("headers", {}))
            for k, v in parsed_test_request.items():
                v = utils.omit_long_data(v)
                err_msg += "{}: {}\n".format(k, repr(v))

            err_msg += "\n"

            # log response
            err_msg += "====== response details ======\n"
            err_msg += "status_code: {}\n".format(resp_obj.status_code)
            err_msg += "headers: {}\n".format(resp_obj.headers)
            err_msg += "body: {}\n".format(repr(resp_obj.text))
            logger.log_error(err_msg)

            raise

        finally:
            self.validation_results = self.session_context.validation_results
            # teardown hooks
            teardown_hooks = test_dict.get("teardown_hooks", [])
            if teardown_hooks:
                self.session_context.update_test_variables(
                    "response", resp_obj)
                self.do_hook_actions(teardown_hooks, "teardown")
示例#10
0
 def log_print(req_resp_dict, r_type):
     msg = "\n================== {} details ==================\n".format(
         r_type)
     for key, value in req_resp_dict[r_type].items():
         msg += "{:<16} : {}\n".format(key, repr(value))
     logger.log_debug(msg)
示例#11
0
    def validate(self, validators, resp_obj):
        """ make validation with comparators
        """
        self.validation_results = []
        if not validators:
            return

        logger.log_debug("start to validate.")

        validate_pass = True
        failures = []

        for validator in validators:
            # validator should be LazyFunction object
            if not isinstance(validator, parser.LazyFunction):
                raise exceptions.ValidationFailure(
                    "validator should be parsed first: {}".format(validators))

            # evaluate validator args with context variable mapping.
            validator_args = validator.get_args()
            check_item, expect_item = validator_args
            check_value = self.__eval_validator_check(check_item, resp_obj)
            expect_value = self.__eval_validator_expect(expect_item)
            validator.update_args([check_value, expect_value])

            comparator = validator.func_name
            validator_dict = {
                "comparator": comparator,
                "check": check_item,
                "check_value": check_value,
                "expect": expect_item,
                "expect_value": expect_value
            }
            validate_msg = "\nvalidate: {} {} {}({})".format(
                check_item, comparator, expect_value,
                type(expect_value).__name__)

            try:
                validator.to_value(self.test_variables_mapping)
                validator_dict["check_result"] = "pass"
                validate_msg += "\t==> pass"
                logger.log_debug(validate_msg)
            except (AssertionError, TypeError):
                validate_pass = False
                validator_dict["check_result"] = "fail"
                validate_msg += "\t==> fail"
                validate_msg += "\n{}({}) {} {}({})".format(
                    check_value,
                    type(check_value).__name__, comparator, expect_value,
                    type(expect_value).__name__)
                logger.log_error(validate_msg)
                failures.append(validate_msg)

            self.validation_results.append(validator_dict)

            # restore validator args, in case of running multiple times
            validator.update_args(validator_args)

        if not validate_pass:
            failures_string = "\n".join([failure for failure in failures])
            raise exceptions.ValidationFailure(failures_string)