def parse_variables(content, variable_mapping): """ replace all variables of string content with mapping value. @param (str) content @return (str) parsed content e.g. variable_mapping = { "var_1": "abc", "var_2": "def" } $var_1 => "abc" $var_1#XYZ => "abc#XYZ" /$var_1/$var_2/var3 => "/abc/def/var3" ${func($var_1, $var_2, xyz)} => "${func(abc, def, xyz)}" """ variable_name_list = get_contain_variables(content) for variable_name in variable_name_list: variable_value = variable_mapping.get(variable_name) if variable_value is None: raise ParamsError("%s is not defined in bind variables!" % variable_name) if "${}".format(variable_name) == content: # content is a variable content = variable_value else: # content contains one or many variables content = content.replace("${}".format(variable_name), str(variable_value), 1) return content
def query_json(json_content, query, delimiter='.'): """ Do an xpath-like query with json_content. @param (json_content) json_content json_content = { "ids": [1, 2, 3, 4], "person": { "name": { "first_name": "Leo", "last_name": "Lee", }, "age": 29, "cities": ["Guangzhou", "Shenzhen"] } } @param (str) query "person.name.first_name" => "Leo" "person.cities.0" => "Guangzhou" @return queried result """ stripped_query = query.strip(delimiter) if not stripped_query: return None try: for key in stripped_query.split(delimiter): if isinstance(json_content, list): key = int(key) json_content = json_content[key] except (KeyError, ValueError, IndexError): raise ParamsError("invalid query string in extract_binds!") return json_content
def get_timestamp(str_len=13): """ get timestamp string, length can only between 0 and 16 """ if isinstance(str_len, int) and 0 < str_len < 17: return str(time.time()).replace(".", "")[:str_len] raise ParamsError("timestamp length can only between 0 and 16.")
def _build_url(self, path): """ prepend url with hostname unless it's already an absolute URL """ if absolute_http_url_regexp.match(path): return path elif self.base_url: return "%s%s" % (self.base_url, path) else: raise ParamsError("base url missed!")
def load_testcases(testcase_file_path): file_suffix = os.path.splitext(testcase_file_path)[1] if file_suffix == '.json': return load_json_file(testcase_file_path) elif file_suffix in ['.yaml', '.yml']: return load_yaml_file(testcase_file_path) else: # '' or other suffix raise ParamsError("Bad testcase file name!")
def register_request(self, request_dict, level="testcase"): if "headers" in request_dict: # convert keys in request headers to lowercase headers = request_dict.pop("headers") if not isinstance(headers, dict): raise ParamsError("HTTP Request Headers invalid!") request_dict["headers"] = {key.lower(): headers[key] for key in headers} self.__update_context_request_config(level, request_dict)
def match_expected(value, expected, comparator="eq"): """ check if value matches expected value. @param value: value that get from response. @param expected: expected result described in testcase @param comparator: compare method """ try: if comparator in ["eq", "equals", "=="]: assert value == expected elif comparator in ["str_eq", "string_equals"]: assert str(value) == str(expected) elif comparator in ["ne", "not_equals"]: assert value != expected elif comparator in ["len_eq", "length_equal", "count_eq"]: assert len(value) == expected elif comparator in [ "len_gt", "count_gt", "length_greater_than", "count_greater_than" ]: assert len(value) > expected elif comparator in ["len_ge", "count_ge", "length_greater_than_or_equals", \ "count_greater_than_or_equals"]: assert len(value) >= expected elif comparator in [ "len_lt", "count_lt", "length_less_than", "count_less_than" ]: assert len(value) < expected elif comparator in ["len_le", "count_le", "length_less_than_or_equals", \ "count_less_than_or_equals"]: assert len(value) <= expected elif comparator in ["lt", "less_than"]: assert value < expected elif comparator in ["le", "less_than_or_equals"]: assert value <= expected elif comparator in ["gt", "greater_than"]: assert value > expected elif comparator in ["ge", "greater_than_or_equals"]: assert value >= expected elif comparator in ["contains"]: assert expected in value elif comparator in ["contained_by"]: assert value in expected elif comparator in ["regex"]: assert re.match(expected, value) elif comparator in ["str_len", "string_length"]: assert len(value) == int(expected) elif comparator in ["startswith"]: assert str(value).startswith(str(expected)) else: raise ParamsError("comparator not supported!") return True except AssertionError: return False
def parse_content_with_variables(content, variables_binds): """ replace variables with bind value """ # check if content includes $variable matched = re.match(r"^(.*)\$(\w+)(.*)$", content) if matched: # this is a variable, and will replace with its bind value variable_name = matched.group(2) value = variables_binds.get(variable_name) if value is None: raise ParamsError("%s is not defined in bind variables!" % variable_name) if matched.group(1) or matched.group(3): # e.g. /api/users/$uid return content.replace("$%s" % variable_name, value) return value return content
def parse_content_with_variables(content, variables_binds): """ replace variables with bind value """ # check if content includes ${variable} matched = re.match(r"(.*)\$\{(.*)\}(.*)", content) if matched: # this is a variable, and will replace with its bind value variable_name = matched.group(2) value = variables_binds.get(variable_name) if value is None: raise ParamsError("%s is not defined in bind variables!" % variable_name) if matched.group(1) or matched.group(3): # e.g. /api/users/${uid} return re.sub(r"\$\{.*\}", value, content) return value return content
def parse_content_with_bindings(content, variables_binds, functions_binds): """ evaluate content recursively, each variable in content will be evaluated with bind variables and functions. variables marker: $variable. @param (dict) content in any data structure { "url": "http://127.0.0.1:5000/api/users/$uid", "method": "POST", "headers": { "Content-Type": "application/json", "authorization": "$authorization", "random": "$random", "sum": "${add_two_nums(1, 2)}" }, "body": "$data" } @param (dict) variables_binds, variables binds mapping { "authorization": "a83de0ff8d2e896dbd8efb81ba14e17d", "random": "A2dEx", "data": {"name": "user", "password": "******"} } @param (dict) functions_binds, functions binds mapping { "add_two_nums": lambda a, b=1: a + b } @return (dict) parsed content with evaluated bind values { "url": "http://127.0.0.1:5000/api/users/1000", "method": "POST", "headers": { "Content-Type": "application/json", "authorization": "a83de0ff8d2e896dbd8efb81ba14e17d", "random": "A2dEx", "sum": 3 }, "body": {"name": "user", "password": "******"} } """ if isinstance(content, (list, tuple)): return [ parse_content_with_bindings(item, variables_binds, functions_binds) for item in content ] if isinstance(content, dict): evaluated_data = {} for key, value in content.items(): evaluated_data[key] = parse_content_with_bindings( value, variables_binds, functions_binds) return evaluated_data if isinstance(content, (int, float)): return content # content is in string format here content = "" if content is None else content.strip() if is_functon(content): # function marker: ${func(1, 2, a=3, b=4)} fuction_meta = parse_function(content) func_name = fuction_meta['func_name'] func = functions_binds.get(func_name) if func is None: raise ParamsError("%s is not defined in bind functions!" % func_name) args = fuction_meta.get('args', []) kwargs = fuction_meta.get('kwargs', {}) args = parse_content_with_bindings(args, variables_binds, functions_binds) kwargs = parse_content_with_bindings(kwargs, variables_binds, functions_binds) return func(*args, **kwargs) elif get_contain_variables(content): parsed_data = parse_variables(content, variables_binds) return parsed_data else: return content