mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-14 04:09:38 +08:00
256 lines
6.8 KiB
Python
256 lines
6.8 KiB
Python
import collections
|
|
import copy
|
|
import json
|
|
import os.path
|
|
import platform
|
|
import string
|
|
import uuid
|
|
from typing import Dict, List, Any, Text
|
|
|
|
import sentry_sdk
|
|
from loguru import logger
|
|
|
|
from httprunner import __version__
|
|
from httprunner import exceptions
|
|
from httprunner.models import VariablesMapping
|
|
|
|
|
|
def init_sentry_sdk():
|
|
sentry_sdk.init(
|
|
dsn="https://460e31339bcb428c879aafa6a2e78098@sentry.io/5263855",
|
|
release="httprunner@{}".format(__version__),
|
|
)
|
|
with sentry_sdk.configure_scope() as scope:
|
|
scope.set_user({"id": uuid.getnode()})
|
|
|
|
|
|
def set_os_environ(variables_mapping):
|
|
""" set variables mapping to os.environ
|
|
"""
|
|
for variable in variables_mapping:
|
|
os.environ[variable] = variables_mapping[variable]
|
|
logger.debug(f"Set OS environment variable: {variable}")
|
|
|
|
|
|
def unset_os_environ(variables_mapping):
|
|
""" set variables mapping to os.environ
|
|
"""
|
|
for variable in variables_mapping:
|
|
os.environ.pop(variable)
|
|
logger.debug(f"Unset OS environment variable: {variable}")
|
|
|
|
|
|
def get_os_environ(variable_name):
|
|
""" get value of environment variable.
|
|
|
|
Args:
|
|
variable_name(str): variable name
|
|
|
|
Returns:
|
|
value of environment variable.
|
|
|
|
Raises:
|
|
exceptions.EnvNotFound: If environment variable not found.
|
|
|
|
"""
|
|
try:
|
|
return os.environ[variable_name]
|
|
except KeyError:
|
|
raise exceptions.EnvNotFound(variable_name)
|
|
|
|
|
|
def lower_dict_keys(origin_dict):
|
|
""" convert keys in dict to lower case
|
|
|
|
Args:
|
|
origin_dict (dict): mapping data structure
|
|
|
|
Returns:
|
|
dict: mapping with all keys lowered.
|
|
|
|
Examples:
|
|
>>> origin_dict = {
|
|
"Name": "",
|
|
"Request": "",
|
|
"URL": "",
|
|
"METHOD": "",
|
|
"Headers": "",
|
|
"Data": ""
|
|
}
|
|
>>> lower_dict_keys(origin_dict)
|
|
{
|
|
"name": "",
|
|
"request": "",
|
|
"url": "",
|
|
"method": "",
|
|
"headers": "",
|
|
"data": ""
|
|
}
|
|
|
|
"""
|
|
if not origin_dict or not isinstance(origin_dict, dict):
|
|
return origin_dict
|
|
|
|
return {key.lower(): value for key, value in origin_dict.items()}
|
|
|
|
|
|
def print_info(info_mapping):
|
|
""" print info in mapping.
|
|
|
|
Args:
|
|
info_mapping (dict): input(variables) or output mapping.
|
|
|
|
Examples:
|
|
>>> info_mapping = {
|
|
"var_a": "hello",
|
|
"var_b": "world"
|
|
}
|
|
>>> info_mapping = {
|
|
"status_code": 500
|
|
}
|
|
>>> print_info(info_mapping)
|
|
==================== Output ====================
|
|
Key : Value
|
|
---------------- : ----------------------------
|
|
var_a : hello
|
|
var_b : world
|
|
------------------------------------------------
|
|
|
|
"""
|
|
if not info_mapping:
|
|
return
|
|
|
|
content_format = "{:<16} : {:<}\n"
|
|
content = "\n==================== Output ====================\n"
|
|
content += content_format.format("Variable", "Value")
|
|
content += content_format.format("-" * 16, "-" * 29)
|
|
|
|
for key, value in info_mapping.items():
|
|
if isinstance(value, (tuple, collections.deque)):
|
|
continue
|
|
elif isinstance(value, (dict, list)):
|
|
value = json.dumps(value)
|
|
elif value is None:
|
|
value = "None"
|
|
|
|
content += content_format.format(key, value)
|
|
|
|
content += "-" * 48 + "\n"
|
|
logger.info(content)
|
|
|
|
|
|
def omit_long_data(body, omit_len=512):
|
|
""" omit too long str/bytes
|
|
"""
|
|
if not isinstance(body, (str, bytes)):
|
|
return body
|
|
|
|
body_len = len(body)
|
|
if body_len <= omit_len:
|
|
return body
|
|
|
|
omitted_body = body[0:omit_len]
|
|
|
|
appendix_str = f" ... OMITTED {body_len - omit_len} CHARACTORS ..."
|
|
if isinstance(body, bytes):
|
|
appendix_str = appendix_str.encode("utf-8")
|
|
|
|
return omitted_body + appendix_str
|
|
|
|
|
|
def get_platform():
|
|
return {
|
|
"httprunner_version": __version__,
|
|
"python_version": "{} {}".format(
|
|
platform.python_implementation(), platform.python_version()
|
|
),
|
|
"platform": platform.platform(),
|
|
}
|
|
|
|
|
|
def sort_dict_by_custom_order(raw_dict: Dict, custom_order: List):
|
|
def get_index_from_list(lst: List, item: Any):
|
|
try:
|
|
return lst.index(item)
|
|
except ValueError:
|
|
# item is not in lst
|
|
return len(lst) + 1
|
|
|
|
return dict(
|
|
sorted(raw_dict.items(), key=lambda i: get_index_from_list(custom_order, i[0]))
|
|
)
|
|
|
|
|
|
def ensure_file_path_valid(file_path: Text) -> Text:
|
|
""" ensure file path valid for pytest, handle cases when directory name includes dot/hyphen/space
|
|
|
|
Args:
|
|
file_path: absolute or relative file path
|
|
|
|
Returns:
|
|
ensured valid absolute file path
|
|
|
|
"""
|
|
raw_file_name, file_suffix = os.path.splitext(file_path)
|
|
file_suffix = file_suffix.lower()
|
|
|
|
if os.path.isabs(file_path):
|
|
raw_file_relative_name = raw_file_name[len(os.getcwd()) + 1 :]
|
|
else:
|
|
raw_file_relative_name = raw_file_name
|
|
|
|
if raw_file_relative_name == "":
|
|
return file_path
|
|
|
|
path_names = []
|
|
for name in raw_file_relative_name.rstrip(os.sep).split(os.sep):
|
|
|
|
if name[0] in string.digits:
|
|
# ensure file name not startswith digit
|
|
# 19 => T19, 2C => T2C
|
|
name = f"T{name}"
|
|
|
|
if name.startswith("."):
|
|
# avoid ".csv" been converted to "_csv"
|
|
pass
|
|
else:
|
|
# handle cases when directory name includes dot/hyphen/space
|
|
name = name.replace(" ", "_").replace(".", "_").replace("-", "_")
|
|
|
|
path_names.append(name)
|
|
|
|
new_file_path = os.path.join(os.getcwd(), f"{os.sep.join(path_names)}{file_suffix}")
|
|
return new_file_path
|
|
|
|
|
|
class ExtendJSONEncoder(json.JSONEncoder):
|
|
""" especially used to safely dump json data with python object, such as MultipartEncoder
|
|
"""
|
|
|
|
def default(self, obj):
|
|
try:
|
|
return super(ExtendJSONEncoder, self).default(obj)
|
|
except (UnicodeDecodeError, TypeError):
|
|
return repr(obj)
|
|
|
|
|
|
def override_config_variables(
|
|
step_variables: VariablesMapping, config_variables: VariablesMapping
|
|
) -> VariablesMapping:
|
|
""" override variables:
|
|
testcase step variables > testcase config variables
|
|
testsuite testcase variables > testsuite config variables
|
|
"""
|
|
step_new_variables = {}
|
|
for key, value in step_variables.items():
|
|
if f"${key}" == value or "${" + key + "}" == value:
|
|
# e.g. {"base_url": "$base_url"}
|
|
# or {"base_url": "${base_url}"}
|
|
continue
|
|
|
|
step_new_variables[key] = value
|
|
|
|
variables = copy.deepcopy(config_variables)
|
|
variables.update(step_new_variables)
|
|
return variables
|