addproject
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 检查jenkins上的用例构建结果,并根据用例失败数量,将结果设置为通过或失败
|
||||
import sys
|
||||
import os
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
WORKSPACE = os.path.abspath(os.path.join(HERE, '../../../'))
|
||||
sys.path.append(WORKSPACE)
|
||||
from base_framework.public_tools.xml_file_api import XmlFileApi
|
||||
PROJECT_NAME = sys.argv[0]
|
||||
|
||||
# 指定 Robot Framework 输出文件的路径
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
output_file = os.path.abspath(os.path.join(HERE, '../../../Report/out/output.xml'))
|
||||
|
||||
# 解析输出文件, 获取失败用例数量
|
||||
xf = XmlFileApi(file_path=output_file)
|
||||
result = xf.get_contain_by_tag_names(tag_names='statistics/total/stat')
|
||||
failed_count = int(result[0]['attrib']['fail'])
|
||||
|
||||
# 按失败用例对应的qa人员标签,
|
||||
if failed_count > 0:
|
||||
result = xf.get_contain_by_tag_names(tag_names='statistics/tag/stat')
|
||||
qa_info = {}
|
||||
qa_failed_case_number = 0
|
||||
for item in result:
|
||||
if 'qa-' in item['text'].lower():
|
||||
fail_case = int(item['attrib']['fail'])
|
||||
if fail_case > 0:
|
||||
qa_info[item['text'][3:]] = fail_case
|
||||
qa_failed_case_number += fail_case
|
||||
if len(qa_info) > 0:
|
||||
print("失败用例对应的QA人员及失败用例数量:")
|
||||
for key, value in qa_info.items():
|
||||
print(f"{key}: {value}个")
|
||||
if failed_count > qa_failed_case_number:
|
||||
print(f"无qa标签:{failed_count - qa_failed_case_number}个")
|
||||
|
||||
# 根据失败用例数量设置构建结果
|
||||
if "-IT" in PROJECT_NAME: # it用例失败用例大于5个就标红
|
||||
if failed_count > 5:
|
||||
print(f"测试用例执行失败,共有 {failed_count} 个失败的用例。")
|
||||
sys.exit(1) # 退出码为1,表示构建失败,构建结果将展示为红色
|
||||
elif failed_count > 0:
|
||||
print("测试用例小范围失败,但是不影响整体结果。")
|
||||
sys.exit(0) # 退出码为0,表示构建成功,但此时受jenkins构建后步骤限制,将展示为黄色
|
||||
else:
|
||||
print("测试用例执行成功,所有用例都通过了。")
|
||||
sys.exit(0) # 退出码为0,表示构建成功,构建结果将展示为绿色
|
||||
else: # st和smoking有失败用例就标红
|
||||
if failed_count > 0:
|
||||
print(f"测试用例执行失败,共有 {failed_count} 个失败的用例。")
|
||||
sys.exit(1) # 退出码为1,表示构建失败,构建结果将展示为红色
|
||||
else:
|
||||
print("测试用例执行成功,所有用例都通过了。")
|
||||
sys.exit(0) # 退出码为0,表示构建成功,构建结果将展示为绿色
|
||||
68
base_framework/platform_tools/Jenkins/jenkins_api.py
Normal file
68
base_framework/platform_tools/Jenkins/jenkins_api.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# coding: utf-8
|
||||
from base_framework.public_tools.selenium_api import SeleniumWebUI
|
||||
import time
|
||||
|
||||
|
||||
class JenkinsAPI(SeleniumWebUI):
|
||||
def __init__(self):
|
||||
SeleniumWebUI.__init__(self)
|
||||
self.project_info = {}
|
||||
|
||||
def jenkins_login(self, u_name=None, u_pwd=None):
|
||||
""" 登陆jenkins """
|
||||
if not u_name:
|
||||
u_name = "admin"
|
||||
u_pwd = "admin"
|
||||
login_url = "http://39.170.26.156:8256/jenkins/login?from=%2Fjenkins%2F"
|
||||
self.web_open_url(url=login_url)
|
||||
self.web_send_keys(locator="//*[@id=\"j_username\"]", text=u_name)
|
||||
self.web_send_keys(locator="//*[@name=\"j_password\"]", text=u_pwd)
|
||||
self.web_click_element(locator="//*[@name=\"Submit\"]")
|
||||
time.sleep(1) # 切换到构建墙
|
||||
self.web_click_element(locator="//*[text()='1-用户产品组']")
|
||||
time.sleep(1) # 显示用例数量
|
||||
self.web_click_element(locator="//*[@title=\"Configure Build Monitor Settings\"]")
|
||||
time.sleep(1)
|
||||
self.web_click_checkbox(locator="//*[@id=\"settings-show-test-result\"]")
|
||||
self.web_click_checkbox(locator="//button[text()=\"Done\"]")
|
||||
|
||||
def jenkins_find_all_case_number(self):
|
||||
""" 从构建墙上读取用例数量 """
|
||||
elements = self.web_find_elements(locator="//li[@ng-repeat=\"project in jobs track by project.hashCode\"]")
|
||||
for element in elements:
|
||||
item = element.text.split('\n')
|
||||
case_number = item[1].split('/')
|
||||
self.project_info[item[0]] = case_number[1]
|
||||
|
||||
def jenkins_edit_project_config(self, **kwargs):
|
||||
""" 编辑工程配置信息 """
|
||||
for jp in self.project_info:
|
||||
case_number = int(self.project_info[jp])
|
||||
if jp == "TO-IT":
|
||||
jp = "TO-IT-TEST"
|
||||
elif jp == "TO-SMOKING":
|
||||
jp = "TO-SMOKING_NEW"
|
||||
project_url = "http://10.250.200.1:8080/jenkins/view/1-%E7%94%A8%E6%88%B7%E4%BA%A7%E5%93%81%E7%BB%84/" \
|
||||
"job/{}/configure".format(jp)
|
||||
if "SMOKING" in jp or "-ST" in jp:
|
||||
threshold = 99.999999 # 冒烟和ST用例需全部构建成功
|
||||
else:
|
||||
threshold = round((1-5.5/case_number)*100, 4) # 阈值
|
||||
self.web_open_url(url=project_url) # 进入设置页面
|
||||
self.web_send_keys(locator="//input[@name=\"_.unstableThreshold\"]", text=str(threshold))
|
||||
self.web_click_checkbox(locator="//button[text()=\"保存\"]")
|
||||
print("{}设置阈值{}完毕....".format(jp, threshold))
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ja = JenkinsAPI()
|
||||
ja.jenkins_login()
|
||||
time.sleep(3)
|
||||
ja.jenkins_find_all_case_number()
|
||||
ja.jenkins_edit_project_config()
|
||||
time.sleep(3)
|
||||
ja.web_close_browser()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
special_team = ["EN", "PZ", "ASTOP", "ASOPE", "ASTWB", "ASORG", "UBRD", "UBJ", "ES", "ASTEA", 'GUE', "TMO", "PB"]
|
||||
@@ -0,0 +1,684 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import importlib.util as i_util
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from base_framework.platform_tools.Keywords_service import special_team
|
||||
|
||||
token_false_server = ["scm-server", "scm-biz-server", "peppa-qi-api", "lggzt", "cti-manage", "ccwx-api", "peppa-la-core-server"]
|
||||
add_api_list = ["peppa-teach-api"]
|
||||
host_servers = ["HULK-ORG-API", "HULK-TEACHER-API", "PEPPA-LA-CORE-SERVER"]
|
||||
host_servers_except_api = ["HULK-CONTENT-AUDIT-SERVER"]
|
||||
header_servers = ["hulk-operation-api-server", "hulk-teach-supply-cli-api", "hulk-teach-backend-api"]
|
||||
uid_server = ['SPARKEDU-API', 'SPARKEDU-SITE-API', 'HUOHUA-SERVICE-API']
|
||||
eid_server = ['SPARKEDU-SITE-MANAGE', 'SPARKEDU-SITE-SCHEDULER']
|
||||
|
||||
def check_server_host(servers, host):
|
||||
"""
|
||||
检查不需要token的server是否存在url中
|
||||
"""
|
||||
for server in servers:
|
||||
if server in host:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
|
||||
from base_framework.public_tools.log import get_logger
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
|
||||
db = MySqLHelper()
|
||||
log = get_logger()
|
||||
|
||||
|
||||
def get_classes(module):
|
||||
classes = []
|
||||
cls_members = inspect.getmembers(module, inspect.isclass)
|
||||
for (name, _) in cls_members:
|
||||
classes.append(name)
|
||||
return classes
|
||||
|
||||
|
||||
def get_module_by_file_path(file_path):
|
||||
spec = importlib.util.spec_from_file_location("module_name", file_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class SetAttr(object):
|
||||
|
||||
def __init__(self, paras):
|
||||
for key, values in paras.items():
|
||||
self._add_attr(key, values)
|
||||
|
||||
def _add_attr(self, key, value):
|
||||
self.__setattr__(key, value)
|
||||
|
||||
|
||||
class SwaggerDBInfo(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_team(teamname):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where team = '%s'" % teamname
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_id(swagger_id):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where id = '%s'" % swagger_id
|
||||
return db.select_one(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_info_by_url_and_type(request_url, request_type, interface_id):
|
||||
sql = '''select * from sparkatp.interface_info where in_url = "%s" and type = "%s" and
|
||||
id = %s''' % (request_url, request_type, interface_id)
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_parameters_by_interface_id(interface_id):
|
||||
sql = """select * from sparkatp.request_parameters a right join sparkatp.parameters_relation b on
|
||||
a.id = b.parameter_id where a.interface_id = {} and a.is_need != 2 and b.type = 'request'""".format(
|
||||
interface_id)
|
||||
return db.select_all(sql=sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_request_demo_info_by_interface_id(interface_id):
|
||||
sql = 'SELECT * FROM sparkatp.parameters_demo where in_id = "%s" order by id DESC' % interface_id
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
|
||||
class KWFileOperation(object):
|
||||
"""
|
||||
操作xx_interface.py文件
|
||||
"""
|
||||
|
||||
def __init__(self, team):
|
||||
self.kw_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
self.all_keywords = []
|
||||
self.all_url = {}
|
||||
self.kw_name_with_url = {}
|
||||
self.runner = self._get_obj_runner()
|
||||
self.team = team
|
||||
self.kw_class = self._get_interface_class_name(self.kw_file_path)
|
||||
|
||||
def _clean_demo_to_less_key(self, demo):
|
||||
demo_result = copy.deepcopy(demo)
|
||||
for key, value in demo.items():
|
||||
if not value:
|
||||
demo_result.pop(key)
|
||||
lenth = len(demo_result)
|
||||
paras = "kwargs"
|
||||
if lenth == 1 and isinstance(list(demo_result.values())[0], list):
|
||||
paras = "args"
|
||||
return lenth, demo_result, paras
|
||||
|
||||
def _get_interface_class_name(self, file_path):
|
||||
module = get_module_by_file_path(file_path)
|
||||
classes = get_classes(module)
|
||||
for class_ in classes:
|
||||
if "interface" in class_.lower() and class_.lower().startswith(self.team.lower()):
|
||||
return class_
|
||||
|
||||
def _get_obj_runner(self):
|
||||
spec = i_util.spec_from_file_location("module_name", self.kw_file_path)
|
||||
module = i_util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
runner = inspect.getmembers(module)
|
||||
for module in runner:
|
||||
if module[0] == "obj_runner":
|
||||
return module[1]
|
||||
return runner
|
||||
|
||||
def _get_file_content(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as f:
|
||||
for line in f:
|
||||
yield line.strip("\n")
|
||||
|
||||
def re_write_all_keyword_to_py_file(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file:
|
||||
all_content = file.readlines()
|
||||
index = len(all_content)
|
||||
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file1:
|
||||
for line in file1:
|
||||
if "if __name__" in line and "__main__" in line:
|
||||
index = all_content.index(line)
|
||||
break
|
||||
|
||||
for i in range(index - 1, 0, -1):
|
||||
s = all_content[i]
|
||||
if all_content[i] != "\n":
|
||||
index = i
|
||||
all_content.insert(i + 1, "\n")
|
||||
break
|
||||
a = all_content[i + 1]
|
||||
with open(self.kw_file_path, "w", encoding="UTF-8") as file:
|
||||
file.writelines(all_content[:index + 1])
|
||||
|
||||
def get_all_exist_keywords(self):
|
||||
for item in self._get_file_content():
|
||||
kw_match = re.search(r"def\s+(kw_in{1}_\w+)\(", item)
|
||||
if kw_match:
|
||||
self.all_keywords.append(kw_match.group(1))
|
||||
return self.all_keywords
|
||||
|
||||
def get_all_exist_keywords_info(self):
|
||||
for item in self._get_file_content():
|
||||
kw_match = re.search(r"(kw_in{1}_\w+)\(", item)
|
||||
if kw_match:
|
||||
self.all_keywords.append(kw_match.group(1))
|
||||
return self.all_keywords
|
||||
|
||||
def write_content_to_interface_file(self, content, file_path=None):
|
||||
if not file_path:
|
||||
file_path = self.kw_file_path
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
f.write(content)
|
||||
|
||||
def generate_all_keyword_info(self):
|
||||
"""
|
||||
获取interface文件内已有关键字的url请求地址和请求方法
|
||||
"""
|
||||
temp_dict = {}
|
||||
all_content = self._get_file_content()
|
||||
server = ""
|
||||
for line in all_content:
|
||||
line_new = line
|
||||
if "def kw_in_" in line:
|
||||
kw_name = re.search("(kw_in_[\w+_]+)\(", line).group(1)
|
||||
if "tools.get_container_ip_from_eureka" in line or "tool.get_container_ip_from_eureka" in line:
|
||||
server = list(re.search("\([\"\']?([\w\-_]+)[\"\']?|\=[\"\']?([\w\-_]+)[\"\']?", line).groups())
|
||||
server.remove(None)
|
||||
server = server[0]
|
||||
if "self." in line and "=" in line:
|
||||
temp_dict[line.split("=")[0].strip()] = line.split("=")[1].strip().strip("\"")
|
||||
if " url =" in line or "url=" in line:
|
||||
log.info(line)
|
||||
try:
|
||||
# urls = list(re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line).groups())
|
||||
match = re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line)
|
||||
urls = list(match.groups()) if match else []
|
||||
except Exception as err:
|
||||
traceback.print_exc()
|
||||
continue
|
||||
# urls.remove(None)
|
||||
urls = [url for url in urls if url is not None]
|
||||
if not urls:
|
||||
continue
|
||||
url = urls[0]
|
||||
if "\\" in line:
|
||||
line_new = all_content.__next__()
|
||||
try:
|
||||
host = re.search("obj_runner\.([\w\-_]+)", line_new).group(1).strip().strip(")").strip(",")
|
||||
except:
|
||||
if "self." in line:
|
||||
if self.team.upper() in ["EN", "ASTWB", "ASOPE"]:
|
||||
host_header = "https://"
|
||||
else:
|
||||
host_header = "http://"
|
||||
server = re.search("self\.([\w\-_]+)", line_new).group(1).strip().strip(")").strip(",")
|
||||
server = server.replace("_", "-").lower()
|
||||
if self.team.upper() == 'TMO':
|
||||
host = host_header + "%s" % 'swagger' + ".qa.huohua.cn"
|
||||
elif server.upper() in host_servers:
|
||||
if server.upper() == "HULK-ORG-API":
|
||||
host = host_header + "%s" % server + ".qa.huohua.cn"
|
||||
else:
|
||||
host = host_header + "%s" % "api" + ".qa.allschool.com"
|
||||
else:
|
||||
if server.upper() in host_servers_except_api:
|
||||
host = host_header + "%s" % server + ".qa.allschool.com"
|
||||
else:
|
||||
host = host_header + "%s" % server + ".qa.huohua.cn"
|
||||
|
||||
if self.team.upper() == "TO":
|
||||
host = "teach_opt_host"
|
||||
if re.search("\{\}/\{\}", line) and "self." in line:
|
||||
para = re.search("(self\.[\w_]+)", line).group(1).strip().strip(")").strip(",")
|
||||
url = temp_dict[para] + urls[0]
|
||||
if "?" in url:
|
||||
url = url.split("?")[0]
|
||||
# if url.endswith("/"):
|
||||
# url = url[:len(url) - 1]
|
||||
|
||||
if server:
|
||||
if self.team.upper() in special_team and self.team.upper() != 'TMO':
|
||||
if url.startswith("/"):
|
||||
url = host + "%s" % url
|
||||
else:
|
||||
url = host + "/%s" % url
|
||||
elif self.team.upper() == 'TMO':
|
||||
if url.startswith("/"):
|
||||
url = '%s/%s%s' % (host, server, url)
|
||||
else:
|
||||
url = '%s/%s/%s' % (host, server, url)
|
||||
else:
|
||||
try:
|
||||
if url.startswith("/"):
|
||||
url = eval("self.runner.%s" % host) + "/%s" % server + url
|
||||
else:
|
||||
url = eval("self.runner.%s" % host) + "/%s/" % server + url
|
||||
except Exception as err:
|
||||
url = "empty_url"
|
||||
else:
|
||||
try:
|
||||
if url.startswith("/"):
|
||||
url1 = eval("self.runner.%s" % host)
|
||||
url = url1 + url
|
||||
else:
|
||||
url = eval("self.runner.%s" % host) + url
|
||||
except Exception as err:
|
||||
url = "empty_url"
|
||||
|
||||
if "req_type=" in line:
|
||||
method = re.search('req_type="(\w+)"|req_type=\'(\w+)\'', line.replace("\'", "\"")).group(1)
|
||||
self.all_url[url] = [method, kw_name]
|
||||
server = ""
|
||||
return self.all_url
|
||||
|
||||
|
||||
class KWOperation(object):
|
||||
"""
|
||||
根据数据库内容生成对应的keywords
|
||||
"""
|
||||
|
||||
def __init__(self, team, kw_instance, server=None):
|
||||
self.team = team
|
||||
self.doc_string_table_content_temp = " |{}|{}|{}|{}|{}|"
|
||||
self.function_name = ""
|
||||
self.help_doc = None
|
||||
self.function_content = None
|
||||
self.has_kw_list = []
|
||||
self.function_list = []
|
||||
self.file = kw_instance
|
||||
self.all_keywords = self.file.get_all_exist_keywords()
|
||||
self.all_url = self.file.generate_all_keyword_info()
|
||||
self.kw_name = None
|
||||
self.kw_string = None
|
||||
self.kw_array = None
|
||||
self.server = server
|
||||
|
||||
def get_interface_demo(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_request_demo_info_by_interface_id(interface_id)
|
||||
|
||||
def get_interface_parameters(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_parameters_by_interface_id(interface_id)
|
||||
|
||||
def get_swagger_info_by_id(self, swagger_id):
|
||||
return SwaggerDBInfo.get_team_swagger_info_by_id(swagger_id)
|
||||
|
||||
def _check_kw_exist(self, keywordname):
|
||||
if keywordname in self.all_keywords:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def generate_kw_name(self, interface_info, demo):
|
||||
"""
|
||||
生成对应关键字方法名称如kw_in_to_get_leads_get(**kwargs)
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
_, name_list, _, _ = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
if "" in name_list:
|
||||
name_list.remove("")
|
||||
name_list.append(interface_info.name)
|
||||
for item in add_api_list:
|
||||
if item in interface_info.in_url:
|
||||
name_list[-1] = 'api_' + name_list[-1]
|
||||
count = -1
|
||||
self.kw_name = "kw_in_{}_{}_{}".format(self.team.lower(), name_list[-1], interface_info.type.lower())
|
||||
while abs(count) <= len(name_list):
|
||||
if self._check_kw_exist(self.kw_name):
|
||||
if lenth > 1:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if self.team.upper() in ["TMO"]:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if demo_result:
|
||||
if isinstance(list(demo_result.values())[0], list):
|
||||
self.function_name = " def {kw_name}(self, *args, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
self.all_keywords.append(self.kw_name)
|
||||
return True
|
||||
else:
|
||||
count -= 1
|
||||
self.kw_name += "_" + name_list[count]
|
||||
else:
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
return False
|
||||
|
||||
def generate_kw_doc(self, interface_info, parameters_list):
|
||||
"""
|
||||
根据接口参数信息生成对应的帮助文档
|
||||
"""
|
||||
|
||||
def set_lenth(para):
|
||||
return " " + str(para).strip() + " "
|
||||
def get_normal_value(normal_values):
|
||||
return normal_values.split(",")[0] if normal_values else 0
|
||||
|
||||
doc = []
|
||||
doc_string_head = " \"\"\""
|
||||
doc_string_table_head = " | {} | {} | {} | {} | {} |".format("请求参数名".ljust(30), "说明".ljust(15),
|
||||
"类型".ljust(12), "是否必填".ljust(8),
|
||||
"如无要求时的值".ljust(8))
|
||||
doc.append(doc_string_head)
|
||||
doc.append(" | 功能说明:| {} |".format(interface_info.interface_describe))
|
||||
doc.append(doc_string_table_head)
|
||||
# if self.team.upper() not in ["TMO"]:
|
||||
# doc.append(self.doc_string_table_content_temp.format(set_lenth("is_check"),
|
||||
# set_lenth("is_check默认空不校验返回,有值就校验返回"),
|
||||
# set_lenth("string"),
|
||||
# set_lenth("业务case的时候需要传入值"),
|
||||
# set_lenth("False")))
|
||||
for parameter in parameters_list:
|
||||
attr = SetAttr(parameter)
|
||||
if int(attr.in_body) != 0 and int(attr.is_need) != 2:
|
||||
doc.append(self.doc_string_table_content_temp.format(set_lenth(attr.name), set_lenth(attr.note),
|
||||
set_lenth(attr.type), set_lenth(attr.is_need),
|
||||
set_lenth(get_normal_value(attr.normal_values))))
|
||||
doc.append(doc_string_head)
|
||||
return "\n".join(doc)
|
||||
|
||||
def _generate_all_url_paramaters(self, url):
|
||||
"""
|
||||
拆解url生成host, 生成keyword名的相关名称和url中带{}的参数
|
||||
"""
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
search.pop(0)
|
||||
url_lasr_temp = url_last.split("/")
|
||||
url_lasr_temp.pop(1)
|
||||
url_last = '/'.join(url_lasr_temp)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return host, head_list, para_list, url_last
|
||||
|
||||
def _generate_url_host(self, host):
|
||||
"""
|
||||
根据host生成对应的url后缀,比如teach_opt_host
|
||||
如果需要将host替换成IP则生成对应的IP
|
||||
"""
|
||||
url_host = ""
|
||||
if 'peppa-cc-manage' in host:
|
||||
url_host = "cc_host"
|
||||
elif 'crmv2' in host or 'smm' in host or 'xxljob' in host:
|
||||
url_host = "crm_host"
|
||||
elif 'scm' in host:
|
||||
url_host = "scm_host"
|
||||
elif "la-gate" in host:
|
||||
url_host = "insights_host"
|
||||
elif 'la-ai-api-bg' in host or 'la-api-ai' in host:
|
||||
url_host = "spark_land_host"
|
||||
elif 'manage' in host:
|
||||
url_host = "manage_host"
|
||||
elif 'teach' in host:
|
||||
url_host = "teach_host"
|
||||
elif 'teach-opt-api' in host or 'sparkle-manage' in host:
|
||||
url_host = "teach_opt_host"
|
||||
elif 'opengalaxy' in host:
|
||||
url_host = "opengalaxy_host"
|
||||
elif 'employee-manage' in host:
|
||||
url_host = "ehr_host"
|
||||
elif 'peppa-agent-manage' in host:
|
||||
url_host = "hhr_host"
|
||||
elif 'peppa-agent-api' in host:
|
||||
url_host = "hhr_api_host"
|
||||
elif "teach-message-api" in host:
|
||||
url_host = "teacher_message_host"
|
||||
elif "swagger" in host:
|
||||
url_host = "swagger_host"
|
||||
elif "peppa-qi-api" in host:
|
||||
url_host = "qi_host"
|
||||
elif "scm-server" in host:
|
||||
url_host = "scm_server_host"
|
||||
elif "scm-biz-server" in host:
|
||||
url_host = "scm_biz_server_host"
|
||||
elif "cti-manage" in host:
|
||||
url_host = "cti_host"
|
||||
elif "peppa-conversion-api" in host:
|
||||
url_host = "uc_host"
|
||||
elif "la-api" in host:
|
||||
url_host = "la_api_host"
|
||||
elif "hulk_content_audit_server" in host:
|
||||
url_host = "hulk_content_audit"
|
||||
elif "ccwx-api" in host:
|
||||
url_host = "cc_weixin_host"
|
||||
|
||||
return url_host
|
||||
|
||||
def generate_function_url(self, interface_info, demo):
|
||||
"""
|
||||
生成对应的url
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
url_list = []
|
||||
user_kwargs = " user, kwargs = get_user(kwargs)"
|
||||
url_list.append(user_kwargs)
|
||||
check_json = " kwargs = convert_json(kwargs)"
|
||||
if paras_d == "args":
|
||||
check_json = " args = convert_json(args)"
|
||||
url_list.append(check_json)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
host = re.findall("[\w+-]+", host)
|
||||
url_host = self._generate_url_host(host)
|
||||
server = ""
|
||||
# if self.team.upper() in ["TMO"]:
|
||||
# url_list.append(" try:\n kwargs = eval(args[0])\n except:")
|
||||
# url_list.append(" try:\n if args[0] and isinstance(args[0], dict):")
|
||||
# url_list.append("kwargs = args[0]".rjust(20 + len("kwargs = args[0]"), " "))
|
||||
# url_list.append("except:".rjust(12 + len("except:"), " "))
|
||||
# url_list.append("kwargs = kwargs".rjust(16 + len("kwargs = kwargs"), " "))
|
||||
# url_last_tmp = url_last.split("/")
|
||||
# server = url_last_tmp.pop(1)
|
||||
# url_last = "/".join(url_last_tmp)
|
||||
# ip = " ip = tools.get_container_ip_from_eureka(\"%s\", need_jira_id=True)" % server
|
||||
# url_list.append(ip)
|
||||
# host = " obj_runner." + url_host + " = \"http://\" + " + "ip[\"container_ip\"]" + " + \":8080\""
|
||||
# url_list.append(host)
|
||||
if self.team.upper() in special_team:
|
||||
if self.server.upper() in host_servers or self.team.upper() in ["TMO"]:
|
||||
host_server = self.server.lower().replace("-", "_")
|
||||
else:
|
||||
host_server = host[1].replace("-", "_")
|
||||
url_host = "self.%s" % host_server
|
||||
else:
|
||||
if self.server.upper() in host_servers or self.team.upper() in ["TMO"]:
|
||||
host_server = self.server.lower().replace("-", "_")
|
||||
url_host = "self.%s" % host_server
|
||||
# if self.team.upper() == 'LALIVE' and 'la-api' in host:
|
||||
# url_last = url_last.replace("smart", "api/smart")
|
||||
if "PEPPA-QI-API".lower() in host:
|
||||
ip = " obj_runner.%s = \"http://{}:8080\".format(self.get_container_ip('PEPPA-QI-API'))" % url_host
|
||||
url_list.append(ip)
|
||||
if parameters:
|
||||
if lenth > 1:
|
||||
temp = 'kwargs.get("u")'
|
||||
else:
|
||||
temp = 'kwargs'
|
||||
url = " url = \"%s" + url_last + "\".format("
|
||||
for item in parameters:
|
||||
url += "%s=%s, " % (item, "%s.get(\"%s\", \"\")" % (temp, item))
|
||||
url += ") % "
|
||||
else:
|
||||
url = " url = \"%s" + url_last + "\" % "
|
||||
|
||||
if self.team.upper() in special_team or self.server.upper() in host_servers:
|
||||
url += url_host
|
||||
else:
|
||||
url += "obj_runner.%s" % url_host
|
||||
url_list.append(url)
|
||||
return "\n".join(url_list), server
|
||||
|
||||
def generate_function_body(self, interface_info, demo, parameters_all):
|
||||
"""
|
||||
生成关键字的body内容
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
func_body = []
|
||||
if re.search(r"\*(\w+)", self.function_name):
|
||||
paras = re.search(r"\*(\w+)", self.function_name).group(1)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
paras = "kwargs"
|
||||
content = ' obj_log.info("your input:{0}".format(%s))' % paras
|
||||
func_body.append(content)
|
||||
if self.kw_string:
|
||||
func_body.append(" kwargs = kwargs.get('%s', '')" % parameters_all[0].get("name"))
|
||||
if self.kw_array:
|
||||
func_body.append(" kwargs = eval(kwargs.pop('%s'))" % parameters_all[0].get("name"))
|
||||
else:
|
||||
paras = None
|
||||
if lenth > 1:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", ' % interface_info.type
|
||||
if "d" in demo_result:
|
||||
resp = resp + 'json=kwargs.get("d"), '
|
||||
if "p" in demo_result:
|
||||
resp = resp + 'params=kwargs.get("p"), '
|
||||
if "h" in demo_result:
|
||||
resp = resp + 'headers=kwargs.get("h"), '
|
||||
resp = resp + 'user=user)'
|
||||
else:
|
||||
if demo_result:
|
||||
if "d" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", json=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "h" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", headers=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "p" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", params=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "u" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
else:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
|
||||
if self.team.upper() in special_team or check_server_host(token_false_server, host) or \
|
||||
self.team.upper() in ["TMO"]:
|
||||
resp = resp.replace("user=user", "token=False")
|
||||
try:
|
||||
if self.server.upper() in host_servers:
|
||||
resp = resp.replace("token=False", "token=False, user=user")
|
||||
if self.server.upper() == "HULK-ORG-API":
|
||||
resp = resp.replace("user=user", "user=user, as_login_type=2")
|
||||
if self.server.upper() in host_servers_except_api:
|
||||
resp = resp.replace("token=False", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
if str(self.server).lower() in header_servers:
|
||||
header = " header = {'debug-param': 'huangliye@huohua.cn'}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'huangliye@huohua.cn'})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in eid_server:
|
||||
header = " header = {'debug-param': 'eid:%s' % self.eid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'eid:%s' % self.eid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in uid_server:
|
||||
header = " header = {'debug-param': 'uid:%s' % self.uid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'uid:%s' % self.uid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
func_body.append(resp)
|
||||
# if self.team.upper() not in ["TMO"] and paras_d == "kwargs":
|
||||
# func_body.append(" check_resp(is_check, resp)")
|
||||
func_body.append(" return resp\n")
|
||||
|
||||
return "\n".join(func_body)
|
||||
|
||||
def get_has_keyword_id_in_db(self):
|
||||
"""
|
||||
获取所有已有
|
||||
"""
|
||||
keyword_list = []
|
||||
for key in self.all_url.keys():
|
||||
interface_name = key.split("/")[-1]
|
||||
sql = "select * from sparkatp.interface_info where name = '%s' and type = '%s'" % (
|
||||
interface_name, self.all_url[key][0])
|
||||
interface_infos = SwaggerDBInfo.get_db_info_by_sql(sql=sql)
|
||||
for interface_info in interface_infos:
|
||||
interface_attr = SetAttr(interface_info)
|
||||
if key.lower() in interface_attr.in_url.lower():
|
||||
keyword_list.append(interface_attr.id)
|
||||
|
||||
return keyword_list
|
||||
|
||||
|
||||
def run_keyword_generage(result_path, interface, demo_info, parameters, keyword):
|
||||
if demo_info.request_demo:
|
||||
demo_info = json.loads(demo_info.request_demo)
|
||||
else:
|
||||
demo_info = {}
|
||||
if keyword.all_url.get(interface.in_url) and keyword.all_url.get(interface.in_url)[
|
||||
0].lower() == interface.type.lower():
|
||||
msg = "接口url:%s,%s,已存在关键字,继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return True
|
||||
else:
|
||||
exist = keyword.generate_kw_name(interface, demo_info)
|
||||
if exist:
|
||||
keyword.function_list = []
|
||||
doc = keyword.generate_kw_doc(interface, parameters)
|
||||
url, temp = keyword.generate_function_url(interface, demo_info)
|
||||
content = keyword.generate_function_body(interface, demo_info, parameters)
|
||||
keyword.function_list.append("\n".join(("", keyword.function_name, doc, url, content)))
|
||||
keyword.file.re_write_all_keyword_to_py_file()
|
||||
keyword.file.write_content_to_interface_file("\n".join(keyword.function_list))
|
||||
keyword.file.all_url[interface.in_url] = [interface.type, keyword.kw_name]
|
||||
return True
|
||||
else:
|
||||
msg = "接口url:%s,%s,无法生成关键字,请检查!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return False
|
||||
@@ -0,0 +1,666 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import importlib.util as i_util
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from base_framework.platform_tools.Keywords_service import special_team
|
||||
|
||||
token_false_server = ["scm-server", "scm-biz-server", "peppa-qi-api", "lggzt", "cti-manage", "ccwx-api"]
|
||||
add_api_list = ["peppa-teach-api"]
|
||||
host_servers = ["HULK-ORG-API", "HULK-TEACHER-API"]
|
||||
host_servers_except_api = ["HULK-CONTENT-AUDIT-SERVER"]
|
||||
header_servers = ["hulk-operation-api-server", "hulk-teach-supply-cli-api", "hulk-teach-backend-api"]
|
||||
uid_server = ['SPARKEDU-API', 'SPARKEDU-SITE-API']
|
||||
eid_server = ['SPARKEDU-SITE-MANAGE', 'SPARKEDU-SITE-SCHEDULER']
|
||||
|
||||
def check_server_host(servers, host):
|
||||
"""
|
||||
检查不需要token的server是否存在url中
|
||||
"""
|
||||
for server in servers:
|
||||
if server in host:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
|
||||
from base_framework.public_tools.log import get_logger
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
|
||||
db = MySqLHelper()
|
||||
log = get_logger()
|
||||
|
||||
|
||||
def get_classes(module):
|
||||
classes = []
|
||||
cls_members = inspect.getmembers(module, inspect.isclass)
|
||||
for (name, _) in cls_members:
|
||||
classes.append(name)
|
||||
return classes
|
||||
|
||||
|
||||
def get_module_by_file_path(file_path):
|
||||
spec = importlib.util.spec_from_file_location("module_name", file_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class SetAttr(object):
|
||||
|
||||
def __init__(self, paras):
|
||||
for key, values in paras.items():
|
||||
self._add_attr(key, values)
|
||||
|
||||
def _add_attr(self, key, value):
|
||||
self.__setattr__(key, value)
|
||||
|
||||
|
||||
class SwaggerDBInfo(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_team(teamname):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where team = '%s'" % teamname
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_id(swagger_id):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where id = '%s'" % swagger_id
|
||||
return db.select_one(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_info_by_url_and_type(request_url, request_type, team):
|
||||
sql = 'select * from sparkatp.interface_info where in_url = "%s" and type = "%s" and swagger_id in (select id from sparkatp.swagger_info where team = "%s")' % (request_url, request_type, team)
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_parameters_by_interface_id(interface_id):
|
||||
sql = "select * from sparkatp.request_parameters a right join sparkatp.parameters_relation b on a.id = b.parameter_id where a.interface_id = {} and b.type = 'request' and a.offline=0".format(
|
||||
interface_id)
|
||||
return db.select_all(sql=sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_request_demo_info_by_interface_id(interface_id):
|
||||
sql = 'SELECT * FROM sparkatp.parameters_demo where in_id = "%s" order by id DESC' % interface_id
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_swagger_url_by_team_and_server(team, server):
|
||||
sql = "select * from sparkatp.swagger_info where team='%s' and server_name = '%s';" % (team, server)
|
||||
return db.select_one(sql, choose_db="huohua")
|
||||
|
||||
|
||||
class KWFileOperation(object):
|
||||
"""
|
||||
操作xx_interface.py文件
|
||||
"""
|
||||
|
||||
def __init__(self, team, server):
|
||||
self.kw_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
self.team = team
|
||||
self.server = server
|
||||
self.all_url = {}
|
||||
self.all_keywords = []
|
||||
self.runner = self._get_obj_runner()
|
||||
self.file_content = self.re_write_all_keyword_to_py_file()
|
||||
self.check_server_and_rewrite_server()
|
||||
self.get_all_exist_keywords()
|
||||
self.generate_all_keyword_info()
|
||||
self.kw_class = self._get_interface_class_name(self.kw_file_path)
|
||||
|
||||
def check_server_and_rewrite_server(self):
|
||||
"""
|
||||
检查server是否在interface.py文件中配置获取IP
|
||||
"""
|
||||
start_index = 0
|
||||
empty_index = 0
|
||||
server_host = self.server.lower().replace('-', '_')
|
||||
server_upper = self.server.upper()
|
||||
if "self.%s =" % server_host in ''.join(self.file_content) or '':
|
||||
return
|
||||
for line in self.file_content:
|
||||
if "def __init__(self):" in line:
|
||||
start_index = self.file_content.index(line) + 1
|
||||
log.info(start_index)
|
||||
if start_index == 0:
|
||||
continue
|
||||
if " def " in line and "__init__" not in line:
|
||||
end_index = self.file_content.index(line)
|
||||
log.info(end_index)
|
||||
break
|
||||
if "pass" in self.file_content[start_index]:
|
||||
self.file_content[
|
||||
start_index] = ' self.need_jira_id = True if ReadConfig(env_choose_path).get_value("run_jira_id", "huohua-podenv") else False\n'
|
||||
elif 'self.need_jira_id = True if ' not in self.file_content[start_index] and "pass" in self.file_content[
|
||||
start_index]:
|
||||
self.file_content.insert(start_index,
|
||||
' self.need_jira_id = True if ReadConfig(env_choose_path).get_value("run_jira_id", "huohua-podenv") else False\n')
|
||||
for item in range(start_index, end_index):
|
||||
if self.file_content[item] == "\n":
|
||||
self.file_content.insert(item,
|
||||
" self.%s_ip = obj_tool.get_container_ip_from_eureka('%s', need_jira_id=self.need_jira_id)\n" % (
|
||||
server_host, server_upper))
|
||||
|
||||
self.file_content.insert(item + 1,
|
||||
' self.%s = "http://" + self.%s_ip["container_ip"] + ":8080"\n' % (
|
||||
server_host, server_host))
|
||||
break
|
||||
with open(self.kw_file_path, "w", encoding="UTF-8") as file:
|
||||
for line1 in self.file_content:
|
||||
file.writelines(line1)
|
||||
|
||||
|
||||
def _clean_demo_to_less_key(self, demo):
|
||||
demo_result = copy.deepcopy(demo)
|
||||
for key, value in demo.items():
|
||||
if not value:
|
||||
demo_result.pop(key)
|
||||
lenth = len(demo_result)
|
||||
paras = "kwargs"
|
||||
if lenth == 1 and isinstance(list(demo_result.values())[0], list):
|
||||
paras = "args"
|
||||
return lenth, demo_result, paras
|
||||
|
||||
def _get_interface_class_name(self, file_path):
|
||||
module = get_module_by_file_path(file_path)
|
||||
classes = get_classes(module)
|
||||
for class_ in classes:
|
||||
if "interface" in class_.lower():
|
||||
return class_
|
||||
|
||||
def _get_obj_runner(self):
|
||||
spec = i_util.spec_from_file_location("module_name", self.kw_file_path)
|
||||
module = i_util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
runner = inspect.getmembers(module)
|
||||
for module in runner:
|
||||
if module[0] == "obj_runner":
|
||||
return module[1]
|
||||
return runner
|
||||
|
||||
def _get_file_content(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file:
|
||||
all_content = file.readlines()
|
||||
|
||||
return all_content
|
||||
|
||||
def re_write_all_keyword_to_py_file(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file:
|
||||
all_content = file.readlines()
|
||||
index = len(all_content)
|
||||
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file1:
|
||||
for line in file1:
|
||||
if "if __name__" in line and "__main__" in line:
|
||||
index = all_content.index(line)
|
||||
break
|
||||
|
||||
for i in range(index - 1, 0, -1):
|
||||
s = all_content[i]
|
||||
if all_content[i] != "\n":
|
||||
index = i
|
||||
all_content.insert(i + 1, "\n")
|
||||
break
|
||||
a = all_content[i + 1]
|
||||
with open(self.kw_file_path, "w", encoding="UTF-8") as file:
|
||||
file.writelines(all_content[:index + 1])
|
||||
return all_content[:index + 1]
|
||||
|
||||
def get_all_exist_keywords(self):
|
||||
for item in self.file_content:
|
||||
kw_match = re.search(r"def\s+(kw_in{1}_\w+)\(", item)
|
||||
if kw_match:
|
||||
self.all_keywords.append(kw_match.group(1))
|
||||
return self.all_keywords
|
||||
|
||||
def write_content_to_interface_file(self, content, file_path=None):
|
||||
if not file_path:
|
||||
file_path = self.kw_file_path
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
f.write(content)
|
||||
|
||||
def generate_all_keyword_info(self):
|
||||
"""
|
||||
获取interface文件内已有关键字的url请求地址和请求方法
|
||||
"""
|
||||
temp_dict = {}
|
||||
all_content = self.file_content
|
||||
server = False
|
||||
kw_line = False
|
||||
url_line = False
|
||||
host = False
|
||||
for line in all_content:
|
||||
if "def kw_in_" in line:
|
||||
kw_line = True
|
||||
kw_name = re.search("(kw_in_[\w+_]+)\(", line).group(1)
|
||||
if kw_line:
|
||||
try:
|
||||
if "get_container_ip" in line:
|
||||
server = list(re.search(r'get_container_ip.*\([\'\"]([\w\-_]+)[\'\"]', line).groups())
|
||||
server = server[0]
|
||||
except Exception as err:
|
||||
pass
|
||||
if "self." in line and "=" in line:
|
||||
temp_dict[line.split("=")[0].strip()] = line.split("=")[1].strip().strip("\"")
|
||||
if (" url =" in line or "url=" in line) and 'url +' not in line:
|
||||
log.info(line)
|
||||
try:
|
||||
urls = list(re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line).groups())
|
||||
urls.remove(None)
|
||||
except Exception as err:
|
||||
log.info("error: %s" % line)
|
||||
url_line = True
|
||||
if url_line:
|
||||
if 'url = "%s/api/customer/{customerId}/give_up"' in line:
|
||||
print()
|
||||
if not host:
|
||||
try:
|
||||
host_key = re.search("obj_runner\.([\w\-_]+)", line).group(1).strip().strip(")").strip(",")
|
||||
host = eval("self.runner.%s" % host_key)
|
||||
except:
|
||||
if not server:
|
||||
if "self." in line:
|
||||
server = re.search("self\.([\w\-_]+)", line).group(1).strip().strip(")").strip(",")
|
||||
server = server.replace("_", "-").lower()
|
||||
if server:
|
||||
try:
|
||||
host_temp = SwaggerDBInfo.get_swagger_url_by_team_and_server(self.team, server)['sw_url']
|
||||
host = host_temp.split("/v2")[0]
|
||||
except Exception as err:
|
||||
host = ""
|
||||
if host:
|
||||
url = host + urls[0]
|
||||
|
||||
if "req_type=" in line:
|
||||
method = re.search('req_type="(\w+)"|req_type=\'(\w+)\'', line.replace("\'", "\"")).group(1)
|
||||
self.all_url[url] = [method, kw_name]
|
||||
server = False
|
||||
kw_line = False
|
||||
url_line = False
|
||||
host = False
|
||||
return self.all_url
|
||||
|
||||
|
||||
class KWOperation(object):
|
||||
"""
|
||||
根据数据库内容生成对应的keywords
|
||||
"""
|
||||
|
||||
def __init__(self, team, kw_instance):
|
||||
self.team = team
|
||||
self.doc_string_table_content_temp = " |{}|{}|{}|{}|{}|"
|
||||
self.function_name = ""
|
||||
self.help_doc = None
|
||||
self.function_content = None
|
||||
self.has_kw_list = []
|
||||
self.function_list = []
|
||||
self.file = kw_instance
|
||||
self.all_keywords = self.file.all_keywords
|
||||
self.all_url = self.file.all_url
|
||||
self.kw_name = None
|
||||
self.kw_string = None
|
||||
self.server = kw_instance.server
|
||||
|
||||
def get_interface_demo(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_request_demo_info_by_interface_id(interface_id)
|
||||
|
||||
def get_interface_parameters(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_parameters_by_interface_id(interface_id)
|
||||
|
||||
def get_swagger_info_by_id(self, swagger_id):
|
||||
return SwaggerDBInfo.get_team_swagger_info_by_id(swagger_id)
|
||||
|
||||
def _check_kw_exist(self, keywordname):
|
||||
if keywordname in self.all_keywords:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def generate_kw_name(self, interface_info, demo):
|
||||
"""
|
||||
生成对应关键字方法名称如kw_in_to_get_leads_get(is_check='', **kwargs)
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
_, name_list, _, _ = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
if "" in name_list:
|
||||
name_list.remove("")
|
||||
name_list.append(interface_info.name)
|
||||
for item in add_api_list:
|
||||
if item in interface_info.in_url:
|
||||
name_list[-1] = 'api_' + name_list[-1]
|
||||
count = -1
|
||||
self.kw_name = "kw_in_{}_{}_{}".format(self.team.lower(), name_list[-1], interface_info.type.lower())
|
||||
while abs(count) <= len(name_list):
|
||||
if self._check_kw_exist(self.kw_name):
|
||||
if lenth > 1:
|
||||
self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if self.team.upper() in ["TMO"]:
|
||||
self.function_name = " def {kw_name}(self, *args, **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if demo_result:
|
||||
if isinstance(list(demo_result.values())[0], list):
|
||||
self.function_name = " def {kw_name}(self, *args, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
self.all_keywords.append(self.kw_name)
|
||||
return True
|
||||
else:
|
||||
count -= 1
|
||||
self.kw_name += "_" + name_list[count]
|
||||
else:
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
return False
|
||||
|
||||
def generate_kw_doc(self, interface_info, parameters_list):
|
||||
"""
|
||||
根据接口参数信息生成对应的帮助文档
|
||||
"""
|
||||
|
||||
def set_lenth(para):
|
||||
return " " + str(para).strip() + " "
|
||||
|
||||
doc = []
|
||||
doc_string_head = " \"\"\""
|
||||
doc_string_table_head = " | {}| {}| {}| {}| {}|".format("请求参数名".ljust(30), "说明".ljust(15),
|
||||
"类型".ljust(12), "条件".ljust(10),
|
||||
"是否必填".ljust(8))
|
||||
doc.append(doc_string_head)
|
||||
doc.append(" {} + {} + interface id: {}".format(interface_info.interface_describe, interface_info.type,
|
||||
interface_info.id))
|
||||
doc.append(" url: " + interface_info.in_url)
|
||||
doc.append(doc_string_table_head)
|
||||
if self.team.upper() not in ["TMO"]:
|
||||
doc.append(self.doc_string_table_content_temp.format(set_lenth("is_check"),
|
||||
set_lenth("is_check默认空不校验返回,有值就校验返回"),
|
||||
set_lenth("string"),
|
||||
set_lenth("业务case的时候需要传入值"),
|
||||
set_lenth("False")))
|
||||
for parameter in parameters_list:
|
||||
attr = SetAttr(parameter)
|
||||
if int(attr.in_body) != 0 and int(attr.is_need) != 2:
|
||||
doc.append(self.doc_string_table_content_temp.format(set_lenth(attr.name), set_lenth(attr.note),
|
||||
set_lenth(attr.type), set_lenth(attr.p_condition),
|
||||
set_lenth(attr.is_need)))
|
||||
doc.append(doc_string_head)
|
||||
return "\n".join(doc)
|
||||
|
||||
def _generate_all_url_paramaters(self, url):
|
||||
"""
|
||||
拆解url生成host, 生成keyword名的相关名称和url中带{}的参数
|
||||
"""
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return host, head_list, para_list, url_last
|
||||
|
||||
def _generate_url_host(self):
|
||||
"""
|
||||
根据host生成对应的url后缀,比如teach_opt_host
|
||||
如果需要将host替换成IP则生成对应的IP
|
||||
"""
|
||||
server_temp = self.server.lower().replace('-', '_')
|
||||
url_host = 'self.%s' % server_temp
|
||||
# if self.server:
|
||||
# url_host = ""
|
||||
# url_host = ""
|
||||
# if 'peppa-cc-manage' in host:
|
||||
# url_host = "cc_host"
|
||||
# elif 'crmv2' in host or 'smm' in host or 'xxljob' in host:
|
||||
# url_host = "crm_host"
|
||||
# elif 'scm' in host:
|
||||
# url_host = "scm_host"
|
||||
# elif "la-gate" in host:
|
||||
# url_host = "insights_host"
|
||||
# elif 'la-ai-api-bg' in host or 'la-api-ai' in host:
|
||||
# url_host = "spark_land_host"
|
||||
# elif 'manage' in host:
|
||||
# url_host = "manage_host"
|
||||
# elif 'teach' in host:
|
||||
# url_host = "teach_host"
|
||||
# elif 'teach-opt-api' in host or 'sparkle-manage' in host:
|
||||
# url_host = "teach_opt_host"
|
||||
# elif 'opengalaxy' in host:
|
||||
# url_host = "opengalaxy_host"
|
||||
# elif 'employee-manage' in host:
|
||||
# url_host = "ehr_host"
|
||||
# elif 'peppa-agent-manage' in host:
|
||||
# url_host = "hhr_host"
|
||||
# elif 'peppa-agent-api' in host:
|
||||
# url_host = "hhr_api_host"
|
||||
# elif "teach-message-api" in host:
|
||||
# url_host = "teacher_message_host"
|
||||
# elif "swagger" in host:
|
||||
# url_host = "swagger_host"
|
||||
# elif "peppa-qi-api" in host:
|
||||
# url_host = "qi_host"
|
||||
# elif "scm-server" in host:
|
||||
# url_host = "scm_server_host"
|
||||
# elif "scm-biz-server" in host:
|
||||
# url_host = "scm_biz_server_host"
|
||||
# elif "cti-manage" in host:
|
||||
# url_host = "cti_host"
|
||||
# elif "peppa-conversion-api" in host:
|
||||
# url_host = "uc_host"
|
||||
# elif "la-api" in host:
|
||||
# url_host = "la_api_host"
|
||||
# elif "hulk_content_audit_server" in host:
|
||||
# url_host = "hulk_content_audit"
|
||||
|
||||
return url_host
|
||||
|
||||
def generate_function_url(self, interface_info, demo):
|
||||
"""
|
||||
生成对应的url
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
url_list = []
|
||||
user_kwargs = " user, kwargs = get_user(kwargs)"
|
||||
url_list.append(user_kwargs)
|
||||
check_json = " kwargs = convert_json(kwargs)"
|
||||
if paras_d == "args":
|
||||
check_json = " args = convert_json(args)"
|
||||
url_list.append(check_json)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
host = re.findall("[\w+-]+", host)
|
||||
url_host = self._generate_url_host()
|
||||
server = ""
|
||||
if self.team.upper() in ["TMO"]:
|
||||
url_list.append(" try:\n kwargs = eval(args[0])\n except:")
|
||||
url_list.append(" try:\n if args[0] and isinstance(args[0], dict):")
|
||||
url_list.append("kwargs = args[0]".rjust(20 + len("kwargs = args[0]"), " "))
|
||||
url_list.append("except:".rjust(12 + len("except:"), " "))
|
||||
url_list.append("kwargs = kwargs".rjust(16 + len("kwargs = kwargs"), " "))
|
||||
url_last_tmp = url_last.split("/")
|
||||
server = url_last_tmp.pop(1)
|
||||
url_last = "/".join(url_last_tmp)
|
||||
|
||||
if parameters:
|
||||
if lenth > 1:
|
||||
temp = 'kwargs.get("u")'
|
||||
else:
|
||||
temp = 'kwargs'
|
||||
url = " url = \"%s" + url_last + "\".format("
|
||||
for item in parameters:
|
||||
url += "%s=%s, " % (item, "%s.get(\"%s\", \"\")" % (temp, item))
|
||||
url += ") % "
|
||||
else:
|
||||
url = " url = \"%s" + url_last + "\" % "
|
||||
|
||||
url += url_host
|
||||
url_list.append(url)
|
||||
return "\n".join(url_list), server
|
||||
|
||||
def generate_function_body(self, interface_info, demo, parameters_all):
|
||||
"""
|
||||
生成关键字的body内容
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
func_body = []
|
||||
if re.search(r"\*(\w+)", self.function_name):
|
||||
paras = re.search(r"\*(\w+)", self.function_name).group(1)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
paras = "kwargs"
|
||||
content = ' obj_log.info("your input:{0}".format(%s))' % paras
|
||||
func_body.append(content)
|
||||
if self.kw_string:
|
||||
func_body.append(" kwargs = kwargs.get('%s', '')" % parameters_all[0].get("name"))
|
||||
else:
|
||||
paras = None
|
||||
if lenth > 1:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", ' % interface_info.type
|
||||
if "d" in demo_result:
|
||||
resp = resp + 'json=kwargs.get("d"), '
|
||||
if "p" in demo_result:
|
||||
resp = resp + 'params=kwargs.get("p"), '
|
||||
if "h" in demo_result:
|
||||
resp = resp + 'headers=kwargs.get("h"), '
|
||||
resp = resp + 'user=user)'
|
||||
else:
|
||||
if demo_result:
|
||||
if "d" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", json=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "h" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", headers=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "p" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", params=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "u" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
else:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
|
||||
if self.team.upper() in special_team or check_server_host(token_false_server, host) or self.team.upper() in [
|
||||
"TMO"]:
|
||||
resp = resp.replace("user=user", "token=False")
|
||||
try:
|
||||
if self.server.upper() in host_servers:
|
||||
resp = resp.replace("token=False", "token=False, user=user")
|
||||
if self.server.upper() == "HULK-ORG-API":
|
||||
resp = resp.replace("user=user", "user=user, as_login_type=2")
|
||||
if self.server.upper() in host_servers_except_api:
|
||||
resp = resp.replace("token=False", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
if str(self.server).lower() in header_servers:
|
||||
header = " header = {'debug-param': 'huangliye@huohua.cn'}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'huangliye@huohua.cn'})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in eid_server:
|
||||
header = " header = {'debug-param': 'eid:%s' % self.eid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'eid:%s' % self.eid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in uid_server:
|
||||
header = " header = {'debug-param': 'uid:%s' % self.uid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'uid:%s' % self.uid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
func_body.append(resp)
|
||||
if self.team.upper() not in ["TMO"] and paras_d == "kwargs":
|
||||
func_body.append(" check_resp(is_check, resp)")
|
||||
func_body.append(" return resp\n")
|
||||
|
||||
return "\n".join(func_body)
|
||||
|
||||
def get_has_keyword_id_in_db(self):
|
||||
"""
|
||||
获取所有已有
|
||||
"""
|
||||
keyword_list = []
|
||||
for key in self.all_url.keys():
|
||||
interface_name = key.split("/")[-1]
|
||||
sql = "select * from sparkatp.interface_info where name = '%s' and type = '%s'" % (
|
||||
interface_name, self.all_url[key][0])
|
||||
interface_infos = SwaggerDBInfo.get_db_info_by_sql(sql=sql)
|
||||
for interface_info in interface_infos:
|
||||
interface_attr = SetAttr(interface_info)
|
||||
if key.lower() in interface_attr.in_url.lower():
|
||||
keyword_list.append(interface_attr.id)
|
||||
|
||||
return keyword_list
|
||||
|
||||
|
||||
def run_keyword_generage(result_path, interface, demo_info, parameters, keyword):
|
||||
if demo_info.request_demo:
|
||||
demo_info = json.loads(demo_info.request_demo)
|
||||
else:
|
||||
demo_info = {}
|
||||
if keyword.all_url.get(interface.in_url) and keyword.all_url.get(interface.in_url)[
|
||||
0].lower() == interface.type.lower():
|
||||
msg = "接口url:%s,%s,已存在关键字,继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return True
|
||||
else:
|
||||
exist = keyword.generate_kw_name(interface, demo_info)
|
||||
if exist:
|
||||
keyword.function_list = []
|
||||
doc = keyword.generate_kw_doc(interface, parameters)
|
||||
url, temp = keyword.generate_function_url(interface, demo_info)
|
||||
content = keyword.generate_function_body(interface, demo_info, parameters)
|
||||
keyword.function_list.append("\n".join(("", keyword.function_name, doc, url, content)))
|
||||
keyword.file.re_write_all_keyword_to_py_file()
|
||||
keyword.file.write_content_to_interface_file("\n".join(keyword.function_list))
|
||||
keyword.file.all_url[interface.in_url] = [interface.type, keyword.kw_name]
|
||||
return True
|
||||
else:
|
||||
msg = "接口url:%s,%s,无法生成关键字,请检查!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
kw = KWFileOperation("CC", "peppa-qi-api")
|
||||
kw.check_server_and_rewrite_server()
|
||||
3
base_framework/platform_tools/Keywords_service/readme
Normal file
3
base_framework/platform_tools/Keywords_service/readme
Normal file
@@ -0,0 +1,3 @@
|
||||
目录结构说明:
|
||||
使用者:王刚
|
||||
用途:存放自动生成py文件关键字的脚本
|
||||
141
base_framework/platform_tools/Message_service/Feishu_api.py
Normal file
141
base_framework/platform_tools/Message_service/Feishu_api.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
功能:发送飞书消息接口
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
import urllib3
|
||||
urllib3.disable_warnings()
|
||||
import os
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
|
||||
try:
|
||||
JSONDecodeError = json.decoder.JSONDecodeError
|
||||
except AttributeError:
|
||||
JSONDecodeError = ValueError
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
msg_config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'msg_config.ini')
|
||||
read_config = ReadConfig(filename=msg_config_path)
|
||||
|
||||
|
||||
def get_feishu_config_options():
|
||||
"""
|
||||
功能:获取飞书配置文件信息
|
||||
option_key:配置文件中的key
|
||||
"""
|
||||
return read_config.get_options('feishu_user_list')
|
||||
|
||||
|
||||
def get_feishu_config_value(option_key):
|
||||
"""
|
||||
功能:获取飞书配置文件信息
|
||||
option_key:配置文件中的key
|
||||
"""
|
||||
return read_config.get_value(sections='feishu_user_list', options=option_key)
|
||||
|
||||
def get_user_name_by_email_prefix(email_prefix):
|
||||
"""
|
||||
功能:获取飞书配置文件中的邮箱前缀与中文姓名的对应关系
|
||||
email_prefix:邮箱前缀
|
||||
返回:对应的中文姓名
|
||||
"""
|
||||
return read_config.get_value(sections='user_name', options=email_prefix)
|
||||
|
||||
|
||||
class FeiShuMessage(object):
|
||||
def __init__(self, team='TO', secret=None, pc_slide=False, fail_notice=False):
|
||||
"""
|
||||
机器人初始化
|
||||
:param team: 业务组名,用于从msg_config配置文件中读取对应群组的webhook地址
|
||||
:param secret: 机器人安全设置页面勾选“加签”时需要传入的密钥
|
||||
:param pc_slide: 消息链接打开方式,默认False为浏览器打开,设置为True时为PC端侧边栏打开
|
||||
:param fail_notice: 消息发送失败提醒,默认为False不提醒,开发者可以根据返回的消息发送结果自行判断和处理
|
||||
"""
|
||||
super(FeiShuMessage, self).__init__()
|
||||
self.headers = {'Content-Type': 'application/json; charset=utf-8'}
|
||||
team_name = team.lower() + '_webhook_token'
|
||||
if team_name in get_feishu_config_options():
|
||||
token = get_feishu_config_value(option_key=team_name)
|
||||
self.webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/{0}".format(token)
|
||||
else:
|
||||
self.webhook = None
|
||||
logging.error("Team: {} not in msg_config.ini".format(team))
|
||||
return
|
||||
self.secret = secret
|
||||
self.pc_slide = pc_slide
|
||||
self.fail_notice = fail_notice
|
||||
|
||||
def send_text(self, msg):
|
||||
"""
|
||||
消息类型为text类型
|
||||
:param msg: 消息内容
|
||||
:return: 返回消息发送结果
|
||||
"""
|
||||
data = {"msg_type": "text"}
|
||||
if msg and self.webhook: # 传入msg非空
|
||||
data["content"] = {"text": msg}
|
||||
if "全部通过" in msg:
|
||||
# 不用发飞书消息
|
||||
logging.info("+++++++++ 全部构建成功,不发消息 ++++++++")
|
||||
return False
|
||||
return self.post(data)
|
||||
|
||||
def post(self, data):
|
||||
"""
|
||||
发送消息(内容UTF-8编码)
|
||||
:param data: 消息数据(字典)
|
||||
:return: 返回消息发送结果
|
||||
"""
|
||||
try:
|
||||
post_data = json.dumps(data)
|
||||
response = requests.post(self.webhook, headers=self.headers, data=post_data, verify=False)
|
||||
except requests.exceptions.HTTPError as exc:
|
||||
logging.error("消息发送失败, HTTP error: %d, reason: %s" % (exc.response.status_code, exc.response.reason))
|
||||
raise
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.error("消息发送失败,HTTP connection error!")
|
||||
raise
|
||||
except requests.exceptions.Timeout:
|
||||
logging.error("消息发送失败,Timeout error!")
|
||||
raise
|
||||
except requests.exceptions.RequestException:
|
||||
logging.error("消息发送失败, Request Exception!")
|
||||
raise
|
||||
else:
|
||||
try:
|
||||
result = response.json()
|
||||
except JSONDecodeError:
|
||||
logging.error("服务器响应异常,状态码:%s,响应内容:%s" % (response.status_code, response.text))
|
||||
return {'errcode': 500, 'errmsg': '服务器响应异常'}
|
||||
else:
|
||||
logging.debug('发送结果:%s' % result)
|
||||
# 消息发送失败提醒(errcode 不为 0,表示消息发送异常),默认不提醒,开发者可以根据返回的消息发送结果自行判断和处理
|
||||
if self.fail_notice and result.get('errcode', True):
|
||||
time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
|
||||
error_data = {
|
||||
"msgtype": "text",
|
||||
"text": {
|
||||
"content": "[注意-自动通知]飞书机器人消息发送失败,时间:%s,原因:%s,请及时跟进,谢谢!" % (
|
||||
time_now, result['errmsg'] if result.get('errmsg', False) else '未知异常')
|
||||
},
|
||||
"at": {
|
||||
"isAtAll": False
|
||||
}
|
||||
}
|
||||
logging.error("消息发送失败,自动通知:%s" % error_data)
|
||||
requests.post(self.webhook, headers=self.headers, data=json.dumps(error_data))
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
fs = FeiShuMessage(team='TO')
|
||||
吴勇刚 = "<at user_id='ou_94f57439f58bde9376189a3cabb0b11a'>吴勇刚</at>"
|
||||
刘明浩 = "<at user_id='ou_94f57439f58bde9376189a3cabb0b11b'>刘明浩</at>"
|
||||
陈慧宗 = "<at user_id='ou_bc2b266b05a428959bfcff3378af2d36'>陈慧宗</at>"
|
||||
message = "【TO-SMOKING_NEW】 第【1314】次构建结果:失败【5】个 \n|--{}: 1个\n|--{}: 2个\n|--{}: 3个" \
|
||||
"构建报告:...........test..........".format(吴勇刚, 刘明浩, 陈慧宗)
|
||||
resp = fs.send_text(msg=message)
|
||||
print(resp)
|
||||
101
base_framework/platform_tools/Message_service/check_jira.py
Normal file
101
base_framework/platform_tools/Message_service/check_jira.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# encoding: utf-8
|
||||
# @Time : 2021/12/20 18:14
|
||||
# @Author : yk
|
||||
# @Site :
|
||||
# @File : jira_message_by_ding.py
|
||||
|
||||
from jira import JIRA
|
||||
import json
|
||||
import requests
|
||||
import datetime
|
||||
import urllib.parse
|
||||
|
||||
|
||||
class JiraObj:
|
||||
def __init__(self, user, pwd, jira_addr):
|
||||
if not hasattr(JiraObj, 'jira'):
|
||||
JiraObj.jira_obj(user, pwd, jira_addr)
|
||||
|
||||
@staticmethod
|
||||
def jira_obj(user, pwd, jira_addr):
|
||||
JiraObj.jira = JIRA(auth=(user, pwd), options={'server': jira_addr})
|
||||
|
||||
@staticmethod
|
||||
def search_by_jql(jql, fields=None):
|
||||
return JiraObj.jira.search_issues(jql, fields=fields, maxResults=-1, json_result='true')
|
||||
|
||||
|
||||
|
||||
def get_delay_sub_task_creator(jira_obj, now_date):
|
||||
name_list="chengpu,fengtian,dengzhenbo,guosongchao,handongtang,huanghaifeng,lidaijun,wanglushun,wangyujie02,yangwenlei01,yangwenlei01,zhanghaodong,zhengxin01,zhuzipeng"
|
||||
# name_list="zhanghaodong"
|
||||
jql = 'issuetype = 缺陷 and createdDate < \'{} 18:00\' AND status not in (QA测试,SIM验证,Done,关闭,待测试) and assignee in ({})'.format(
|
||||
now_date,name_list)
|
||||
fields = 'assignee'
|
||||
print(jql)
|
||||
issue_list = jira_obj.search_by_jql(jql, fields).get('issues')
|
||||
print(issue_list)
|
||||
if issue_list:
|
||||
return list(map(lambda x: x.get('fields').get('assignee').get('name'), issue_list))
|
||||
else:
|
||||
return None
|
||||
def send_message_by_feishu(name, message):
|
||||
data={"list":[name]}
|
||||
a=requests.post(url="http://sqe.qc.huohua.cn:8082/feishu/getUserIds",data=data)
|
||||
print(a.text)
|
||||
open_id=a.text.split("open_id")[1].split('"')[2].split("\\")[0]
|
||||
|
||||
data={
|
||||
"app_id": "cli_a2d926427f39900d",
|
||||
"app_secret": "CvETCGh3rHu6CtcnxzaWK7rMnVgcSLED"
|
||||
}
|
||||
a=requests.post(url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",json=data)
|
||||
|
||||
tenant_access_token=eval(a.text)["tenant_access_token"]
|
||||
data ={"msg_type": "text","content": { "text": "{}".format(message) }, "open_ids": [open_id]}
|
||||
|
||||
header={"Authorization":"Bearer {}".format(tenant_access_token),"Content-Type":"application/json; charset=utf-8"}
|
||||
|
||||
a=requests.post(url="https://open.feishu.cn/open-apis/message/v4/batch_send/", json=data,headers=header)
|
||||
|
||||
print(a.text)
|
||||
if __name__ == '__main__':
|
||||
|
||||
now_date = datetime.datetime.now().strftime('%Y-%m-%d')
|
||||
|
||||
jira_obj = JiraObj('yaokun', 'Cyjayk1314', 'https://jira.bg.huohua.cn')
|
||||
creator = get_delay_sub_task_creator(jira_obj, now_date)
|
||||
|
||||
if creator:
|
||||
creator=list(set(creator))
|
||||
message="你还有未解决的bug,请及时解决,若已解决请及时更新jira状态"
|
||||
for i in creator:
|
||||
jql = 'issuetype = 缺陷 and createdDate < \'{} 18:00\' AND status not in (QA测试,SIM验证,Done,关闭,待测试) and assignee in ({})'.format(
|
||||
now_date, i)
|
||||
print(jql)
|
||||
i=i+"@sparkedu.com"
|
||||
|
||||
a=jira_obj.search_by_jql(jql, "assignee").get('issues')
|
||||
for j in a:
|
||||
print(j["key"])
|
||||
send_message_by_feishu(i,"你有未修复的bug请及时修复https://jira.bg.huohua.cn/browse/{}".format(j["key"]))
|
||||
# send_message_by_feishu("yaokun@sparkedu.com","https://jira.bg.huohua.cn/browse/HHC-50790")
|
||||
# data={"list":["yaokun@sparkedu.com"]}
|
||||
# a=requests.post(url="http://sqe.qc.huohua.cn:8082/feishu/getUserIds",data=data)
|
||||
#
|
||||
# print(a.text.split("open_id")[1].split('"')[2].split("\\")[0])
|
||||
# open_id="ou_95feb664191332a7916bb3b0d886f553"
|
||||
# print(a.text)
|
||||
# data={
|
||||
# "app_id": "cli_a2d926427f39900d",
|
||||
# "app_secret": "CvETCGh3rHu6CtcnxzaWK7rMnVgcSLED"
|
||||
# }
|
||||
# a=requests.post(url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",json=data)
|
||||
# print(a.text)
|
||||
#
|
||||
# data ={"msg_type": "text","content": { "text": "https://jira.bg.huohua.cn/browse/HHC-50790" }, "open_ids": [open_id]}
|
||||
#
|
||||
# header={"Authorization":"Bearer t-719d260f474e6a1a6b4a5d990a6bccec6fc3b17f","Content-Type":"application/json; charset=utf-8"}
|
||||
# print(data)
|
||||
# a = requests.post(url="https://open.feishu.cn/open-apis/message/v4/batch_send/", json=data,headers=header)
|
||||
# print(a.text)
|
||||
@@ -0,0 +1 @@
|
||||
# to be add 。。。。
|
||||
236
base_framework/platform_tools/Message_service/msg_config.ini
Normal file
236
base_framework/platform_tools/Message_service/msg_config.ini
Normal file
@@ -0,0 +1,236 @@
|
||||
# 此文档中配置钉钉,飞书群组和群用户信息
|
||||
[feishu_user_list]
|
||||
JENKINS_webhook_token = 6de2e886-10de-4a85-aa92-2d7446d0c315
|
||||
CDQA_webhook_token = 30e400ed-b802-4afb-b38c-2a0690c1a2a6
|
||||
TO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
TMO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
HHI_TO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
HHI_TMO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
GUE_webhook_token = a46a610c-4c52-4abf-bcad-5fde5cd545e4
|
||||
LALIVE_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4
|
||||
H2R_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4
|
||||
CC_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4
|
||||
ASTWB_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0
|
||||
ASOPE_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0
|
||||
ASTOP_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0
|
||||
ES_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20
|
||||
SCM_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20
|
||||
UBRD_webhook_token = a46a610c-4c52-4abf-bcad-5fde5cd545e4
|
||||
DBSYNC_webhook_token = 054f4c3c-b38b-46f8-9cc4-d9660e205fc7
|
||||
ODS_webhook_token = 054f4c3c-b38b-46f8-9cc4-d9660e205fc7
|
||||
AUTOMATION_webhook_token = ceb41bf3-f3ce-433f-bbea-2960bf46a819
|
||||
; TO-RD_webhook_token = d8d9ac4b-e5f9-47a6-8810-0ae6b621a7cb
|
||||
TO-RD_webhook_token = 2387b345-3675-49dd-971a-2f81b98e3ed1
|
||||
TO-FE_webhook_token = 1d0dac58-dd68-412d-a2eb-b445927339a
|
||||
GUE-RD_webhook_token = 8951949c-e300-4c96-a725-53346309851a
|
||||
USER-FE_webhook_token = 4f946e2b-0b58-4eb5-aa22-3d3516aee1ba
|
||||
SCM-RD_webhook_token = 74ceedc8-510f-4efd-bf64-4c5e6e87dc0f
|
||||
SCM-monitor_webhook_token = b821c1e5-63b0-42c9-b7ff-ab89c6dcd076
|
||||
PB_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20
|
||||
INFO_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde
|
||||
CODING_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde
|
||||
OFFLINE_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde
|
||||
|
||||
陈林 = ou_69254b2555a2c6257d5ca45d885f785e
|
||||
吴勇刚 = ou_94f57439f58bde9376189a3cabb0b11a
|
||||
陈慧宗 = ou_bc2b266b05a428959bfcff3378af2d36
|
||||
胥雯筠 = ou_04e4a3ccf680acc17f1039dd57349a00
|
||||
陈江 = ou_02ce6ebcb8a510c0b4102e15aaf59d05
|
||||
谯新久 = ou_c3c6f7ac7997000dcb23e634a830851d
|
||||
罗洪 = ou_f6981cb788d066accae770fda5fa7ee5
|
||||
刘睿权 = ou_218303e87f4c45ff81ed16a1e1d0b1ee
|
||||
陈典模 = ou_0fe84f418974af89e89148e2380427dd
|
||||
李聪 = ou_711ebaac08461ae9a2726fcbe393d7de
|
||||
左其灵 = ou_cc27134156714d88da23e159ba8390e1
|
||||
宋飞飞 = ou_ccafa6ebe0194e42ea55f8804734e6d4
|
||||
|
||||
付文龙 = ou_589b7151ac583ca3d94f0fb2d579f282
|
||||
赵晓放 = ou_a62ef7960b9c57e1946f55a6bb5d8b35
|
||||
张浩东 = ou_04ba397251c63036e9d75096f3c28704
|
||||
王玉杰 = ou_e3198cbf59327bac7dd686a94af30c2a
|
||||
冯天 = ou_3522b9c4015645ea2d4c36aa6c260e90
|
||||
郭松超 = ou_6a8e6ccaceb51167c4745b347085edd9
|
||||
张雄 = ou_1ab263e2e7386fd5c5ecd67c1c6f2b6d
|
||||
刘学刚 = ou_a17ade671a3030f12ae0fa13cc4f82bc
|
||||
朱亮 = ou_0cc7a4c1a97bb58dd188b6aac6c7569b
|
||||
高志军 = ou_5df6937cc109565f92868f0cdca4906c
|
||||
马锦程 = ou_d35f2668f960300ee2d9a7768c7de9ce
|
||||
刘英杰 = ou_d62d587ce3960add08214726897c228d
|
||||
张晓刚 = ou_320976aaeb1e50976932f2eb446918c7
|
||||
|
||||
|
||||
赵飞 = ou_17fdcc65d985a21a225034db318308bf
|
||||
刘信林 = ou_dec1cf51c8b3e63823e7262928b20dbb
|
||||
叶飞 = ou_3559e23c390a60a59f4b10e7053e5551
|
||||
向明 = ou_4c26b3066017f57f6f2658b49e16b8a5
|
||||
姜浩 = ou_8014499bb65adda6399ebc82e1a46724
|
||||
姜灵敏 = ou_e4e113c845e88a43125c8a994710ff94
|
||||
徐长乐 = ou_54c5d0f98d26ab7500a5af64d9022b1b
|
||||
卿晨 = ou_3e517c22dbcdfce4b4e75af20a7e5d8c
|
||||
李柏成 = ou_e40510cf340bc5ac0f860ac01844f1a1
|
||||
苟宇恒 = ou_d82652fb433a470d7ac72a542292cd3b
|
||||
董吉祥 = ou_4d9d1b8cf8c743f75313863a3ac336ad
|
||||
白杨 = ou_ac886a59ffd1a4a0da4cb3d06c1e7b03
|
||||
|
||||
李明泽 = ou_0fc5af64989a1677fa4c34b7542e4c2c
|
||||
沈佳坤 = ou_36ed516d812a1bacc3f978179e32910d
|
||||
朱乾元 = ou_31a10ecba1074d45df6c6cbb13ec16e9
|
||||
顾洋 = ou_543fe7b50e1cadf6974e93ac8663b09d
|
||||
吴优 = ou_117b8d43a8122021d290ef89cbcb9c88
|
||||
|
||||
|
||||
王亚超 = ou_649286a2b30bfe81bb4041a2b617fd80
|
||||
徐佳林 = ou_f382da52d42932870a0ea122753f795a
|
||||
彭霞 = ou_9ef2d9e4a5c098ab27933208dd95ff8a
|
||||
通用 = ou_abcdefghijklmnopqrstuvwxyz123456
|
||||
韩东堂 = ou_4ff52d71cb3cbe7cd1f26b2ac7da5eb0
|
||||
邓振博 = ou_994af7ca097ea1b63a5bec13b0b9a42c
|
||||
朱子朋 = ou_8d13121890e1de3851c0616b8392cb1f
|
||||
李代军 = ou_279bbc6d368cffa743c959d7461d8481
|
||||
黄海峰 = ou_8a4cf96dfc173dbed704d44fdc8fc5c1
|
||||
杨文磊 = ou_885d10cea3c3ae72c380971a7082da06
|
||||
卢纪霖 = ou_eaaf66ef5c53d3977a0bd3961376beae
|
||||
杨远宁 = ou_8693a6f29fe174b4d18155e6d4da0396
|
||||
崔瑞 = ou_7bbb7b678b07e74fd1c245bafc6877f9
|
||||
李振宇 = ou_82a619af1d592dfb994ee28389815ee9
|
||||
王同刚 = ou_ca8ea6ff6bf6258f49932dc4b0c98fbc
|
||||
张瑞涛 = ou_ca355f3fad90fe934087d225bb5794b4
|
||||
左钊 = ou_ea9a364cffa235aec88d37d075e300bb
|
||||
田翔 = ou_75796572af14c996f853f015ecb788e0
|
||||
赵加会 = ou_33ed201f4e7f0f8919bc7021130c066e
|
||||
刘欣畅 = ou_6247db29c46ac536d2aa10fab9711a82
|
||||
田文昌 = ou_60e01001facaadd78b89ef05281e0763
|
||||
郑宇翔 = ou_4e8d677a2389a8f8a5517961f2423970
|
||||
王国静 = ou_862b663bac7be9762eb78d71a9e6defb
|
||||
李其 = ou_cfb094d4d15f7da729eea8b1491ffd13
|
||||
尚万中 = ou_53e6edbf2ef8a1620cbadb4b7efeb966
|
||||
张景峰 = ou_48c88a2ff129e5d23215d245c6b82fc0
|
||||
|
||||
[user_name]
|
||||
fuwenlong = 付文龙
|
||||
fengtian = 冯天
|
||||
guosongchao = 郭松超
|
||||
wangyujie02 = 王玉杰
|
||||
zhanghaodong = 张浩东
|
||||
zhuliang = 朱亮
|
||||
liuxuegang = 刘学刚
|
||||
gaozhijun = 高志军
|
||||
majincheng = 马锦程
|
||||
zhaoxiaofang = 赵晓放
|
||||
liuyingjie = 刘英杰
|
||||
zhangxiaogang = 张晓刚
|
||||
|
||||
zhaofei = 赵飞
|
||||
liuxinlin = 刘信林
|
||||
yefei = 叶飞
|
||||
xiangming = 向明
|
||||
jianghao = 姜浩
|
||||
jianglingmin = 姜灵敏
|
||||
xuchangle = 徐长乐
|
||||
qingchen = 卿晨
|
||||
libaicheng = 李柏成
|
||||
gouyuheng = 苟宇恒
|
||||
jixiang.dong = 董吉祥
|
||||
baiyang01 = 白杨
|
||||
|
||||
wuyonggang = 吴勇刚
|
||||
xuwenjun = 胥雯筠
|
||||
songfeifei = 宋飞飞
|
||||
wanggang02 = 王刚
|
||||
lichao04 = 李超
|
||||
xiexiangyi = 谢祥益
|
||||
denghaiou = 邓海鸥
|
||||
yaokun = 姚坤
|
||||
chenhuizong = 陈慧宗
|
||||
yuanzhengqi = 袁正旗
|
||||
luohong = 罗洪
|
||||
chendianmo = 陈典模
|
||||
licong = 李聪
|
||||
zuoqiling = 左其灵
|
||||
zhengxin01 = 郑新
|
||||
handongtang = 韩东堂
|
||||
dengzhenbo = 邓振博
|
||||
zhuzipeng = 朱子朋
|
||||
lidaijun = 李代军
|
||||
huanghaifeng =黄海峰
|
||||
yangwenlei01 =杨文磊
|
||||
lujilin = 卢纪霖
|
||||
yangyuanning = 杨远宁
|
||||
cuirui = 崔瑞
|
||||
lizhenyu = 李振宇
|
||||
wangtonggang = 王同刚
|
||||
zhangruitao01 = 张瑞涛
|
||||
zuozhao = 左钊
|
||||
tianxiang = 田翔
|
||||
zhaojiahui = 赵加会
|
||||
liuxinchang = 刘欣畅
|
||||
tianwenchang = 田文昌
|
||||
zhengyuxiang = 郑宇翔
|
||||
zhangxiong = 张雄
|
||||
wangguojing = 王国静
|
||||
liqi03 = 李其
|
||||
shangwanzhong = 尚万中
|
||||
zhangjingfeng = 张景峰
|
||||
|
||||
limingze = 李明泽
|
||||
zhuqianyuan = 朱乾元
|
||||
guyang = 顾洋
|
||||
shenjiakun = 沈佳坤
|
||||
wuyou = 吴优
|
||||
|
||||
[dingding_user_list]
|
||||
SCM_owner = 13350956802
|
||||
景虎成 = 13350956802
|
||||
文妮 = 17302811505
|
||||
罗洪 = 13550629276
|
||||
TO_owner = 18215530124
|
||||
李超 = 18011454607
|
||||
吴勇刚 = 13540133074
|
||||
王刚 = 19141999584
|
||||
刘明浩 = 18380450039
|
||||
谢祥益 = 18010623985
|
||||
唐其麟 = 18011454607
|
||||
肖淇迈 = 19141999584
|
||||
唐浩 = 18380450039
|
||||
TMO_owner = 18215530124
|
||||
姚坤 = 18215530124
|
||||
罗志鹏 = 18582482272
|
||||
刘涛婷 = 18328504751
|
||||
陈江 = 13458500234
|
||||
ASTWB_owner = 18202810506
|
||||
谯新久 = 18202810506
|
||||
刘鹏 = 13547858402
|
||||
杨雨 = 13608006659
|
||||
华学敏 = 13708231975
|
||||
ASORG_owner = 18202810506
|
||||
PZ_owner = 18202810506
|
||||
肖亮 = 18108096240
|
||||
ASTOP_owner = 18780106567
|
||||
ASOPE_owner = 18180956201
|
||||
蒋安龙 = 18180956201
|
||||
杨中莲 = 13882105134
|
||||
CC_owner = 18215530124
|
||||
林于棚 = 18782019436
|
||||
黄业宏 = 18603267203
|
||||
EN_owner = 18180956201
|
||||
LALIVE_owner = 18215530124
|
||||
H2R_owner = 18380448416
|
||||
胥雯筠 = 18780106567
|
||||
周仁华 = 18281029023
|
||||
袁正旗 = 18030895120
|
||||
刘睿权 = 15681935823
|
||||
文青 = 18584851102
|
||||
刘德全 = 13402829590
|
||||
包利 = 18380448416
|
||||
ASTOP_to_dd = 495fc7e0171e829acda91ffa08eaf37307e123fafc090249b633eb651c31dd79
|
||||
ASTWB_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3
|
||||
ASOPE_to_dd = 147617cea7e1b480e54cecb6dfd33edb5a9ed872e1bab52007d488673ad8ab0f
|
||||
ASORG_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3
|
||||
CC_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
H2R_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
LALIVE_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
TMO_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
TO_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
UBRD_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
ULS_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
SCM_to_dd = 38fc76120cb204e2c1da53ec9921805670466da64c37aed8bc6fba0513f5688b
|
||||
851
base_framework/platform_tools/Testcase_service/case_service.py
Normal file
851
base_framework/platform_tools/Testcase_service/case_service.py
Normal file
@@ -0,0 +1,851 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
sys.path.append(Project_Path)
|
||||
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service import SetAttr, log
|
||||
|
||||
|
||||
def load_module_from_file(file_path, team):
|
||||
module_file = re.search(r"\\(%s\\.*)\.py" % team, file_path)
|
||||
# if not module_file:
|
||||
# module_file = re.search(r"\\(%s\\.*)\.py" % team.lower(), file_path)
|
||||
temp = module_file.group(1)
|
||||
temp = temp.replace(os.sep, ".")
|
||||
# try:
|
||||
# module = importlib.import_module(temp)
|
||||
# except Exception as err:
|
||||
# temp = re.sub(r"%s\." % team, "%s." % team.lower(), temp)
|
||||
# module = importlib.import_module(temp)
|
||||
return importlib.import_module(temp)
|
||||
|
||||
|
||||
class ITCaseFileOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.kw = kw_instance
|
||||
self.all_url = kw_instance.all_url
|
||||
self.kw_class_name = kw_instance.kw_class
|
||||
self.case_dir = os.path.abspath(os.path.join(Project_Path, team, "test_case", "TestCase", "1.接口"))
|
||||
self.env_path = os.path.abspath(
|
||||
os.path.join(Project_Path, team, "test_case", "Resource", "AdapterKws", "env.robot"))
|
||||
|
||||
def _check_case_file_exist(self, file_path):
|
||||
if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def check_case_exist(self, case_path, case_name):
|
||||
if self._check_case_file_exist(case_path):
|
||||
with open(case_path, "r", encoding="utf-8") as f:
|
||||
for line in f.readlines():
|
||||
if line.startswith(case_name):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_case_file(self, file_path):
|
||||
dirname = file_path[:-len(os.path.basename(file_path))]
|
||||
if not self._check_case_file_exist(file_path):
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname))
|
||||
else:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
content = f.readlines()
|
||||
if content:
|
||||
if "\n" != content[-1]:
|
||||
self.write_content_to_case_file(file_path, "\n")
|
||||
else:
|
||||
self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname))
|
||||
|
||||
def generate_env_releate_path(self, file_path):
|
||||
return "*** Settings ***\nResource " + os.path.relpath(self.env_path,
|
||||
file_path).replace("\\",
|
||||
"/") + "\n\n*** Test Cases ***\n"
|
||||
|
||||
def _check_case_file_resource(self, file_path):
|
||||
"""
|
||||
确定case文件中是否有关键字
|
||||
"""
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
content_list = f.readlines()
|
||||
if "*** Keywords ***\n" not in content_list:
|
||||
return True
|
||||
else:
|
||||
kw_index = content_list.rindex("*** Keywords ***\n")
|
||||
for index in range(kw_index, len(content_list) + 1):
|
||||
if "*** Test Cases ***\n" == content_list[index]:
|
||||
status = True
|
||||
if "*** Keywords ***\n" == content_list[index]:
|
||||
status = False
|
||||
return status
|
||||
|
||||
def write_content_to_case_file(self, file_path, content):
|
||||
status = self._check_case_file_resource(file_path)
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
if status:
|
||||
f.write(content)
|
||||
else:
|
||||
content = "*** Test Cases ***\n" + content
|
||||
f.write(content)
|
||||
|
||||
|
||||
class ITCaseContentOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.team = team
|
||||
self.file = ITCaseFileOperation(team, kw_instance)
|
||||
self.doc_content_row_temp = " ... | {} | {} | {} |"
|
||||
self.normal_1001 = None
|
||||
self.un_expect_list = None
|
||||
self.case_string = None
|
||||
|
||||
def _generate_case_name(self, interface_name, case_type, case_des):
|
||||
return "-".join([interface_name, case_type, case_des])
|
||||
|
||||
def _check_interface_confirm(self, interface_info):
|
||||
if not interface_info.confirmed:
|
||||
raise Exception("接口%s未确认,请先确认接口参数。" % interface_info.name)
|
||||
|
||||
def generate_case_body_all_1001_array(self, request_demo, parameters):
|
||||
temp_list = list()
|
||||
demo_copy = copy.deepcopy(request_demo)
|
||||
for key, value in request_demo.items():
|
||||
if not value:
|
||||
demo_copy.pop(key)
|
||||
if len(demo_copy.keys()) == 1:
|
||||
demo_copy = demo_copy.pop(list(demo_copy.keys())[0])
|
||||
dict_para, self.un_expect_list, _, _ = self._analysis_case_parameters_to_dict(parameters)
|
||||
demo_add_values = self._analysis_case_parameters_to_value(demo_copy, dict_para)
|
||||
data_list = self._analysis_dict_to_body_value(demo_copy, demo_add_values)
|
||||
if isinstance(demo_copy, list):
|
||||
for item in data_list:
|
||||
temp_list.append([item])
|
||||
data_list = temp_list
|
||||
return data_list
|
||||
|
||||
def _generate_parameter_to_dict(self, parameters):
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
def _analysis_case_parameters_to_value_empty_dict(self, request_demo, parameters, type=None):
|
||||
"""
|
||||
循环生成缺少某个参数的值,其它正确的参数组合列表
|
||||
"""
|
||||
parameter_expect_list = list()
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for key, parameter_attr in parameters.items():
|
||||
if parameter_attr.normal_values:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
parameter_un_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_un_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
for key in parameter_expect_dict.keys():
|
||||
if key not in json.dumps(request_demo):
|
||||
continue
|
||||
value = copy.copy(parameter_expect_dict)
|
||||
value[key] = ['NULL']
|
||||
if parameters.get(key).is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
if type == "1102":
|
||||
name = "缺少参数%s" % key
|
||||
else:
|
||||
name = "缺少参数%s的值" % key
|
||||
parameter_expect_list.append([value, status, name])
|
||||
return parameter_expect_list
|
||||
|
||||
def _distinguish_parameters_by_is_needed(self, parameters):
|
||||
"""
|
||||
将必填参数和非必须参数分开成2个列表
|
||||
"""
|
||||
need_parameters = list()
|
||||
no_need_parameters = list()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.is_need != 0:
|
||||
need_parameters.append(parameter_attr)
|
||||
else:
|
||||
no_need_parameters.append(parameter_attr)
|
||||
return need_parameters, no_need_parameters
|
||||
|
||||
def _replace_null_value_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s[\[\{]" % parameter_attr.name
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1101_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1101用例对应的所有请求参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_value_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数为空"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search("\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
log.info("1101:%s" % name)
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": []" % parameter_attr.name
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def _clean_all_none_json_char(self, really_body):
|
||||
"""
|
||||
清除1102场景删除缺少参数后不规则的字符串内容
|
||||
"""
|
||||
really_body = re.sub(",\s,", ",", really_body)
|
||||
really_body = re.sub("\[\s?,", "[", really_body)
|
||||
really_body = re.sub("\{\s?,", "{", really_body)
|
||||
really_body = re.sub(",\s\]", "]", really_body)
|
||||
really_body = re.sub(",\s\}", "}", really_body)
|
||||
really_body = re.sub("\s,", "", really_body)
|
||||
return really_body
|
||||
|
||||
def _replace_null_all_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1102_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1102用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_all_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数均缺失"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
log.info("1102:%s" % name)
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def generate_case_body_all_1201_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1201用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
for item in parameters:
|
||||
parameter_attr = SetAttr(item)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json):
|
||||
continue
|
||||
log.info("参数%s开始替换异常值" % parameter_attr.name)
|
||||
for un_expect_item in self.un_expect_list.get(parameter_attr.name, []):
|
||||
name = "参数%s使用异常值%s" % (parameter_attr.name, un_expect_item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = '"%s": ["%s"]' % (parameter_attr.name, un_expect_item)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = '"%s": "%s"' % (parameter_attr.name, un_expect_item)
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
status = "FAIL"
|
||||
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def create_case_content(self, result_path, interface_info, parameters, demo, case_type, tags=None):
|
||||
"""
|
||||
生成用例的所有内容
|
||||
"""
|
||||
self._check_interface_confirm(interface_info)
|
||||
body_content = list()
|
||||
doc = self.generate_case_doc(interface_info, tags=tags, demo=demo)
|
||||
case_file, _ = self.generate_case_file_path(interface_info)
|
||||
interface_name = interface_info.name
|
||||
if not parameters:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, None, status="PASS")
|
||||
|
||||
else:
|
||||
try:
|
||||
body_array = self.generate_case_body_all_1001_array(demo, parameters)
|
||||
self.normal_1001 = body_array[0]
|
||||
except TypeError:
|
||||
log.warning(traceback.print_exc())
|
||||
body_array = []
|
||||
self.normal_1001 = {}
|
||||
if case_type == "1001":
|
||||
try:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, body_array, status="PASS")
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
|
||||
elif case_type == "1101" or case_type == "1102" or case_type == "1201":
|
||||
body_temp_list = []
|
||||
# parameters_temp = self._generate_parameter_to_dict(parameters)
|
||||
try:
|
||||
body_array = eval(
|
||||
"self.generate_case_body_all_%s_array(self.normal_1001, parameters)" % case_type)
|
||||
if case_type == "1101":
|
||||
name = "缺少参数值"
|
||||
elif case_type == "1102":
|
||||
name = "缺少参数key和值"
|
||||
elif case_type == "1201":
|
||||
name = "参数使用异常值"
|
||||
else:
|
||||
name = ""
|
||||
body_tmp = self.generate_case_body_content(interface_info.in_url, doc, interface_name,
|
||||
case_type,
|
||||
name, body_array, status="PASS")
|
||||
body_temp_list.append(body_tmp)
|
||||
body = "".join(body_temp_list)
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
raise err
|
||||
body_content.append(body)
|
||||
self.file.write_content_to_case_file(case_file, "\n".join(body_content))
|
||||
msg = "接口url:%s,%s,生成case%s成功!路径为:%s" % (interface_info.type, interface_info.in_url, case_type, case_file)
|
||||
log.info(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
return msg
|
||||
|
||||
def generate_case_body_content(self, url, doc, interface_name, case_type, description, body_para, status="PASS"):
|
||||
body_content = list()
|
||||
case_name = self._generate_case_name(interface_name, case_type, description)
|
||||
body_content.append(case_name)
|
||||
body_content.append(doc)
|
||||
# 如何需要自动生成1001类型用例的请求body数据,请将304/305行放开,并把306~310注释
|
||||
request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
body_content.append(request)
|
||||
# if case_type != "1001":
|
||||
# request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
# body_content.append(request)
|
||||
# else:
|
||||
# body_content.append(" #请自行添加用例请求数据!\n")
|
||||
return "\n".join(body_content)
|
||||
|
||||
def _generate_case_body_content(self, url, body_content=None, status="PASS"):
|
||||
body_list = []
|
||||
if body_content is None:
|
||||
try:
|
||||
body_request = " ${resp} %s" % (self.file.all_url.get(url)[1])
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp}")
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_success = " should be equal as json ${resp} %s" % '请自行添加检查结果'
|
||||
else:
|
||||
body_validate_code = " Should Be Equal ${resp['code']} ${200}"
|
||||
body_validate_success = " Should Be True ${resp['success']}"
|
||||
body_list.append(body_validate_code)
|
||||
body_list.append(body_validate_success)
|
||||
else:
|
||||
if not body_content:
|
||||
body_create = " #request_demo无内容或者parameters在数据表中不全,请先检查再生成 \n ${body} Evaluate %s" % body_content
|
||||
body_list.append(body_create)
|
||||
count = 0
|
||||
for real_body in body_content:
|
||||
body_error = None
|
||||
result = {"message": "", "code": 200, "success": True}
|
||||
temp = copy.copy(real_body)
|
||||
if len(real_body) == 3 and isinstance(real_body, list):
|
||||
if real_body[1] == "PASS" or real_body[1] == "FAIL":
|
||||
real_body = temp[0]
|
||||
status = temp[1]
|
||||
case_description = temp[2]
|
||||
body_list.append(" #%s" % case_description)
|
||||
try:
|
||||
module = load_module_from_file(self.file.kw.kw_file_path, self.team)
|
||||
importlib.reload(module)
|
||||
if isinstance(real_body, list):
|
||||
temp_type = "*"
|
||||
else:
|
||||
temp_type = "**"
|
||||
result = eval(
|
||||
"%s.%s().%s(%sreal_body)" % (
|
||||
"module", self.file.kw_class_name, self.file.all_url.get(url)[1], temp_type))
|
||||
except:
|
||||
msg = "#url: %s 无法通过参数得到请求结果!" % url
|
||||
log.warning(real_body)
|
||||
log.warning(msg)
|
||||
log.warning(traceback.print_exc())
|
||||
if "${time}" in str(real_body):
|
||||
body_list.append(" ${time} get time epoch")
|
||||
body_create = " ${body_%s} Evaluate %s" % (count, real_body)
|
||||
try:
|
||||
if isinstance(real_body, list):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "@{body_%s}" % count)
|
||||
elif isinstance(real_body, dict):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "&{body_%s}" % count)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_create)
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp_%s}" % count)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_resp = " Should Be Equal as json ${resp_%s} %s" % (count, result)
|
||||
else:
|
||||
try:
|
||||
body_validate_code = " Should Be Equal as strings ${resp_%s['code']} ${%s}"
|
||||
# body_validate_success = " %s ${resp_%s['success']}"
|
||||
body_validate_success = " %s ${resp_%s['success']} %s"
|
||||
body_validate_resp = " %s ${resp_%s[\"message\"]} %s"
|
||||
if isinstance(result, str):
|
||||
body_validate_code = " #特殊接口返回!"
|
||||
body_validate_success = " #返回为bytes类型的字符码。仅检查结果是否相等!"
|
||||
body_validate_resp = " Should Be Equal as strings ${resp_%s} %s" % (count, result)
|
||||
|
||||
elif status == "PASS":
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
body_validate_success = body_validate_success % ("Should Be True", count, "")
|
||||
try:
|
||||
if result["message"]:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
else:
|
||||
try:
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
except Exception as e:
|
||||
if "status" in result:
|
||||
body_validate_code = body_validate_code % (count, result['status'])
|
||||
body_validate_code = body_validate_code.replace("code", "status")
|
||||
else:
|
||||
raise KeyError("接口不中无code返回,请与开发确认!!!")
|
||||
try:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["success"])
|
||||
except Exception as err:
|
||||
if "error" in result:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["error"])
|
||||
body_validate_success = body_validate_success.replace("success", "error")
|
||||
else:
|
||||
raise KeyError("接口不中无success返回,请与开发确认!!!")
|
||||
try:
|
||||
if result.get("message"):
|
||||
if re.search("[a-zA-Z0-9]{32}", result.get("message", "")):
|
||||
body_validate_resp = body_validate_resp % ("should contain", count,
|
||||
re.sub("[a-zA-Z0-9]{32}", "", result["message"]))
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"should be equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
except Exception as error:
|
||||
log.error(error)
|
||||
body_validate_code = None
|
||||
body_validate_success = None
|
||||
body_validate_resp = None
|
||||
body_error = " #无法正确生成异常用例返回值,请检查并手动生成!"
|
||||
if body_validate_code:
|
||||
body_list.append(body_validate_code)
|
||||
if body_validate_success:
|
||||
body_list.append(body_validate_success)
|
||||
if body_validate_resp:
|
||||
body_list.append(body_validate_resp)
|
||||
if body_error:
|
||||
body_list.append(body_error)
|
||||
# body_list.append(" #请自行添加data校验!")
|
||||
count += 1
|
||||
body_list.append("\n")
|
||||
return "\n".join(body_list)
|
||||
|
||||
def _generate_body_expect_type_value(self, type):
|
||||
if type.lower() == 'integer':
|
||||
value = random.randint(0, 100)
|
||||
elif type.lower() == "string":
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "date":
|
||||
value = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
elif type.lower() == "boolean":
|
||||
value = True
|
||||
elif type.lower() == "array":
|
||||
value = [1]
|
||||
else:
|
||||
value = "123"
|
||||
return value
|
||||
|
||||
def _generate_body_un_expect_type_value(self, type):
|
||||
value = ""
|
||||
if type.lower() == 'integer':
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "string":
|
||||
value = random.randint(0, 10000)
|
||||
else:
|
||||
value = False
|
||||
return value
|
||||
|
||||
def _analysis_dict_to_body_value(self, dict_none, dict_data):
|
||||
"""
|
||||
生成所有请求参数的组合列表
|
||||
"""
|
||||
data_list = []
|
||||
if isinstance(dict_none, list):
|
||||
if dict_none:
|
||||
for index in range(len(dict_none)):
|
||||
data = self._analysis_dict_to_body_value(dict_none[index], dict_data[index])
|
||||
data_list.extend(data)
|
||||
else:
|
||||
data_list = dict_data
|
||||
elif isinstance(dict_none, dict):
|
||||
try:
|
||||
len_num = self._get_nums(dict_data)
|
||||
except Exception as e:
|
||||
# log.error(e)
|
||||
log.error("数据表request_parameters中参数不全,请使用interface_hunter补全参数")
|
||||
raise e
|
||||
for i in range(0, len_num):
|
||||
copy_dict = copy.copy(dict_none)
|
||||
for key, value in copy_dict.items():
|
||||
data = self._analysis_dict_to_body_value(copy_dict[key], dict_data[key])
|
||||
if not data:
|
||||
copy_dict[key] = None
|
||||
elif i >= len(data):
|
||||
copy_dict[key] = data[0]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[0]]
|
||||
else:
|
||||
copy_dict[key] = data[i]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[i]]
|
||||
|
||||
data_list.append(copy_dict)
|
||||
|
||||
else:
|
||||
data_list = dict_data
|
||||
|
||||
return data_list
|
||||
|
||||
def _get_nums(self, dict_data):
|
||||
"""
|
||||
获取所有参数对应值的最大长度
|
||||
"""
|
||||
nums_list = [0]
|
||||
if isinstance(dict_data, list):
|
||||
for item in dict_data:
|
||||
if isinstance(item, dict):
|
||||
data = self._get_nums(dict_data[0])
|
||||
nums_list.append(data)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
|
||||
elif isinstance(dict_data, dict):
|
||||
for value in dict_data.values():
|
||||
data = self._get_nums(value)
|
||||
nums_list.append(data)
|
||||
elif dict_data == None:
|
||||
nums_list.append(0)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
return max(nums_list)
|
||||
|
||||
def _analysis_case_parameters_to_value(self, interface_demo, parameters):
|
||||
"""
|
||||
根据demo将每个参数的对应值的列表写回demo
|
||||
"""
|
||||
body = copy.copy(interface_demo)
|
||||
if isinstance(body, list):
|
||||
for index in range(len(body)):
|
||||
item = body[index]
|
||||
body[index] = self._analysis_case_parameters_to_value(item, parameters)
|
||||
|
||||
elif isinstance(body, dict):
|
||||
for key, value in body.items():
|
||||
if isinstance(value, dict) or isinstance(value, list):
|
||||
if value:
|
||||
body[key] = self._analysis_case_parameters_to_value(body.get(key), parameters)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
return body
|
||||
|
||||
def _analysis_case_parameters_to_dict(self, parameters):
|
||||
"""
|
||||
根据所有参数生成正常值 和异常值的列表
|
||||
"""
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.normal_values:
|
||||
if parameter_attr.normal_values.startswith("["):
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
if parameter_attr.type == "array":
|
||||
parameter_expect_dict[parameter_attr.name] = [[x] for x in parameter_attr.normal_values.split(",")]
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
continue
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
# raise Exception("接口参数%s无异常值,请先确认接口参数。" % parameter_attr.name)
|
||||
parameter_un_expect_dict[parameter_attr.name] = []
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
return parameter_expect_dict, parameter_un_expect_dict, expect_lenth, un_expect_lenth
|
||||
|
||||
def generate_case_doc(self, interface_info, tags=None, demo=None):
|
||||
if demo:
|
||||
lenth, demo_json, paras_d = self.file.kw._clean_demo_to_less_key(demo)
|
||||
doc_list = list()
|
||||
doc_list.append(
|
||||
" [Documentation] {} 接口路径:{}, {}".format(interface_info.interface_describe,
|
||||
interface_info.type, interface_info.in_url))
|
||||
# doc_list.append(self.doc_content_row_temp.format("输入参数名", "参数类型", "说明"))
|
||||
# for parameters_info in parameters:
|
||||
# sub_doc = self._generate_case_doc(parameters_info)
|
||||
# doc_list.append(sub_doc)
|
||||
if tags:
|
||||
tag_list = tags.split(",")
|
||||
tag = " [Tags]"
|
||||
for tag_ in tag_list:
|
||||
tag += " %s" % tag_
|
||||
doc_list.append(tag)
|
||||
if lenth > 1:
|
||||
doc_list.append(
|
||||
" # 请求参数说明 h:放header里的参数 u:放url路径节点中的参数 d:不放在url中在请求参数 p:放在url问号后在key-value结构参数")
|
||||
return "\n".join(doc_list)
|
||||
|
||||
def _generate_case_doc(self, request_parameter):
|
||||
doc_content = list()
|
||||
parameter_attr = SetAttr(request_parameter)
|
||||
row = self.doc_content_row_temp.format(parameter_attr.name, parameter_attr.type, parameter_attr.note)
|
||||
doc_content.append(row)
|
||||
return "\n".join(doc_content)
|
||||
|
||||
def generate_case_file_path(self, interface_info):
|
||||
path_cell_list = self._generate_case_file_path_info(interface_info.in_url)
|
||||
path_cell_list.insert(0, self.file.case_dir)
|
||||
return os.path.abspath(os.path.join(*path_cell_list[:-1]) + ".robot"), path_cell_list[-1]
|
||||
|
||||
def _generate_case_file_path_info(self, url):
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
path_first = re.search("//([\w+\-]+)\.", host).group(1)
|
||||
head_list.append(path_first)
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return head_list
|
||||
|
||||
|
||||
def run_interface_case_generate(result_path, interface, demo_info, parameters, case_instance, tags=None, normal=None):
|
||||
case_type_list = ["1001", "1101", "1102", "1201"]
|
||||
not_exist_case_type = list()
|
||||
exist_case_name = list()
|
||||
case_path, _ = case_instance.generate_case_file_path(interface)
|
||||
case_name_head = interface.name
|
||||
if parameters:
|
||||
for case_type in case_type_list:
|
||||
case_name = "-".join([case_name_head, case_type])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append(case_type)
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
else:
|
||||
case_name = "-".join([case_name_head, "1001"])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append("1001")
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
for item in exist_case_name:
|
||||
msg = "接口url:%s,%s,已存在用例%s,请确认!" % (interface.type, interface.in_url, item)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
if not_exist_case_type:
|
||||
if normal:
|
||||
not_exist_case_type = ["1001"]
|
||||
case_instance.file.generate_case_file(case_path)
|
||||
try:
|
||||
demo = json.loads(demo_info.request_demo)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
log.warning("the demo of interface(%s) is empty,please check." % interface.id)
|
||||
demo = dict()
|
||||
try:
|
||||
result = list()
|
||||
result.append("")
|
||||
result.append("-" * 60)
|
||||
result.append("用例生成成功!")
|
||||
for case_type in not_exist_case_type:
|
||||
body = case_instance.create_case_content(result_path, interface, parameters, demo, case_type, tags=tags)
|
||||
# result.append(body)
|
||||
result.append(body.split("!")[1])
|
||||
log.info("\n".join(result))
|
||||
except:
|
||||
log.error("could not generate case for interface id: %s, url: %s" % (interface.id, interface.in_url))
|
||||
log.error(traceback.print_exc())
|
||||
else:
|
||||
msg = "接口url:%s,%s,已存在用例,请确认!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
833
base_framework/platform_tools/Testcase_service/case_service1.py
Normal file
833
base_framework/platform_tools/Testcase_service/case_service1.py
Normal file
@@ -0,0 +1,833 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
sys.path.append(Project_Path)
|
||||
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service1 import SetAttr, log
|
||||
|
||||
|
||||
def load_module_from_file(file_path, team):
|
||||
module_file = re.search(r"(%s\\.*)\.py" % team, file_path)
|
||||
if not module_file:
|
||||
module_file = re.search(r"(%s.*)\.py" % team.lower(), file_path)
|
||||
temp = module_file.group(1)
|
||||
temp = temp.replace(os.sep, ".")
|
||||
# try:
|
||||
# module = importlib.import_module(temp)
|
||||
# except Exception as err:
|
||||
# temp = re.sub(r"%s\." % team, "%s." % team.lower(), temp)
|
||||
# module = importlib.import_module(temp)
|
||||
return importlib.import_module(temp)
|
||||
|
||||
|
||||
class ITCaseFileOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.kw = kw_instance
|
||||
self.all_url = kw_instance.all_url
|
||||
self.kw_class_name = kw_instance.kw_class
|
||||
self.case_dir = os.path.abspath(os.path.join(Project_Path, team, "test_case", "TestCase", "1.接口"))
|
||||
self.env_path = os.path.abspath(
|
||||
os.path.join(Project_Path, team, "test_case", "Resource", "AdapterKws", "env.robot"))
|
||||
|
||||
def _check_case_file_exist(self, file_path):
|
||||
if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def check_case_exist(self, case_path, case_name):
|
||||
if self._check_case_file_exist(case_path):
|
||||
with open(case_path, "r", encoding="utf-8") as f:
|
||||
for line in f.readlines():
|
||||
if line.startswith(case_name):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_case_file(self, file_path):
|
||||
if not self._check_case_file_exist(file_path):
|
||||
dirname = file_path[:-len(os.path.basename(file_path))]
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname))
|
||||
else:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
if f.readlines():
|
||||
if "\n" != f.readlines()[-1]:
|
||||
self.write_content_to_case_file(file_path, "\n")
|
||||
|
||||
def generate_env_releate_path(self, file_path):
|
||||
return "*** Settings ***\nResource " + os.path.relpath(self.env_path,
|
||||
file_path).replace("\\",
|
||||
"/") + "\n\n*** Test Cases ***\n"
|
||||
|
||||
def _check_case_file_resource(self, file_path):
|
||||
"""
|
||||
确定case文件中是否有关键字
|
||||
"""
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
content_list = f.readlines()
|
||||
if "*** Keywords ***\n" not in content_list:
|
||||
return True
|
||||
else:
|
||||
kw_index = content_list.rindex("*** Keywords ***\n")
|
||||
for index in range(kw_index, len(content_list) + 1):
|
||||
if "*** Test Cases ***\n" == content_list[index]:
|
||||
status = True
|
||||
if "*** Keywords ***\n" == content_list[index]:
|
||||
status = False
|
||||
return status
|
||||
|
||||
def write_content_to_case_file(self, file_path, content):
|
||||
status = self._check_case_file_resource(file_path)
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
if status:
|
||||
f.write(content)
|
||||
else:
|
||||
content = "*** Test Cases ***\n" + content
|
||||
f.write(content)
|
||||
|
||||
|
||||
class ITCaseContentOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.team = team
|
||||
self.file = ITCaseFileOperation(team, kw_instance)
|
||||
self.doc_content_row_temp = " ... | {} | {} | {} |"
|
||||
self.normal_1001 = None
|
||||
self.un_expect_list = None
|
||||
self.case_string = None
|
||||
|
||||
def _generate_case_name(self, interface_name, case_type, case_des):
|
||||
return "-".join([interface_name, case_type, case_des])
|
||||
|
||||
def _check_interface_confirm(self, interface_info):
|
||||
if not interface_info.confirmed:
|
||||
raise Exception("接口%s未确认,请先确认接口参数。" % interface_info.name)
|
||||
|
||||
def generate_case_body_all_1001_array(self, request_demo, parameters):
|
||||
temp_list = list()
|
||||
demo_copy = copy.deepcopy(request_demo)
|
||||
for key, value in request_demo.items():
|
||||
if not value:
|
||||
demo_copy.pop(key)
|
||||
if len(demo_copy.keys()) == 1:
|
||||
demo_copy = demo_copy.pop(list(demo_copy.keys())[0])
|
||||
dict_para, self.un_expect_list, _, _ = self._analysis_case_parameters_to_dict(parameters)
|
||||
demo_add_values = self._analysis_case_parameters_to_value(demo_copy, dict_para)
|
||||
data_list = self._analysis_dict_to_body_value(demo_copy, demo_add_values)
|
||||
if isinstance(demo_copy, list):
|
||||
for item in data_list:
|
||||
temp_list.append([item])
|
||||
data_list = temp_list
|
||||
return data_list
|
||||
|
||||
def _generate_parameter_to_dict(self, parameters):
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
def _analysis_case_parameters_to_value_empty_dict(self, request_demo, parameters, type=None):
|
||||
"""
|
||||
循环生成缺少某个参数的值,其它正确的参数组合列表
|
||||
"""
|
||||
parameter_expect_list = list()
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for key, parameter_attr in parameters.items():
|
||||
if parameter_attr.normal_values:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
parameter_un_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_un_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
for key in parameter_expect_dict.keys():
|
||||
if key not in json.dumps(request_demo):
|
||||
continue
|
||||
value = copy.copy(parameter_expect_dict)
|
||||
value[key] = ['NULL']
|
||||
if parameters.get(key).is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
if type == "1102":
|
||||
name = "缺少参数%s" % key
|
||||
else:
|
||||
name = "缺少参数%s的值" % key
|
||||
parameter_expect_list.append([value, status, name])
|
||||
return parameter_expect_list
|
||||
|
||||
def _distinguish_parameters_by_is_needed(self, parameters):
|
||||
"""
|
||||
将必填参数和非必须参数分开成2个列表
|
||||
"""
|
||||
need_parameters = list()
|
||||
no_need_parameters = list()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.is_need != 0:
|
||||
need_parameters.append(parameter_attr)
|
||||
else:
|
||||
no_need_parameters.append(parameter_attr)
|
||||
return need_parameters, no_need_parameters
|
||||
|
||||
def _replace_null_value_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s[\[\{]" % parameter_attr.name
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1101_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1101用例对应的所有请求参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_value_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数为空"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search("\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": []" % parameter_attr.name
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def _clean_all_none_json_char(self, really_body):
|
||||
"""
|
||||
清除1102场景删除缺少参数后不规则的字符串内容
|
||||
"""
|
||||
really_body = re.sub(",\s,", ",", really_body)
|
||||
really_body = re.sub("\[\s?,", "[", really_body)
|
||||
really_body = re.sub("\{\s?,", "{", really_body)
|
||||
really_body = re.sub(",\s\]", "]", really_body)
|
||||
really_body = re.sub(",\s\}", "}", really_body)
|
||||
really_body = re.sub("\s,", "", really_body)
|
||||
return really_body
|
||||
|
||||
def _replace_null_all_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1102_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1102用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_all_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数均缺失"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def generate_case_body_all_1201_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1201用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
for item in parameters:
|
||||
parameter_attr = SetAttr(item)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json):
|
||||
continue
|
||||
for un_expect_item in self.un_expect_list.get(parameter_attr.name):
|
||||
name = "参数%s使用异常值%s" % (parameter_attr.name, un_expect_item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = '"%s": ["%s"]' % (parameter_attr.name, un_expect_item)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = '"%s": "%s"' % (parameter_attr.name, un_expect_item)
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
status = "FAIL"
|
||||
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def create_case_content(self, result_path, interface_info, parameters, demo, case_type, tags=None):
|
||||
"""
|
||||
生成用例的所有内容
|
||||
"""
|
||||
self._check_interface_confirm(interface_info)
|
||||
body_content = list()
|
||||
doc = self.generate_case_doc(interface_info, tags=tags, demo=demo)
|
||||
case_file, _ = self.generate_case_file_path(interface_info)
|
||||
interface_name = interface_info.name
|
||||
if not parameters:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, None, status="PASS")
|
||||
|
||||
else:
|
||||
try:
|
||||
body_array = self.generate_case_body_all_1001_array(demo, parameters)
|
||||
self.normal_1001 = body_array[0]
|
||||
except TypeError:
|
||||
log.warning(traceback.print_exc())
|
||||
body_array = []
|
||||
self.normal_1001 = {}
|
||||
if case_type == "1001":
|
||||
try:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, body_array, status="PASS")
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
|
||||
elif case_type == "1101" or case_type == "1102" or case_type == "1201":
|
||||
body_temp_list = []
|
||||
# parameters_temp = self._generate_parameter_to_dict(parameters)
|
||||
try:
|
||||
body_array = eval(
|
||||
"self.generate_case_body_all_%s_array(self.normal_1001, parameters)" % case_type)
|
||||
if case_type == "1101":
|
||||
name = "缺少参数值"
|
||||
elif case_type == "1102":
|
||||
name = "缺少参数key和值"
|
||||
elif case_type == "1201":
|
||||
name = "参数使用异常值"
|
||||
else:
|
||||
name = ""
|
||||
body_tmp = self.generate_case_body_content(interface_info.in_url, doc, interface_name,
|
||||
case_type,
|
||||
name, body_array, status="PASS")
|
||||
body_temp_list.append(body_tmp)
|
||||
body = "".join(body_temp_list)
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
raise err
|
||||
body_content.append(body)
|
||||
self.file.write_content_to_case_file(case_file, "\n".join(body_content))
|
||||
msg = "接口url:%s,%s,生成case%s成功!路径为:%s" % (interface_info.type, interface_info.in_url, case_type, case_file)
|
||||
log.info(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
return msg
|
||||
|
||||
def generate_case_body_content(self, url, doc, interface_name, case_type, description, body_para, status="PASS"):
|
||||
body_content = list()
|
||||
case_name = self._generate_case_name(interface_name, case_type, description)
|
||||
body_content.append(case_name)
|
||||
body_content.append(doc)
|
||||
# 如何需要自动生成1001类型用例的请求body数据,请将304/305行放开,并把306~310注释
|
||||
request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
body_content.append(request)
|
||||
# if case_type != "1001":
|
||||
# request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
# body_content.append(request)
|
||||
# else:
|
||||
# body_content.append(" #请自行添加用例请求数据!\n")
|
||||
return "\n".join(body_content)
|
||||
|
||||
def _generate_case_body_content(self, url, body_content=None, status="PASS"):
|
||||
body_list = []
|
||||
if body_content is None:
|
||||
try:
|
||||
body_request = " ${resp} %s" % (self.file.all_url.get(url)[1])
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp}")
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_success = " check_rf ${resp} %s" % '请自行添加检查结果'
|
||||
else:
|
||||
body_validate_code = " Should Be Equal ${resp['code']} ${200}"
|
||||
body_validate_success = " Should Be True ${resp['success']}"
|
||||
body_list.append(body_validate_code)
|
||||
body_list.append(body_validate_success)
|
||||
else:
|
||||
if not body_content:
|
||||
body_create = " #request_demo无内容或者parameters在数据表中不全,请先检查再生成 \n ${body} Evaluate %s" % body_content
|
||||
body_list.append(body_create)
|
||||
count = 0
|
||||
for real_body in body_content:
|
||||
result = {"message": "", "code": 200, "success": True}
|
||||
temp = copy.copy(real_body)
|
||||
if len(real_body) == 3 and isinstance(real_body, list):
|
||||
if real_body[1] == "PASS" or real_body[1] == "FAIL":
|
||||
real_body = temp[0]
|
||||
status = temp[1]
|
||||
case_description = temp[2]
|
||||
body_list.append(" #%s" % case_description)
|
||||
try:
|
||||
module = load_module_from_file(self.file.kw.kw_file_path, self.team)
|
||||
importlib.reload(module)
|
||||
if isinstance(real_body, list):
|
||||
temp_type = "*"
|
||||
else:
|
||||
temp_type = "**"
|
||||
result = eval(
|
||||
"%s.%s().%s(%sreal_body)" % (
|
||||
"module", self.file.kw_class_name, self.file.all_url.get(url)[1], temp_type))
|
||||
except:
|
||||
msg = "#url: %s 无法通过参数得到请求结果!" % url
|
||||
log.warning(real_body)
|
||||
log.warning(msg)
|
||||
log.warning(traceback.print_exc())
|
||||
if "${time}" in str(real_body):
|
||||
body_list.append(" ${time} get time epoch")
|
||||
body_create = " ${body_%s} Evaluate %s" % (count, real_body)
|
||||
try:
|
||||
if isinstance(real_body, list):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "@{body_%s}" % count)
|
||||
elif isinstance(real_body, dict):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "&{body_%s}" % count)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_create)
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp_%s}" % count)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_resp = " check_rf ${resp_%s} %s" % (count, result)
|
||||
else:
|
||||
body_validate_code = " Should Be Equal as strings ${resp_%s['code']} ${%s}"
|
||||
# body_validate_success = " %s ${resp_%s['success']}"
|
||||
body_validate_success = " %s ${resp_%s['success']} %s"
|
||||
body_validate_resp = " %s ${resp_%s[\"message\"]} %s"
|
||||
if isinstance(result, str):
|
||||
body_validate_code = " #特殊接口返回!"
|
||||
body_validate_success = " #返回为bytes类型的字符码。仅检查结果是否相等!"
|
||||
body_validate_resp = " Should Be Equal as strings ${resp_%s} %s" % (count, result)
|
||||
|
||||
elif status == "PASS":
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
body_validate_success = body_validate_success % ("Should Be True", count, "")
|
||||
try:
|
||||
if result["message"]:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
else:
|
||||
try:
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
except Exception as e:
|
||||
if "status" in result:
|
||||
body_validate_code = body_validate_code % (count, result['status'])
|
||||
body_validate_code = body_validate_code.replace("code", "status")
|
||||
else:
|
||||
raise KeyError("接口不中无code返回,请与开发确认!!!")
|
||||
try:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["success"])
|
||||
except Exception as err:
|
||||
if "error" in result:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["error"])
|
||||
body_validate_success = body_validate_success.replace("success", "error")
|
||||
else:
|
||||
raise KeyError("接口不中无success返回,请与开发确认!!!")
|
||||
try:
|
||||
if result.get("message"):
|
||||
if re.search("[a-zA-Z0-9]{32}", result.get("message", "")):
|
||||
body_validate_resp = body_validate_resp % ("should contain", count,
|
||||
re.sub("[a-zA-Z0-9]{32}", "", result["message"]))
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"should be equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
|
||||
body_list.append(body_validate_code)
|
||||
body_list.append(body_validate_success)
|
||||
body_list.append(body_validate_resp)
|
||||
# body_list.append(" #请自行添加data校验!")
|
||||
count += 1
|
||||
body_list.append("\n")
|
||||
return "\n".join(body_list)
|
||||
|
||||
def _generate_body_expect_type_value(self, type):
|
||||
if type.lower() == 'integer':
|
||||
value = random.randint(0, 100)
|
||||
elif type.lower() == "string":
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "date":
|
||||
value = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
elif type.lower() == "boolean":
|
||||
value = True
|
||||
elif type.lower() == "array":
|
||||
value = [1]
|
||||
else:
|
||||
value = "123"
|
||||
return value
|
||||
|
||||
def _generate_body_un_expect_type_value(self, type):
|
||||
value = ""
|
||||
if type.lower() == 'integer':
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "string":
|
||||
value = random.randint(0, 10000)
|
||||
else:
|
||||
value = False
|
||||
return value
|
||||
|
||||
def _analysis_dict_to_body_value(self, dict_none, dict_data):
|
||||
"""
|
||||
生成所有请求参数的组合列表
|
||||
"""
|
||||
data_list = []
|
||||
if isinstance(dict_none, list):
|
||||
if dict_none:
|
||||
for index in range(len(dict_none)):
|
||||
data = self._analysis_dict_to_body_value(dict_none[index], dict_data[index])
|
||||
data_list.extend(data)
|
||||
else:
|
||||
data_list = dict_data
|
||||
elif isinstance(dict_none, dict):
|
||||
try:
|
||||
len_num = self._get_nums(dict_data)
|
||||
except Exception as e:
|
||||
# log.error(e)
|
||||
log.error("数据表request_parameters中参数不全,请使用interface_hunter补全参数")
|
||||
raise e
|
||||
for i in range(0, len_num):
|
||||
copy_dict = copy.copy(dict_none)
|
||||
for key, value in copy_dict.items():
|
||||
data = self._analysis_dict_to_body_value(copy_dict[key], dict_data[key])
|
||||
if not data:
|
||||
copy_dict[key] = None
|
||||
elif i >= len(data):
|
||||
copy_dict[key] = data[0]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[0]]
|
||||
else:
|
||||
copy_dict[key] = data[i]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[i]]
|
||||
|
||||
data_list.append(copy_dict)
|
||||
|
||||
else:
|
||||
data_list = dict_data
|
||||
|
||||
return data_list
|
||||
|
||||
def _get_nums(self, dict_data):
|
||||
"""
|
||||
获取所有参数对应值的最大长度
|
||||
"""
|
||||
nums_list = [0]
|
||||
if isinstance(dict_data, list):
|
||||
for item in dict_data:
|
||||
if isinstance(item, dict):
|
||||
data = self._get_nums(dict_data[0])
|
||||
nums_list.append(data)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
|
||||
elif isinstance(dict_data, dict):
|
||||
for value in dict_data.values():
|
||||
data = self._get_nums(value)
|
||||
nums_list.append(data)
|
||||
elif dict_data == None:
|
||||
nums_list.append(0)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
return max(nums_list)
|
||||
|
||||
def _analysis_case_parameters_to_value(self, interface_demo, parameters):
|
||||
"""
|
||||
根据demo将每个参数的对应值的列表写回demo
|
||||
"""
|
||||
body = copy.copy(interface_demo)
|
||||
if isinstance(body, list):
|
||||
for index in range(len(body)):
|
||||
item = body[index]
|
||||
body[index] = self._analysis_case_parameters_to_value(item, parameters)
|
||||
|
||||
elif isinstance(body, dict):
|
||||
for key, value in body.items():
|
||||
if isinstance(value, dict) or isinstance(value, list):
|
||||
if value:
|
||||
body[key] = self._analysis_case_parameters_to_value(body.get(key), parameters)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
return body
|
||||
|
||||
def _analysis_case_parameters_to_dict(self, parameters):
|
||||
"""
|
||||
根据所有参数生成正常值 和异常值的列表
|
||||
"""
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.normal_values:
|
||||
if parameter_attr.normal_values.startswith("["):
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
if parameter_attr.type == "array":
|
||||
parameter_expect_dict[parameter_attr.name] = [[x] for x in parameter_attr.normal_values.split(",")]
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
continue
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
# raise Exception("接口参数%s无异常值,请先确认接口参数。" % parameter_attr.name)
|
||||
parameter_un_expect_dict[parameter_attr.name] = []
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
return parameter_expect_dict, parameter_un_expect_dict, expect_lenth, un_expect_lenth
|
||||
|
||||
def generate_case_doc(self, interface_info, tags=None, demo=None):
|
||||
if demo:
|
||||
lenth, demo_json, paras_d = self.file.kw._clean_demo_to_less_key(demo)
|
||||
doc_list = list()
|
||||
doc_list.append(
|
||||
" [Documentation] {} 接口路径:{}, {}".format(interface_info.interface_describe,
|
||||
interface_info.type, interface_info.in_url))
|
||||
# doc_list.append(self.doc_content_row_temp.format("输入参数名", "参数类型", "说明"))
|
||||
# for parameters_info in parameters:
|
||||
# sub_doc = self._generate_case_doc(parameters_info)
|
||||
# doc_list.append(sub_doc)
|
||||
if tags:
|
||||
tag_list = tags.split(",")
|
||||
tag = " [Tags]"
|
||||
for tag_ in tag_list:
|
||||
tag += " %s" % tag_
|
||||
doc_list.append(tag)
|
||||
if lenth > 1:
|
||||
doc_list.append(
|
||||
" # 请求参数说明 h:放header里的参数 u:放url路径节点中的参数 d:不放在url中在请求参数 p:放在url问号后在key-value结构参数")
|
||||
return "\n".join(doc_list)
|
||||
|
||||
def _generate_case_doc(self, request_parameter):
|
||||
doc_content = list()
|
||||
parameter_attr = SetAttr(request_parameter)
|
||||
row = self.doc_content_row_temp.format(parameter_attr.name, parameter_attr.type, parameter_attr.note)
|
||||
doc_content.append(row)
|
||||
return "\n".join(doc_content)
|
||||
|
||||
def generate_case_file_path(self, interface_info):
|
||||
path_cell_list = self._generate_case_file_path_info(interface_info.in_url)
|
||||
path_cell_list.insert(0, self.file.case_dir)
|
||||
return os.path.abspath(os.path.join(*path_cell_list[:-1]) + ".robot"), path_cell_list[-1]
|
||||
|
||||
def _generate_case_file_path_info(self, url):
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
path_first = re.search("//([\w+\-]+)\.", host).group(1)
|
||||
head_list.append(path_first)
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return head_list
|
||||
|
||||
|
||||
def run_interface_case_generate(result_path, interface, demo_info, parameters, case_instance, tags=None, normal=None):
|
||||
case_type_list = ["1001", "1101", "1102", "1201"]
|
||||
not_exist_case_type = list()
|
||||
exist_case_name = list()
|
||||
case_path, _ = case_instance.generate_case_file_path(interface)
|
||||
case_name_head = interface.name
|
||||
if parameters:
|
||||
for case_type in case_type_list:
|
||||
case_name = "-".join([case_name_head, case_type])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append(case_type)
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
else:
|
||||
case_name = "-".join([case_name_head, "1001"])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append("1001")
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
for item in exist_case_name:
|
||||
msg = "接口url:%s,%s,已存在用例%s,请确认!" % (interface.type, interface.in_url, item)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
if not_exist_case_type:
|
||||
if normal:
|
||||
not_exist_case_type = ["1001"]
|
||||
case_instance.file.generate_case_file(case_path)
|
||||
try:
|
||||
demo = json.loads(demo_info.request_demo)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
log.warning("the demo of interface(%s) is empty,please check." % interface.id)
|
||||
demo = dict()
|
||||
try:
|
||||
result = list()
|
||||
result.append("")
|
||||
result.append("-" * 60)
|
||||
result.append("用例生成成功!")
|
||||
for case_type in not_exist_case_type:
|
||||
body = case_instance.create_case_content(result_path, interface, parameters, demo, case_type, tags=tags)
|
||||
# result.append(body)
|
||||
result.append(body.split("!")[1])
|
||||
log.info("\n".join(result))
|
||||
except:
|
||||
log.error("could not generate case for interface id: %s, url: %s" % (interface.id, interface.in_url))
|
||||
log.error(traceback.print_exc())
|
||||
else:
|
||||
msg = "接口url:%s,%s,已存在用例,请确认!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
3
base_framework/platform_tools/Testcase_service/readme
Normal file
3
base_framework/platform_tools/Testcase_service/readme
Normal file
@@ -0,0 +1,3 @@
|
||||
目录结构说明:
|
||||
使用者:王刚
|
||||
用途:存放自动生成RF层用例的脚本
|
||||
7
base_framework/platform_tools/__init__.py
Normal file
7
base_framework/platform_tools/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: linyupeng
|
||||
Email: linyupeng@huohua.cn
|
||||
Create Data: 2021/4/9 19:08
|
||||
"""
|
||||
266
base_framework/platform_tools/case_runner.py
Normal file
266
base_framework/platform_tools/case_runner.py
Normal file
@@ -0,0 +1,266 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import argparse
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service import SetAttr, SwaggerDBInfo, \
|
||||
KWFileOperation, log, KWOperation, run_keyword_generage, Project_Path
|
||||
# from base_framework.platform_tools.Swagger_scanner.scanner import SwaggerOnlineDebug
|
||||
from base_framework.platform_tools.Testcase_service.case_service import ITCaseContentOperation, \
|
||||
run_interface_case_generate, load_module_from_file
|
||||
from base_framework.base_config.current_pth import *
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
from base_framework.platform_tools.Swagger_scanner.scanner_add import GenerateBody
|
||||
|
||||
dir_name = os.path.dirname(__file__)
|
||||
interface_path = os.path.join(dir_name, "Swagger_scanner/interface.txt")
|
||||
token_header = ["accesstoken", "user-token"]
|
||||
|
||||
|
||||
def get_all_interface_info_from_file(path):
|
||||
with open(path, encoding="UTF-8") as f:
|
||||
content = f.readlines()
|
||||
return content[1:]
|
||||
|
||||
|
||||
def write_interface_info_to_file(content, path=interface_path):
|
||||
with open(path, mode="r", encoding="UTF-8") as f:
|
||||
line = f.readline().strip("\n")
|
||||
with open(path, mode="w", encoding="UTF-8") as f1:
|
||||
f1.write(line + "\n" + content)
|
||||
|
||||
|
||||
def generate_result_file():
|
||||
file_name = "reslut_%s.txt" % time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())
|
||||
result_dir = os.path.join(dir_name, "case_result")
|
||||
result_file = os.path.abspath(os.path.join(result_dir, file_name))
|
||||
if not os.path.exists(result_dir):
|
||||
os.makedirs(result_dir)
|
||||
with open(result_file, "w+", encoding="UTF-8") as f:
|
||||
f.write("开始自动生成keyword和case! 开始时间:%s\n" % time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime()))
|
||||
return result_file
|
||||
|
||||
|
||||
def write_case_result_to_case_file(file_path, content):
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
lines = f.readlines()
|
||||
text = content + "\n"
|
||||
if lines:
|
||||
if lines[-1] != "\n":
|
||||
text = "\n" + "\n" + content + "\n"
|
||||
|
||||
f.write(text)
|
||||
|
||||
|
||||
def clean_the_interface_info(result_file, interface_info):
|
||||
interface = tuple(interface_info.strip("\n").split(","))
|
||||
interface_infos = SwaggerDBInfo.get_interface_info_by_url_and_type(interface[1], interface[0], interface[2])
|
||||
if len(interface_infos) > 1:
|
||||
msg = "接口url:%s,%s,存在多条数据,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif len(interface_infos) == 0:
|
||||
msg = "接口url:%s,%s,在数据库中不存在,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
interface_info_attr = SetAttr(interface_infos[0])
|
||||
if interface_info_attr.is_used == 0:
|
||||
msg = "接口url:%s,%s,%s,已不在使用,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif interface_info_attr.at_numbers > 0:
|
||||
msg = "接口url:%s,%s,%s,已存在用例,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
msg = "接口url:%s,%s,%s,开始生成接口关键字和用例!" % interface
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
return interface_info_attr
|
||||
return msg
|
||||
|
||||
|
||||
def generate_swagger_url_and_option(swagger_info):
|
||||
swagger_info_attr = SetAttr(swagger_info)
|
||||
url = swagger_info_attr.sw_url
|
||||
temp_list = url.split("v2")
|
||||
swagger_url = temp_list[0] + 'swagger-ui.html'
|
||||
option = '/v2' + temp_list[1]
|
||||
return swagger_url, option, temp_list[0]
|
||||
|
||||
|
||||
def get_demo_by_interface_info(interface_id):
|
||||
body_demo = GenerateBody(interface_id)
|
||||
request_data = body_demo.combine_request_parameters()
|
||||
if request_data["d"]:
|
||||
request_demo = body_demo.combine_request_data(request_data["d"][0], request_data["d"][1], key=0)
|
||||
request_data["d"] = request_demo
|
||||
return request_data
|
||||
|
||||
|
||||
def clear_request_parameters(parameters):
|
||||
"""
|
||||
清除参数中作为token的参数
|
||||
"""
|
||||
parameters_result = copy.deepcopy(parameters)
|
||||
for item in parameters:
|
||||
if item.get("name").lower() in token_header:
|
||||
parameters_result.remove(item)
|
||||
return parameters_result
|
||||
|
||||
|
||||
def clear_header_token_demo(demo_info):
|
||||
"""
|
||||
清除请求头中带有token这种无用参数
|
||||
"""
|
||||
demo_copy = json.loads(copy.deepcopy(demo_info.request_demo))
|
||||
header = copy.deepcopy(demo_copy.get('h'))
|
||||
for key, _ in demo_copy.get('h').items():
|
||||
if key in token_header:
|
||||
header.pop(key)
|
||||
if not header:
|
||||
demo_copy['h'] = {}
|
||||
else:
|
||||
demo_copy['h'] = header
|
||||
demo_info.request_demo = json.dumps(demo_copy)
|
||||
return demo_info
|
||||
|
||||
|
||||
def generate_parameter_to_dict(parameters):
|
||||
"""
|
||||
将数据库查询到的参数以参数名组成字典
|
||||
"""
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
|
||||
def clean_un_use_parameter(demo, parameters):
|
||||
"""
|
||||
删除不再使用的参数
|
||||
"""
|
||||
if isinstance(demo, SetAttr):
|
||||
demo_item = json.loads(demo.request_demo)
|
||||
demo_copy = copy.deepcopy(demo_item)
|
||||
else:
|
||||
demo_item = copy.deepcopy(demo)
|
||||
demo_copy = copy.deepcopy(demo)
|
||||
parameters_dict = generate_parameter_to_dict(parameters)
|
||||
if isinstance(demo, list):
|
||||
demo_copy.clear()
|
||||
for index in range(len(demo_item)):
|
||||
demo_copy.append(clean_un_use_parameter(demo_item[index], parameters))
|
||||
elif isinstance(demo_item, dict):
|
||||
for key, values in demo_item.items():
|
||||
if parameters_dict.get(key, None) and int(parameters_dict.get(key).is_need) == 2:
|
||||
demo_copy.pop(key)
|
||||
continue
|
||||
demo_copy[key] = clean_un_use_parameter(values, parameters)
|
||||
else:
|
||||
demo_copy = demo_copy
|
||||
return demo_copy
|
||||
|
||||
|
||||
def confirm_demo_value_string(kw, case, demo_string, parameters):
|
||||
"""
|
||||
确认demo中的请求参数是不是纯值,并非json说格式
|
||||
"""
|
||||
demo = copy.deepcopy(demo_string.request_demo)
|
||||
demo_value = json.loads(demo)
|
||||
if (not demo_value["d"]) and (demo_value["d"] != 0):
|
||||
return demo_value
|
||||
if (isinstance(demo_value["d"], str) or isinstance(demo_value["d"], int)):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
elif isinstance(demo_value["d"], list):
|
||||
if not isinstance(demo_value["d"][0], dict):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
elif 'empty' in demo_value['d']:
|
||||
kw.kw_array = demo_value['d'].pop('empty')
|
||||
return demo_value
|
||||
|
||||
|
||||
def confirm_correct_team_case(team):
|
||||
interface_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
try:
|
||||
module = load_module_from_file(interface_file_path, team.upper())
|
||||
real_team = team.upper()
|
||||
except Exception as err:
|
||||
real_team = team.lower()
|
||||
return real_team
|
||||
|
||||
|
||||
def run_interface_case(team, tags=None, platform=None, server=None, driver=None, normal=None):
|
||||
team = confirm_correct_team_case(team)
|
||||
kw_instance = KWFileOperation(team)
|
||||
result_file = generate_result_file()
|
||||
interface_contents = get_all_interface_info_from_file(interface_path)
|
||||
keyword = KWOperation(team, kw_instance, server)
|
||||
case = ITCaseContentOperation(team, kw_instance)
|
||||
for interface_temp in interface_contents:
|
||||
if interface_temp == "\n" or interface_temp.startswith("#"):
|
||||
continue
|
||||
interface = clean_the_interface_info(result_file, interface_temp)
|
||||
if not isinstance(interface, str):
|
||||
parameters = keyword.get_interface_parameters(interface.id)
|
||||
parameters = clear_request_parameters(parameters)
|
||||
if parameters:
|
||||
demo = get_demo_by_interface_info(interface.id)
|
||||
else:
|
||||
demo = {"d": {}, "h": {}, "p": {}, "u": {}}
|
||||
demo = [{"request_demo": json.dumps(demo)}]
|
||||
demo_info = SetAttr(demo[0])
|
||||
demo_info = clear_header_token_demo(demo_info)
|
||||
demo_info.request_demo = json.dumps(clean_un_use_parameter(demo_info, parameters))
|
||||
demo_info.request_demo = json.dumps(confirm_demo_value_string(keyword, case, demo_info, parameters))
|
||||
kw_status = run_keyword_generage(result_file, interface, demo_info, parameters, keyword)
|
||||
if kw_status:
|
||||
msg = "接口url:%s,%s,生成keyword成功!接下来继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
run_interface_case_generate(result_file, interface, demo_info, parameters, case, tags, normal)
|
||||
return True
|
||||
else:
|
||||
return interface
|
||||
|
||||
|
||||
def print_usage():
|
||||
parser_inst.print_usage()
|
||||
parser_inst.exit(1, "生成用例失败,请根据使用帮助传入正确参数。")
|
||||
|
||||
|
||||
def write_platform_to_running_env(platform, team):
|
||||
if not platform:
|
||||
platform = ""
|
||||
cfg_opt = ReadConfig(env_choose_path)
|
||||
cfg_opt.set_section("run_jira_id", "huohua-podenv", platform)
|
||||
cfg_opt.set_section("run_evn_name", "current_team", team)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="请输入组名!")
|
||||
parser.add_argument("-team", dest='team', help="TO or TMO")
|
||||
parser.add_argument("-tag", dest='tags', help="标签", default=None)
|
||||
parser.add_argument("-p", dest='platform', help="独立环境", default=None)
|
||||
parser.add_argument("-s", dest='server', help="服务名", default=None)
|
||||
parser.add_argument("-d", dest='driver', help="驱动路径", default=None)
|
||||
parser.add_argument("-n", dest='normal', help="是否只生成正常用例", default=None)
|
||||
args = parser.parse_args()
|
||||
global parser_inst
|
||||
parser_inst = parser
|
||||
if not args.team:
|
||||
print_usage()
|
||||
else:
|
||||
write_platform_to_running_env(args.platform, args.team)
|
||||
run_interface_case(args.team, args.tags, args.platform, args.server, args.driver, args.normal)
|
||||
264
base_framework/platform_tools/case_runner1.py
Normal file
264
base_framework/platform_tools/case_runner1.py
Normal file
@@ -0,0 +1,264 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import argparse
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service1 import SetAttr, SwaggerDBInfo, \
|
||||
KWFileOperation, log, KWOperation, run_keyword_generage, Project_Path
|
||||
# from base_framework.platform_tools.Swagger_scanner.scanner import SwaggerOnlineDebug
|
||||
from base_framework.platform_tools.Testcase_service.case_service1 import ITCaseContentOperation, \
|
||||
run_interface_case_generate, load_module_from_file
|
||||
from base_framework.base_config.current_pth import *
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
from base_framework.platform_tools.Swagger_scanner.scanner_add import GenerateBody
|
||||
|
||||
dir_name = os.path.dirname(__file__)
|
||||
interface_path = os.path.join(dir_name, "Swagger_scanner/interface.txt")
|
||||
token_header = ["accesstoken", "user-token"]
|
||||
|
||||
|
||||
def get_all_interface_info_from_file(path):
|
||||
with open(path, encoding="UTF-8") as f:
|
||||
content = f.readlines()
|
||||
return content[1:]
|
||||
|
||||
|
||||
def write_interface_info_to_file(content, path=interface_path):
|
||||
with open(path, mode="r", encoding="UTF-8") as f:
|
||||
line = f.readline().strip("\n")
|
||||
with open(path, mode="w", encoding="UTF-8") as f1:
|
||||
f1.write(line + "\n" + content)
|
||||
|
||||
|
||||
def generate_result_file():
|
||||
file_name = "reslut_%s.txt" % time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())
|
||||
result_dir = os.path.join(dir_name, "case_result")
|
||||
result_file = os.path.abspath(os.path.join(result_dir, file_name))
|
||||
if not os.path.exists(result_dir):
|
||||
os.makedirs(result_dir)
|
||||
with open(result_file, "w+", encoding="UTF-8") as f:
|
||||
f.write("开始自动生成keyword和case! 开始时间:%s\n" % time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime()))
|
||||
return result_file
|
||||
|
||||
|
||||
def write_case_result_to_case_file(file_path, content):
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
lines = f.readlines()
|
||||
text = content + "\n"
|
||||
if lines:
|
||||
if lines[-1] != "\n":
|
||||
text = "\n" + "\n" + content + "\n"
|
||||
|
||||
f.write(text)
|
||||
|
||||
|
||||
def clean_the_interface_info(result_file, interface_info, team):
|
||||
interface = tuple(interface_info.strip("\n").split(","))
|
||||
interface_infos = SwaggerDBInfo.get_interface_info_by_url_and_type(interface[1], interface[0], team)
|
||||
if len(interface_infos) > 1:
|
||||
msg = "接口url:%s,%s,存在多条数据,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif len(interface_infos) == 0:
|
||||
msg = "接口url:%s,%s,在数据库中不存在,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
interface_info_attr = SetAttr(interface_infos[0])
|
||||
if interface_info_attr.is_used == 0:
|
||||
msg = "接口url:%s,%s,已不在使用,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif interface_info_attr.at_numbers > 0:
|
||||
msg = "接口url:%s,%s,已存在用例,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
msg = "接口url:%s,%s,开始生成接口关键字和用例!" % interface
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
return interface_info_attr
|
||||
return msg
|
||||
|
||||
|
||||
def generate_swagger_url_and_option(swagger_info):
|
||||
swagger_info_attr = SetAttr(swagger_info)
|
||||
url = swagger_info_attr.sw_url
|
||||
temp_list = url.split("v2")
|
||||
swagger_url = temp_list[0] + 'swagger-ui.html'
|
||||
option = '/v2' + temp_list[1]
|
||||
return swagger_url, option, temp_list[0]
|
||||
|
||||
|
||||
def get_demo_by_interface_info(interface_id):
|
||||
body_demo = GenerateBody(interface_id)
|
||||
request_data = body_demo.combine_request_parameters()
|
||||
if request_data["d"]:
|
||||
request_demo = body_demo.combine_request_data(request_data["d"][0], request_data["d"][1], key=0)
|
||||
request_data["d"] = request_demo
|
||||
return request_data
|
||||
|
||||
|
||||
def clear_request_parameters(parameters):
|
||||
"""
|
||||
清除参数中作为token的参数
|
||||
"""
|
||||
parameters_result = copy.deepcopy(parameters)
|
||||
for item in parameters:
|
||||
if item.get("name").lower() in token_header:
|
||||
parameters_result.remove(item)
|
||||
return parameters_result
|
||||
|
||||
|
||||
def clear_header_token_demo(demo_info):
|
||||
"""
|
||||
清除请求头中带有token这种无用参数
|
||||
"""
|
||||
demo_copy = json.loads(copy.deepcopy(demo_info.request_demo))
|
||||
header = copy.deepcopy(demo_copy.get('h'))
|
||||
for key, _ in demo_copy.get('h').items():
|
||||
if key in token_header:
|
||||
header.pop(key)
|
||||
if not header:
|
||||
demo_copy['h'] = {}
|
||||
else:
|
||||
demo_copy['h'] = header
|
||||
demo_info.request_demo = json.dumps(demo_copy)
|
||||
return demo_info
|
||||
|
||||
|
||||
def generate_parameter_to_dict(parameters):
|
||||
"""
|
||||
将数据库查询到的参数以参数名组成字典
|
||||
"""
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
|
||||
def clean_un_use_parameter(demo, parameters):
|
||||
"""
|
||||
删除不再使用的参数
|
||||
"""
|
||||
if isinstance(demo, SetAttr):
|
||||
demo_item = json.loads(demo.request_demo)
|
||||
demo_copy = copy.deepcopy(demo_item)
|
||||
else:
|
||||
demo_item = copy.deepcopy(demo)
|
||||
demo_copy = copy.deepcopy(demo)
|
||||
parameters_dict = generate_parameter_to_dict(parameters)
|
||||
if isinstance(demo, list):
|
||||
demo_copy.clear()
|
||||
for index in range(len(demo_item)):
|
||||
demo_copy.append(clean_un_use_parameter(demo_item[index], parameters))
|
||||
elif isinstance(demo_item, dict):
|
||||
for key, values in demo_item.items():
|
||||
if parameters_dict.get(key, None) and int(parameters_dict.get(key).is_need) == 2:
|
||||
demo_copy.pop(key)
|
||||
continue
|
||||
demo_copy[key] = clean_un_use_parameter(values, parameters)
|
||||
else:
|
||||
demo_copy = demo_copy
|
||||
return demo_copy
|
||||
|
||||
|
||||
def confirm_demo_value_string(kw, case, demo_string, parameters):
|
||||
"""
|
||||
确认demo中的请求参数是不是纯值,并非json说格式
|
||||
"""
|
||||
demo = copy.deepcopy(demo_string.request_demo)
|
||||
demo_value = json.loads(demo)
|
||||
if (not demo_value["d"]) and (demo_value["d"] != 0):
|
||||
return demo_value
|
||||
if (isinstance(demo_value["d"], str) or isinstance(demo_value["d"], int)):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
elif isinstance(demo_value["d"], list):
|
||||
if not isinstance(demo_value["d"][0], dict):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
return demo_value
|
||||
|
||||
|
||||
def confirm_correct_team_case(team):
|
||||
interface_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
try:
|
||||
module = load_module_from_file(interface_file_path, team.upper())
|
||||
real_team = team.upper()
|
||||
except Exception as err:
|
||||
real_team = team.lower()
|
||||
return real_team
|
||||
|
||||
|
||||
def run_interface_case(team, tags=None, server=None, normal=None):
|
||||
team = confirm_correct_team_case(team)
|
||||
kw_instance = KWFileOperation(team, server)
|
||||
result_file = generate_result_file()
|
||||
interface_contents = get_all_interface_info_from_file(interface_path)
|
||||
keyword = KWOperation(team, kw_instance)
|
||||
case = ITCaseContentOperation(team, kw_instance)
|
||||
for interface_temp in interface_contents:
|
||||
if interface_temp == "\n" or interface_temp.startswith("#"):
|
||||
continue
|
||||
interface = clean_the_interface_info(result_file, interface_temp, team)
|
||||
if not isinstance(interface, str):
|
||||
parameters = keyword.get_interface_parameters(interface.id)
|
||||
parameters = clear_request_parameters(parameters)
|
||||
if parameters:
|
||||
demo = get_demo_by_interface_info(interface.id)
|
||||
else:
|
||||
demo = {"d": {}, "h": {}, "p": {}, "u": {}}
|
||||
demo = [{"request_demo": json.dumps(demo)}]
|
||||
demo_info = SetAttr(demo[0])
|
||||
demo_info = clear_header_token_demo(demo_info)
|
||||
demo_info.request_demo = json.dumps(clean_un_use_parameter(demo_info, parameters))
|
||||
demo_info.request_demo = json.dumps(confirm_demo_value_string(keyword, case, demo_info, parameters))
|
||||
kw_status = run_keyword_generage(result_file, interface, demo_info, parameters, keyword)
|
||||
if kw_status:
|
||||
msg = "接口url:%s,%s,生成keyword成功!接下来继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
run_interface_case_generate(result_file, interface, demo_info, parameters, case, tags, normal)
|
||||
return True
|
||||
else:
|
||||
return interface
|
||||
|
||||
|
||||
def print_usage():
|
||||
parser_inst.print_usage()
|
||||
parser_inst.exit(1, "生成用例失败,请根据使用帮助传入正确参数。")
|
||||
|
||||
|
||||
def write_platform_to_running_env(platform, team):
|
||||
if not platform:
|
||||
platform = ""
|
||||
cfg_opt = ReadConfig(env_choose_path)
|
||||
cfg_opt.set_section("run_jira_id", "huohua-podenv", platform)
|
||||
cfg_opt.set_section("run_evn_name", "current_team", team)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="请输入组名!")
|
||||
parser.add_argument("-team", dest='team', help="TO or TMO")
|
||||
parser.add_argument("-tag", dest='tags', help="标签", default=None)
|
||||
parser.add_argument("-p", dest='platform', help="独立环境", default=None)
|
||||
parser.add_argument("-s", dest='server', help="服务名", default=None)
|
||||
parser.add_argument("-d", dest='driver', help="驱动路径", default=None)
|
||||
parser.add_argument("-n", dest='normal', help="是否只生成正常用例", default=None)
|
||||
args = parser.parse_args()
|
||||
global parser_inst
|
||||
parser_inst = parser
|
||||
if not args.team:
|
||||
print_usage()
|
||||
else:
|
||||
write_platform_to_running_env(args.platform, args.team)
|
||||
run_interface_case(args.team, args.tags, args.platform, args.server, args.driver, args.normal)
|
||||
105
base_framework/platform_tools/case_runner10.py
Normal file
105
base_framework/platform_tools/case_runner10.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__author__ = 'huaxuemin'
|
||||
|
||||
import argparse
|
||||
from runner9 import CaseRunner
|
||||
usage_info = '''
|
||||
usage:
|
||||
$ case_runner.py -w %WORKSPACE% -c %case_path% -t tc1,tc2 -i p0,p1,p2 -e norun,del -r True
|
||||
run tag cases in workspace
|
||||
'''
|
||||
|
||||
|
||||
def usage():
|
||||
print(usage_info)
|
||||
exit(-1)
|
||||
|
||||
|
||||
def get_test_case_name(build_id):
|
||||
try:
|
||||
if build_id:
|
||||
import pymysql
|
||||
db = pymysql.connect(host="mysql.qa.huohua.cn", user="qa-dev", password="jaeg3SCQt0", database="sparkatp",
|
||||
charset='utf8')
|
||||
cursor = db.cursor()
|
||||
get_sql = "SELECT test_case_id FROM sparkatp.build_params WHERE build_info_id={}".format(build_id)
|
||||
cursor.execute(get_sql)
|
||||
res = cursor.fetchone()
|
||||
test_case_name = ""
|
||||
if res:
|
||||
test_case_name = res[0].replace("(", "*").replace(")", "*").replace(" ", "*")
|
||||
cursor.close()
|
||||
db.close()
|
||||
return test_case_name
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return ""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="This is for introduction parameter")
|
||||
parser.add_argument("-w", dest='workspace', help="case_runner workspace")
|
||||
parser.add_argument("-c", dest='case_path', help="case path")
|
||||
parser.add_argument("-t", dest='test_case', default='', help="test case")
|
||||
parser.add_argument("-i", dest='include', default='', help="include tag")
|
||||
parser.add_argument("-e", dest='exclude', default='', help="exclude tag")
|
||||
parser.add_argument("-r", dest='rerun', default='true', help="rerun failed case")
|
||||
parser.add_argument("-env", dest='special_env', default='', help="special env")
|
||||
parser.add_argument("-team", dest='team', default='', help="job name with team ")
|
||||
parser.add_argument("-penv", dest='physics_env', help="QA or SIM", default='QA')
|
||||
parser.add_argument("-b", dest='business', help="hh or hhi", default='hh')
|
||||
parser.add_argument("-b_id", dest='build_info_id', help="build_info_id", default='')
|
||||
parser.add_argument("-b_url", dest='build_url', help="build_url", default='')
|
||||
parser.add_argument("-is_db", dest='is_use_db', help="is_use_db", default='0')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.workspace is None:
|
||||
usage()
|
||||
|
||||
workspace = args.workspace
|
||||
case_path = args.case_path
|
||||
test_case = args.test_case
|
||||
include = args.include
|
||||
exclude = args.exclude
|
||||
rerun = args.rerun
|
||||
special_env = args.special_env
|
||||
team = args.team.split("-")[0]
|
||||
physics_env = args.physics_env
|
||||
business = args.business
|
||||
build_info_id = args.build_info_id
|
||||
build_url = args.build_url
|
||||
is_use_db = args.is_use_db
|
||||
if str(is_use_db) == "1" and build_info_id:
|
||||
test_case_name = get_test_case_name(build_info_id)
|
||||
test_case = test_case_name if test_case_name else test_case
|
||||
|
||||
# if team not in ["CC", "EN", "H2R", "LaLive", "SCM", "TMO", "TO", "ASOP", "ASTWB", "ASORG"]:
|
||||
# team = ""
|
||||
|
||||
case_runner = CaseRunner(workspace, case_path, test_case, include, exclude, rerun, special_env, team, physics_env,
|
||||
business)
|
||||
try:
|
||||
# 更新独立环境代号到配置文件
|
||||
case_runner.update_platform()
|
||||
# 检查工作目录名字对应的端口,有就使用,没有就分配
|
||||
case_runner.assign_port()
|
||||
# 查找对应端口进程并杀掉
|
||||
case_runner.check_port()
|
||||
# 更新KWL和RF中的端口
|
||||
case_runner.update_port()
|
||||
# 更新重名测试套
|
||||
case_runner.update_suite()
|
||||
# 启动KWL
|
||||
case_runner.start_kwl()
|
||||
# 构建用例
|
||||
case_runner.run_cases()
|
||||
# 构建完成后,停止KWL
|
||||
case_runner.check_port()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
# 构建完成后,入库build_url
|
||||
case_runner.record_build_url(build_info_id, build_url)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
683
base_framework/platform_tools/case_runner9.py
Normal file
683
base_framework/platform_tools/case_runner9.py
Normal file
@@ -0,0 +1,683 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__author__ = 'huaxuemin'
|
||||
|
||||
from logbook import Logger
|
||||
import shutil
|
||||
import errno
|
||||
import os
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
import random
|
||||
import socket
|
||||
import configparser
|
||||
import codecs
|
||||
import time
|
||||
import json
|
||||
import re
|
||||
import requests
|
||||
|
||||
logging = Logger(__name__)
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROBOT_LOG_LEVEL = 'INFO'
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
|
||||
class OSType:
|
||||
WIN, LINUX, UNKNOWN = range(3)
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_type():
|
||||
import platform
|
||||
system_name = platform.system()
|
||||
if system_name.lower() == 'windows':
|
||||
return OSType.WIN
|
||||
elif system_name.lower() == 'linux':
|
||||
return OSType.LINUX
|
||||
else:
|
||||
return OSType.UNKNOWN
|
||||
|
||||
|
||||
def run_process(cmd_str, out_p=False):
|
||||
"""
|
||||
run command
|
||||
cmd_str unicode string.
|
||||
"""
|
||||
if OSType.WIN == OSType.get_type():
|
||||
# cmd_str = cmd_str.encode('gbk')
|
||||
cmd_str = cmd_str
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
cmd_str = cmd_str.encode('utf-8')
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
|
||||
logging.info('cmd: %s' % cmd_str)
|
||||
print(cmd_str)
|
||||
import subprocess
|
||||
from subprocess import PIPE
|
||||
close_fds = False if OSType.WIN == OSType.get_type() else True
|
||||
if out_p:
|
||||
p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=PIPE)
|
||||
p.wait()
|
||||
return p.returncode, p.stdout.read()
|
||||
else:
|
||||
p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=subprocess.DEVNULL)
|
||||
p.wait()
|
||||
return p.returncode, None
|
||||
|
||||
|
||||
class CaseRunner:
|
||||
|
||||
def __init__(self, workspace, case_path, test_case, include, exclude, rerun="true", special_env="", team="",
|
||||
physics_env="QA", business="hh"):
|
||||
self.workspace = workspace
|
||||
self.case_path = case_path
|
||||
self.test_case = test_case
|
||||
self.include = include
|
||||
self.exclude = exclude
|
||||
self.rerun = rerun
|
||||
self.special_env = special_env
|
||||
self.team = team
|
||||
self.physics_env = physics_env
|
||||
self.business = business
|
||||
self.con = sqlite3.connect(HERE + "/case_runner.db")
|
||||
self.cur = self.con.cursor()
|
||||
self.def_port = set("9" + "".join(map(str, random.choices(range(10), k=3))) for i in range(500))
|
||||
self.env_port = None
|
||||
self.pid = None
|
||||
self.wait_time = 120
|
||||
self.all_dir_name = set()
|
||||
self.galaxy_server_name_to_swagger = {"peppa-teach-opt-cms-api": "teach-opt-cms-api",
|
||||
"peppa-teach-biz-server": "peppa-teach-biz",
|
||||
"peppa-market-server": "peppa-market",
|
||||
"peppa-teach-parker-server": "peppa-teach-parker",
|
||||
"peppa-course-server": "peppa-course"}
|
||||
self.swagger_name_to_galaxy = {"teach-opt-cms-api": "peppa-teach-opt-cms-api",
|
||||
"peppa-teach-biz": "peppa-teach-biz-server",
|
||||
"peppa-market": "peppa-market-server",
|
||||
"peppa-teach-parker": "peppa-teach-parker-server",
|
||||
"peppa-course": "peppa-course-server"}
|
||||
# 获取opengalaxy ssotoken相关变量
|
||||
self.ops_uri = "http://opengalaxy.bg.huohua.cn"
|
||||
self.showUsername = "luohong"
|
||||
self.username = "luohong"
|
||||
self.password = "Lh123456789@"
|
||||
self.sso_login_url = "https://sso.huohua.cn/authentication/form"
|
||||
self.redirect_url = "https://sso.huohua.cn/oauth/authorize?client_id=open-galaxy&response_type=code&redirect_uri=http://opengalaxy.bg.huohua.cn/api/v1/users/user/ssologin/"
|
||||
|
||||
def getSsoToken(self):
|
||||
session = requests.session()
|
||||
post_data = dict()
|
||||
post_data['showUsername'] = self.showUsername
|
||||
post_data['username'] = self.username
|
||||
post_data['password'] = self.password
|
||||
session.post(url=self.sso_login_url, data=post_data, allow_redirects=True, verify=False)
|
||||
resp = session.get(
|
||||
url=self.redirect_url,
|
||||
allow_redirects=False,
|
||||
verify=False)
|
||||
resp1 = session.get(
|
||||
url=resp.headers['Location'],
|
||||
allow_redirects=False,
|
||||
verify=False)
|
||||
ssoToken = resp1.headers["Set-Cookie"].split(";")[4].split("=")[2]
|
||||
return ssoToken
|
||||
|
||||
def update_platform(self):
|
||||
if len(self.special_env) > 0:
|
||||
config_file = str(Path(self.workspace) / "base_framework/base_config/env_choose.ini")
|
||||
fd = open(config_file, encoding='utf-8')
|
||||
data = fd.read()
|
||||
if data[:3] == codecs.BOM_UTF8:
|
||||
data = data[3:]
|
||||
files = codecs.open(config_file, "w")
|
||||
files.write(data)
|
||||
files.close()
|
||||
fd.close()
|
||||
|
||||
cf = configparser.ConfigParser(allow_no_value=True)
|
||||
cf.read(config_file, encoding='utf-8')
|
||||
cf.set("run_jira_id", "huohua-podenv", self.special_env)
|
||||
with open(config_file, 'w') as fw: # 循环写入
|
||||
cf.write(fw)
|
||||
|
||||
def assign_port(self):
|
||||
workspace_base_name = Path(self.workspace).name
|
||||
query_port = "SELECT PORT FROM ENV_PORT WHERE NAME='{}'".format(workspace_base_name)
|
||||
res_port = self.cur.execute(query_port).fetchall()
|
||||
query_all_port = "SELECT PORT FROM ENV_PORT"
|
||||
res_all_port = self.cur.execute(query_all_port).fetchall()
|
||||
not_use_port = (self.def_port - set(str(item[0]) for item in res_all_port)).pop()
|
||||
print("not use port is {}".format(not_use_port))
|
||||
self.env_port = not_use_port
|
||||
if len(res_port) == 0:
|
||||
insert_name_port_sql = "INSERT INTO ENV_PORT(NAME,PORT)VALUES(?,?)"
|
||||
self.cur.execute(insert_name_port_sql, (workspace_base_name, not_use_port)).fetchall()
|
||||
self.con.commit()
|
||||
else:
|
||||
update_name_port_sql = "UPDATE ENV_PORT SET PORT='{}' WHERE NAME='{}'".format(self.env_port,
|
||||
workspace_base_name)
|
||||
self.cur.execute(update_name_port_sql).fetchall()
|
||||
self.con.commit()
|
||||
|
||||
self.cur.close()
|
||||
self.con.close()
|
||||
print("use port is {}".format(self.env_port))
|
||||
|
||||
# def assign_port(self):
|
||||
# workspace_base_name = Path(self.workspace).name
|
||||
# query_port = "SELECT PORT FROM ENV_PORT WHERE NAME='{}'".format(workspace_base_name)
|
||||
# res = self.cur.execute(query_port).fetchall()
|
||||
# if len(res) == 0:
|
||||
# query_all_port = "SELECT PORT FROM ENV_PORT"
|
||||
# res_all_port = self.cur.execute(query_all_port).fetchall()
|
||||
# not_use_port = (self.def_port - set(str(item[0]) for item in res_all_port)).pop()
|
||||
# print("not use port is {}".format(not_use_port))
|
||||
# insert_name_port_sql = "INSERT INTO ENV_PORT(NAME,PORT)VALUES(?,?)"
|
||||
# self.cur.execute(insert_name_port_sql, (workspace_base_name, not_use_port)).fetchall()
|
||||
# self.con.commit()
|
||||
# self.env_port = not_use_port
|
||||
# else:
|
||||
# self.env_port = str(res[0][0])
|
||||
# self.cur.close()
|
||||
# self.con.close()
|
||||
# print("use port is {}".format(self.env_port))
|
||||
|
||||
def _get_not_used_port(self):
|
||||
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_linux_cmd, out_p=True)
|
||||
if len(res_context) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def check_port(self):
|
||||
if OSType.WIN == OSType.get_type():
|
||||
find_pid_win_cmd = 'netstat -ano | findstr {} | findstr LISTENING'.format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_win_cmd, out_p=True)
|
||||
if res_code == 0:
|
||||
print(res_context)
|
||||
if len(res_context) > 0:
|
||||
try:
|
||||
self.pid = str(res_context).split()[-1].replace("\\r\\n'", "")
|
||||
self._kill_pid()
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_linux_cmd, out_p=True)
|
||||
if res_code == 0:
|
||||
print(res_context)
|
||||
# 获取pid
|
||||
if len(res_context) > 0:
|
||||
try:
|
||||
self.pid = str(res_context).split("\\n")[1].split()[1]
|
||||
self._kill_3_pid()
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
|
||||
def _kill_3_pid(self):
|
||||
self._kill_pid()
|
||||
count = 3
|
||||
while count > 0:
|
||||
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_linux_cmd, out_p=True)
|
||||
|
||||
if len(res_context) > 0:
|
||||
time.sleep(2)
|
||||
self.pid = str(res_context).split("\\n")[1].split()[1]
|
||||
self._kill_pid()
|
||||
count -= 1
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
def _kill_pid(self):
|
||||
if OSType.WIN == OSType.get_type():
|
||||
kill_pid_cmd = "taskkill /f /pid {}".format(self.pid)
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
kill_pid_cmd = "kill -9 {}".format(self.pid)
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
|
||||
res_code, res_context = run_process(kill_pid_cmd)
|
||||
if res_code:
|
||||
raise RuntimeError("kill pid: {} failed. error: {}".format(self.pid, res_context))
|
||||
|
||||
def update_port(self):
|
||||
main_py = Path(self.workspace) / "base_framework/main.py"
|
||||
tmp_py = Path(self.workspace) / "base_framework/tmp.py"
|
||||
shutil.copy(main_py, tmp_py)
|
||||
src_context = "port=9999"
|
||||
dst_context = "port={}".format(self.env_port)
|
||||
self._replace_file_context(tmp_py, main_py, src_context, dst_context)
|
||||
env_robot = Path(self.workspace) / "{}/test_case/Resource/AdapterKws/env.robot".format(self.team)
|
||||
tmp_robot = Path(self.workspace) / "{}/test_case/Resource/AdapterKws/tmp.robot".format(self.team)
|
||||
shutil.copy(env_robot, tmp_robot)
|
||||
src_context = "127.0.0.1:9999"
|
||||
dst_context = "127.0.0.1:{}".format(self.env_port)
|
||||
self._replace_file_context(tmp_robot, env_robot, src_context, dst_context)
|
||||
|
||||
def update_suite(self):
|
||||
case_dir = Path(self.workspace) / "{}/test_case".format(self.team)
|
||||
self._set_path(case_dir)
|
||||
|
||||
case_src_path = Path(self.case_path)
|
||||
if case_src_path.is_file():
|
||||
base_name = case_src_path.name.split(case_src_path.suffix)[0]
|
||||
if base_name.upper() in self.all_dir_name:
|
||||
self.case_path = str(case_src_path.with_name(base_name + "9" + case_src_path.suffix))
|
||||
|
||||
self._replace_path(case_dir)
|
||||
|
||||
def _set_path(self, src_path):
|
||||
for p in src_path.iterdir():
|
||||
if p.is_dir():
|
||||
self.all_dir_name.add(p.name.upper())
|
||||
self._set_path(p)
|
||||
|
||||
def _replace_path(self, src_path):
|
||||
for p in src_path.iterdir():
|
||||
if p.is_dir():
|
||||
self._replace_path(p)
|
||||
elif p.is_file():
|
||||
base_name = p.name.split(p.suffix)[0]
|
||||
if base_name.upper() in self.all_dir_name:
|
||||
p.rename(p.with_name(base_name + "9" + p.suffix))
|
||||
|
||||
def start_kwl(self):
|
||||
startup_path = Path(self.workspace) / "base_framework"
|
||||
startup = "startup.py"
|
||||
exec_cmd_params = ""
|
||||
if len(self.special_env) > 0:
|
||||
exec_cmd_params = exec_cmd_params + "-j {} ".format(self.special_env)
|
||||
if len(self.team) > 0:
|
||||
exec_cmd_params = exec_cmd_params + "-t {} ".format(self.team)
|
||||
if len(self.business) > 0:
|
||||
exec_cmd_params = exec_cmd_params + "-b {} ".format(self.business)
|
||||
if OSType.WIN == OSType.get_type():
|
||||
exec_cmd_path = "cd {} && python {} ".format(startup_path, startup)
|
||||
exec_cmd = exec_cmd_path + exec_cmd_params
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
exec_cmd_path = "cd {} && python3 {} ".format(startup_path, startup)
|
||||
exec_cmd = exec_cmd_path + exec_cmd_params
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
exec_cmd = exec_cmd[:-1]
|
||||
# import subprocess
|
||||
# from subprocess import PIPE
|
||||
# close_fds = False if OSType.WIN == OSType.get_type() else True
|
||||
# subprocess.Popen(exec_cmd, shell=True, close_fds=close_fds, stdout=subprocess.DEVNULL)
|
||||
try:
|
||||
pro = self._start_kwl_server(exec_cmd)
|
||||
if not pro.is_alive():
|
||||
print("--------pro.exec_status: ", pro.is_alive())
|
||||
pro.kill()
|
||||
time.sleep(5)
|
||||
self._kill_3_pid()
|
||||
self._start_kwl_server(exec_cmd)
|
||||
except Exception as e:
|
||||
print("--------error: ", e)
|
||||
|
||||
@staticmethod
|
||||
def _start_kwl_server(exec_cmd):
|
||||
from multiprocessing import Process
|
||||
p = Process(target=run_process, args=(exec_cmd,))
|
||||
p.daemon = True
|
||||
p.start()
|
||||
time.sleep(30)
|
||||
return p
|
||||
|
||||
@staticmethod
|
||||
def _replace_file_context(src_file, dst_file, src_context, dst_context):
|
||||
with open(src_file, "r") as f_src:
|
||||
with open(dst_file, "w") as f_dst:
|
||||
for line in f_src:
|
||||
if src_context in line:
|
||||
f_dst.writelines(line.replace(src_context, dst_context))
|
||||
else:
|
||||
f_dst.writelines(line)
|
||||
|
||||
def _get_report_dir(self):
|
||||
"""
|
||||
Create %WORKSPACE%/Report 用于存放对应构建的构建日志
|
||||
"""
|
||||
report_dir = os.path.join(self.workspace, 'Report')
|
||||
if not os.path.exists(report_dir):
|
||||
logging.info(u'create report directory: %s' % report_dir)
|
||||
os.makedirs(report_dir)
|
||||
|
||||
return report_dir
|
||||
|
||||
def _get_report_ci_out_dir(self):
|
||||
"""
|
||||
Create %WORKSPACE%/Report/ci_out,用于存放构建日志
|
||||
"""
|
||||
ci_out_dir = os.path.join(self.workspace, 'Report', 'ci_out')
|
||||
if os.path.exists(ci_out_dir):
|
||||
logging.info(u'create ci output directory: %s' % ci_out_dir)
|
||||
shutil.rmtree(ci_out_dir)
|
||||
|
||||
return ci_out_dir
|
||||
|
||||
@staticmethod
|
||||
def copy_any_thing(src, dst):
|
||||
try:
|
||||
shutil.copytree(src, dst)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOTDIR:
|
||||
shutil.copy(src, dst)
|
||||
else:
|
||||
raise
|
||||
|
||||
def _wait_kwl_run(self):
|
||||
i = 0
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
while i < self.wait_time:
|
||||
try:
|
||||
s.connect(("127.0.0.1", int(self.env_port)))
|
||||
s.shutdown(2)
|
||||
s.close()
|
||||
return
|
||||
except socket.error:
|
||||
i += 1
|
||||
s.close()
|
||||
|
||||
def run_cases(self):
|
||||
"""
|
||||
run cases by workspace include exclude.
|
||||
"""
|
||||
self._wait_kwl_run()
|
||||
report_dir = os.path.join(self.workspace, 'Report')
|
||||
if os.path.exists(report_dir):
|
||||
logging.info(u'clean report directory: %s' % report_dir)
|
||||
shutil.rmtree(report_dir)
|
||||
os.makedirs(report_dir)
|
||||
|
||||
if self.rerun == 'true' or self.rerun == '':
|
||||
self._rerun_failed_cases()
|
||||
else:
|
||||
self._default_run_cases()
|
||||
|
||||
out_dir = os.path.join(self._get_report_dir(), 'out')
|
||||
if os.path.exists(out_dir):
|
||||
# copy report to ci_out.
|
||||
self.copy_any_thing(self._get_report_dir(), self._get_report_ci_out_dir())
|
||||
else:
|
||||
raise RuntimeError(u'%s directory is not exist.' % out_dir)
|
||||
|
||||
def _default_run_cases(self):
|
||||
"""
|
||||
exec robot and output log to {report_dir}/out directory.
|
||||
return command execute exit code.
|
||||
"""
|
||||
|
||||
test_case_cmdstr = []
|
||||
if len(self.test_case) > 0:
|
||||
for tc in self.test_case.split(','):
|
||||
test_case_cmdstr.append(u'--test %s' % tc)
|
||||
|
||||
includes_cmdstr = []
|
||||
if len(self.include) > 0:
|
||||
for include in self.include.split(','):
|
||||
includes_cmdstr.append(u'--include %s' % include)
|
||||
|
||||
excludes_cmdstr = []
|
||||
if len(self.exclude) > 0:
|
||||
for exclude in self.exclude.split(','):
|
||||
excludes_cmdstr.append(u'--exclude %s' % exclude)
|
||||
output_directory = os.path.join(self._get_report_dir(), 'out')
|
||||
Path(output_directory).mkdir(exist_ok=True)
|
||||
|
||||
# Set ROBOT_SYSLOG_FILE and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = os.path.join(self._get_report_dir(), 'robot_syslog.txt')
|
||||
os.environ['ROBOT_SYSLOG_LEVEL'] = ROBOT_LOG_LEVEL
|
||||
|
||||
logging.info(u'building cases...')
|
||||
res_code, _ = run_process(' '.join(
|
||||
[u'robot'] + test_case_cmdstr + excludes_cmdstr + includes_cmdstr + [u'-d', output_directory,
|
||||
self.case_path]))
|
||||
|
||||
# clear ROBOT_SYSLOG_FILE(NONE) and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = 'NONE'
|
||||
|
||||
return res_code
|
||||
|
||||
def _rerun_failed_cases(self):
|
||||
"""
|
||||
execute robot twice(the second execute is only failed in first.)
|
||||
and remerge output to {report_dir}/out directory.
|
||||
"""
|
||||
|
||||
logging.info('rerunfailed cases mode...')
|
||||
|
||||
return_code = self._default_run_cases()
|
||||
logging.info('first run exit code: %s' % return_code)
|
||||
if not return_code:
|
||||
logging.info('first cases built successfully')
|
||||
return
|
||||
|
||||
logging.info('the first cases to build there is failure cases')
|
||||
output_directory = os.path.join(self._get_report_dir(), u'out')
|
||||
|
||||
if not os.path.exists(output_directory):
|
||||
raise RuntimeError(u'the first cases to built throw exception.')
|
||||
|
||||
output_directory_r1 = os.path.join(self._get_report_dir(), u'first_out')
|
||||
output_directory_r2 = os.path.join(self._get_report_dir(), u'second_out')
|
||||
logging.info('rename the first cases to build the output directory')
|
||||
cur_dir = os.getcwd()
|
||||
os.chdir(self._get_report_dir())
|
||||
if OSType.WIN == OSType.get_type():
|
||||
cmd_str = 'ren out first_out'
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
cmd_str = 'cp -R out first_out'
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
os.system(cmd_str)
|
||||
os.chdir(cur_dir)
|
||||
|
||||
output_directory_cmdstr = [u'-d', output_directory_r2]
|
||||
rerun_failed_cmdstr = [u'--rerunfailed', os.path.join(output_directory_r1, u'output.xml')]
|
||||
|
||||
logging.info('rerunfailed test cases...')
|
||||
run_process(' '.join([u'robot'] + output_directory_cmdstr + rerun_failed_cmdstr + [self.case_path]))
|
||||
|
||||
if not os.path.exists(output_directory_r2):
|
||||
raise RuntimeError(u'the second cases to built throw throw exception.')
|
||||
|
||||
# Set ROBOT_SYSLOG_FILE and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = os.path.join(self._get_report_dir(), 'robot_syslog2.txt')
|
||||
os.environ['ROBOT_SYSLOG_LEVEL'] = ROBOT_LOG_LEVEL
|
||||
|
||||
logging.info('merge report...')
|
||||
run_process(' '.join([u'rebot', u'-d', output_directory, u'-o', u'output.xml', u'--merge',
|
||||
os.path.join(output_directory_r1, u'output.xml'),
|
||||
os.path.join(output_directory_r2, u'output.xml')]))
|
||||
# clear ROBOT_SYSLOG_FILE(NONE) and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = 'NONE'
|
||||
|
||||
def record_build_url(self, build_id, report_url):
|
||||
try:
|
||||
if build_id:
|
||||
import pymysql
|
||||
db = pymysql.connect(host="mysql.qa.huohua.cn", user="qa-dev", password="jaeg3SCQt0",
|
||||
database="sparkatp", charset='utf8')
|
||||
cursor = db.cursor()
|
||||
if build_id and report_url:
|
||||
update_sql = "UPDATE sparkatp.build_info set report_url='{}',status=2 WHERE id={}".format(
|
||||
report_url, build_id)
|
||||
cursor.execute(update_sql)
|
||||
cursor.fetchall()
|
||||
try:
|
||||
self.get_jacoco_report(cursor, build_id)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
db.commit()
|
||||
cursor.close()
|
||||
db.close()
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def get_tester_by_project_id(self):
|
||||
"""
|
||||
获取project_id和tester
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
import pymysql
|
||||
db = pymysql.connect(host="10.250.200.53", user="root", password="peppa@test", database="tools",
|
||||
charset='utf8')
|
||||
cursor = db.cursor()
|
||||
|
||||
db.commit()
|
||||
cursor.close()
|
||||
db.close()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def get_project_id_by_build_id(self, cursor, build_id):
|
||||
try:
|
||||
if build_id:
|
||||
get_scene_id_sql = "SELECT scene_id FROM build_info where id='{}'".format(
|
||||
build_id)
|
||||
cursor.execute(get_scene_id_sql)
|
||||
scene_id_info = cursor.fetchone()
|
||||
if scene_id_info:
|
||||
scene_id = scene_id_info[0]
|
||||
get_project_id_sql = "SELECT project_id FROM scene_new where id='{}'".format(
|
||||
scene_id)
|
||||
cursor.execute(get_project_id_sql)
|
||||
project_id_info = cursor.fetchone()
|
||||
if project_id_info:
|
||||
project_id = project_id_info[0]
|
||||
return project_id
|
||||
return 0
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def get_jacoco_report(self, cursor, build_id):
|
||||
get_server_name_sql = "SELECT run_server_list FROM build_info where id='{}' and is_jacoco=1".format(build_id)
|
||||
cursor.execute(get_server_name_sql)
|
||||
server_name_info = cursor.fetchone()
|
||||
if server_name_info:
|
||||
server_name_list = eval(server_name_info[0])
|
||||
for server_name in server_name_list:
|
||||
if server_name == "PEPPA-TEACH-API" or server_name == "peppa-teach-api" or "-EXECUTOR" in server_name.upper():
|
||||
continue
|
||||
self._do_jacoco_report(server_name, build_id, cursor)
|
||||
|
||||
def _do_jacoco_report(self, project_name, build_id, cursor):
|
||||
if not self.special_env:
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, self.special_env, "master",
|
||||
self.special_env, "1", "", "QA环境构建,不需要收集增量覆盖率")
|
||||
cursor.execute(insert_data)
|
||||
return
|
||||
elif self.special_env.upper() == "NONE" or self.special_env.upper() == "QA":
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, self.special_env, "master",
|
||||
self.special_env, "1", "", "QA环境构建,不需要收集增量覆盖率")
|
||||
cursor.execute(insert_data)
|
||||
return
|
||||
# 收集覆盖率报告
|
||||
import requests
|
||||
import json
|
||||
if self.team.upper() in ["CC", "LALIVE", "H2R"]:
|
||||
self.team = "CC"
|
||||
if self.team.upper() in ["SCM", "ES"]:
|
||||
self.team = "ES"
|
||||
if self.team.upper() in ["TO", "TMO"]:
|
||||
self.team = "TTS"
|
||||
current_version = self.get_branch_from_open_galaxy(self.special_env, project_name)
|
||||
if current_version == "master":
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, current_version, current_version,
|
||||
self.special_env, "1", "", "master不需要收集增量覆盖率")
|
||||
cursor.execute(insert_data)
|
||||
return
|
||||
if not current_version:
|
||||
print("-----------------: {}, 独立环境: {}, 服务名: {}".format("未在独立环境中服务,不收集覆盖率", self.special_env, project_name))
|
||||
return
|
||||
if project_name.lower() in self.galaxy_server_name_to_swagger.keys():
|
||||
project_name = self.galaxy_server_name_to_swagger[project_name.lower()]
|
||||
base_version = "master"
|
||||
url = "http://10.250.0.252:8989/cov/syncCollectionCov"
|
||||
params = {"baseVersion": base_version, "businessName": self.team, "currentVersion": current_version,
|
||||
"departmentName": "质量保障中心",
|
||||
"envName": self.special_env, "isBranch": 1, "isDiff": 2, "projectName": project_name}
|
||||
res = requests.post(url, json=params)
|
||||
if res.status_code == 200:
|
||||
if json.loads(res.text)["msg"] != "success":
|
||||
logging.error("{}收集覆盖率报告失败,err: {}".format(project_name, res.text))
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, current_version, base_version,
|
||||
self.special_env, "3", "", res.text)
|
||||
cursor.execute(insert_data)
|
||||
else:
|
||||
jacoco_report_id = json.loads(res.text)["data"]
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, int(jacoco_report_id), self.team, project_name, current_version, base_version,
|
||||
self.special_env, "0", "", "")
|
||||
cursor.execute(insert_data)
|
||||
else:
|
||||
logging.error("{}收集覆盖率报告失败,status:{},err: {}".format(project_name, str(res.status_code), res.text))
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, current_version, base_version,
|
||||
self.special_env, "3", "", res.text)
|
||||
cursor.execute(insert_data)
|
||||
# print(res.text)
|
||||
|
||||
def get_branch_from_open_galaxy(self, special_env, server_name):
|
||||
if server_name.lower() in self.swagger_name_to_galaxy.keys():
|
||||
server_name = self.swagger_name_to_galaxy[server_name.lower()]
|
||||
try:
|
||||
headers = {
|
||||
"Api-Token": "2a1cbc25e0d183e1ec9fe5872c1433617c96da9e2905c4371d8f979479910aa1"}
|
||||
open_galaxy_url = "http://opengalaxy.bg.huohua.cn/api/v1/hcloud/tree/node/sec/application?sec_name={}".format(
|
||||
special_env)
|
||||
res = requests.get(open_galaxy_url, headers=headers)
|
||||
if res.status_code == 200:
|
||||
result = json.loads(res.text)
|
||||
for item in result["data"]["results"]:
|
||||
if item["name"].lower() == server_name.lower():
|
||||
branch = item["branch"]
|
||||
return branch
|
||||
return ""
|
||||
else:
|
||||
print("获取运维服务列表失败,status: {}, res: {}".format(res.status_code, res.text))
|
||||
return ""
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return ""
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# workspace = "/root/workspaces/huaxuemin-dev"
|
||||
workspace = r"E:\huohua\auto\huaxuemin-dev"
|
||||
# case_path = "/root/workspaces/huaxuemin-dev/HuoHuaTestCase/EN/1.接口/Peppa-Eng-Live/play_back.robot"
|
||||
case_path = r"E:\huohua\auto\huaxuemin-dev\HuoHuaTestCase\EN\1.接口\Peppa-Eng-Live\play_back.robot"
|
||||
test_case = ""
|
||||
include = ""
|
||||
exclude = ""
|
||||
test = CaseRunner(workspace, case_path, test_case, include, exclude)
|
||||
# test.assign_port()
|
||||
# test.check_port()
|
||||
# test.update_port()
|
||||
# test.start_kwl()
|
||||
# test.run_cases()
|
||||
# test.check_port()
|
||||
# build_info_id = '16329'
|
||||
# build_url = 'http://10.250.200.1:8080/jenkins/view/QE_JOB/job/qe_job1/63/'
|
||||
# test.record_build_url(build_info_id, build_url)
|
||||
res = test.get_branch_from_open_galaxy("HHC-92692", "peppa-asset-server")
|
||||
print(res)
|
||||
194
base_framework/platform_tools/feishu/feishu_document_api.py
Normal file
194
base_framework/platform_tools/feishu/feishu_document_api.py
Normal file
@@ -0,0 +1,194 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import json
|
||||
import requests
|
||||
import logging
|
||||
from base_framework.public_tools.utils import Tools
|
||||
obj_tool = Tools()
|
||||
|
||||
|
||||
class FeiShuMultidimensionalTableOperations:
|
||||
def __init__(self, app_token, table_id, view_id):
|
||||
"""
|
||||
初始化飞书多维表格操作类
|
||||
Args:
|
||||
app_token: 多维表格token,登录飞书多维表格,按F12,右侧接口列表中找带token的接口,复制token
|
||||
table_id: 多维表格id,登录飞书多维表格,在浏览器地址栏中找到table=后面的值
|
||||
view_id: 多维表格视图id,登录飞书多维表格,在浏览器地址栏中找到view=后面的值
|
||||
Note:
|
||||
如果返回显示禁止访问的话,说明你的飞书表格没有给应用开通权限,需要在更多-添加应用中添加此应用,并开通权限
|
||||
这里我们使用的应用名是:QualityAssurance-Customer-CD-QA
|
||||
如果你搜多不到这个应用,说明你没有此应用的使用权限,找陈江或刚哥开通权限
|
||||
"""
|
||||
self.app_token = app_token # 表格token
|
||||
self.table_id = table_id # 表格id
|
||||
self.view_id = view_id # 表格视图id
|
||||
self.app_id = 'cli_a53b456397bdd00c' # 飞书应用的app_id,使用前,多维表格需要在更多-添加应用中添加此应用,并开通权限
|
||||
self.app_secret = 's3U4tXp9lTz7imMDQsHDNcoO4AgzXWk7' # 飞书应用的app_secret
|
||||
self.fs_login_url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
|
||||
self.http_session = requests.session()
|
||||
self.tenant_access_token = ( # 以应用身份请求api的鉴权凭证,每次获取后有效期为2小时
|
||||
self.__get_tenant_access_token_by_feishu().get("tenant_access_token"))
|
||||
self.http_session.headers.update({"Content-Type": "application/json; charset=utf-8",
|
||||
"Authorization": f"Bearer {self.tenant_access_token}",
|
||||
"User-Agent": "lark-api-explorer/v1"})
|
||||
|
||||
def __get_tenant_access_token_by_feishu(self):
|
||||
"""功能:获取飞书应用的tenant_access_token:以应用身份请求api的鉴权凭证,每次获取后有效期为2小时"""
|
||||
headers = {'Content-Type': 'application/json; charset=utf-8'}
|
||||
data = {'app_id': self.app_id, 'app_secret': self.app_secret}
|
||||
r = requests.post(self.fs_login_url, data=json.dumps(data), headers=headers, verify=False)
|
||||
r_json = r.json()
|
||||
if r_json.get('code') == 0:
|
||||
return {"expire": r_json.get('expire'), "tenant_access_token": r_json.get('tenant_access_token')}
|
||||
else:
|
||||
raise Exception(f"获取原始tenant_access_token失败{r_json}")
|
||||
|
||||
def fsmt_search_from_m_table(self, filter_dict=None, sort=None, automatic_fields=False, field_names=None):
|
||||
"""
|
||||
功能:从多维表格中查询数据,详细说明见:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search
|
||||
Args:
|
||||
filter_dict: 过滤条件,类型:list,格式:[{"conjunction": "string", "conditions": "list"]
|
||||
conjunction: 过滤条件之间的关系,取值范围:and、or,当仅有一个条件时,也需要填写此字段
|
||||
conditions: 过滤条件,格式:[{"field_name": "string", "operator": "string", "value": "list"}]
|
||||
field_name: 筛选条件的左值,值为字段的名称,类型:string
|
||||
operator: 筛选条件的运算符,
|
||||
取值范围:is:等于
|
||||
isNot:不等于
|
||||
contains:包含
|
||||
doesNotContain:不包含
|
||||
isEmpty:为空
|
||||
isNotEmpty:不为空
|
||||
isGreater:大于
|
||||
isGreaterEqual:大于等于
|
||||
isLess:小于
|
||||
isLessEqual:小于等于
|
||||
like:LIKE 运算符。暂未支持
|
||||
in:IN 运算符。暂未支持
|
||||
value: 筛选条件的右值,值为字段的值,类型:list
|
||||
若查询条件是时间格式,且为具体时间或日期时,格式为:["ExactDate", "13位的时间戳"]
|
||||
同时也支持时间段查询,格式为:["具体的时间段描述字符"],如:
|
||||
["Today"]:今天
|
||||
["Tomorrow"]: 明天
|
||||
["Yesterday"]:昨天
|
||||
["CurrentWeek"]:本周
|
||||
["LastWeek"]:上周
|
||||
["CurrentMonth"]:本月
|
||||
["LastMonth"]:上月
|
||||
["TheLastWeek"]:过去七天内
|
||||
["TheNextWeek"]:未来七天内
|
||||
["TheLastMonth"]:过去三十天内
|
||||
["TheNextMonth"]:未来三十天内
|
||||
sort: 排序条件,类型:list,格式:[{"field_name": "string", "desc": "bool"}]
|
||||
field_name: 排序字段的名称,类型:string
|
||||
desc: 是否倒序排序,类型:bool, 默认值:False
|
||||
automatic_fields: 控制是否返回自动计算的字段, true 表示返回
|
||||
field_names: 指定本次查询返回记录中包含哪些字段
|
||||
Returns: {"code": 0, "msg": "",
|
||||
"data": {"items": "入参中自定义的返回字段",
|
||||
"has_more": "bool,是否还有更多项",
|
||||
"page_token": "boolean, 分页标记,当 has_more 为 true 时,会同时返回新的 page_token,否则不返回 page_token",
|
||||
"total": "int, 总数"}}
|
||||
"""
|
||||
url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/search"
|
||||
.format(self.app_token, self.table_id))
|
||||
request_data = {"view_id": self.view_id,
|
||||
"filter": filter_dict,
|
||||
"automatic_fields": automatic_fields,
|
||||
"sort": sort,
|
||||
"field_names": field_names}
|
||||
try:
|
||||
rs = self.http_session.post(url, data=json.dumps(request_data), verify=False)
|
||||
except Exception as e:
|
||||
raise Exception(f"调用飞书接口{url}失败:{e}")
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}")
|
||||
|
||||
def fsmt_insert_data_to_m_table(self, m_table_column_dict):
|
||||
"""
|
||||
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_create
|
||||
api文档地址:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_create
|
||||
"""
|
||||
url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/batch_create"
|
||||
.format(self.app_token, self.table_id))
|
||||
request_data = {"records": []}
|
||||
for column_dict in m_table_column_dict:
|
||||
request_data["records"].append({"fields": column_dict})
|
||||
try:
|
||||
rs = self.http_session.post(url, data=json.dumps(request_data), verify=False)
|
||||
except Exception as e:
|
||||
raise Exception(f"调用飞书接口{url}失败:{e}")
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}")
|
||||
|
||||
def fsmt_update_one_data_to_m_table(self, record_id, m_table_column_dict):
|
||||
"""
|
||||
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/:record_id/update
|
||||
api文档:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/update
|
||||
"""
|
||||
url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{self.table_id}/records/{record_id}"""
|
||||
data = {"fields": m_table_column_dict}
|
||||
logging.info(f"更新数据请求参数为:{data}")
|
||||
rs = self.http_session.put(url, data=json.dumps(data), verify=False)
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
logging.info(f"更新异常,异常返回结果为:{rs}")
|
||||
|
||||
|
||||
def batch_update_data(self, table_id, update_records_data_list):
|
||||
"""
|
||||
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_update
|
||||
api文档:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_update
|
||||
"""
|
||||
url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{table_id}/records/batch_update"""
|
||||
data = {"records": update_records_data_list}
|
||||
logging.info(f"批量更新数据请求参数为:{data}")
|
||||
rs = self.http_session.post(url, data=json.dumps(data), verify=False)
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
logging.info(f"批量更新异常,异常返回结果为:{rs}")
|
||||
raise Exception(f"调用飞书接口{url}失败:{rs}")
|
||||
|
||||
def delete_data(self):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
fs = FeiShuMultidimensionalTableOperations(app_token="HssibyRf4anbpxsA3G2c1IPwnic",
|
||||
table_id="tblVbNoZE1RI3Hdo",
|
||||
view_id="vewx9lK9hI")
|
||||
filter_dict = {"conjunction": "and",
|
||||
"conditions": [{"field_name": "时间", "operator": "is", "value": ["Today"]}]}
|
||||
sort = [{"field_name": "时间", "desc": True}]
|
||||
field_names = ["时间", "入班", "换班", "补课", "月份"]
|
||||
|
||||
rsp = fs.fsmt_search_from_m_table(filter_dict=filter_dict, sort=sort, field_names=field_names)
|
||||
|
||||
if rsp.get("code") == 0:
|
||||
if int(rsp.get("data").get("total")) == 0:
|
||||
ci_data = [['2024-07-24', 7022, 2040, 105]]
|
||||
request_data = []
|
||||
for item in ci_data:
|
||||
ci_date = obj_tool.get_format_date(r_time="{} 00:00:00".format(item[0]), r_type=13)
|
||||
request_data.append({"时间": ci_date, "入班": item[1], "换班": item[2], "补课": item[3],
|
||||
"月份": "{}月".format(int(item[0].split("-")[1]))})
|
||||
for item in request_data:
|
||||
print(item)
|
||||
# request_data = [{"时间": 1721664000000, "入班": 123, "换班": 124, "补课": 125, "月份": "7月"},
|
||||
# {"时间": 1721750400000, "入班": 223, "换班": 224, "补课": 225, "月份": "7月"}]
|
||||
rsp = fs.fsmt_insert_data_to_m_table(m_table_column_dict=request_data)
|
||||
print(rsp)
|
||||
else:
|
||||
print("数据已存在,本次不写入...")
|
||||
else:
|
||||
print("查询失败:{}".format(rsp))
|
||||
204
base_framework/platform_tools/kibana/logstashlog_kibana.py
Normal file
204
base_framework/platform_tools/kibana/logstashlog_kibana.py
Normal file
@@ -0,0 +1,204 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
功能:通过kibana查询logstash日志
|
||||
进度:待完善....
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
|
||||
class LogstashLogKibana:
|
||||
def __init__(self, k_user="wuyonggang", k_pwd="Mima@123"):
|
||||
self.kibana_host = "https://logstashlog-kibana.qc.huohua.cn/internal/bsearch"
|
||||
self.kibana_user = k_user
|
||||
self.kibana_pwd = k_pwd
|
||||
self.r_header = {'kbn-version': '7.14.2',
|
||||
'Content-Type': 'application/json; charset=gbk',
|
||||
'sec-ch-ua-mobile': r'?0'}
|
||||
self.request = requests.session()
|
||||
self._login_kibana()
|
||||
|
||||
def _login_kibana(self):
|
||||
url = "https://logstashlog-kibana.qc.huohua.cn/internal/security/login"
|
||||
payload = json.dumps({"providerType": "basic",
|
||||
"providerName": "basic",
|
||||
"currentURL": "https://logstashlog-kibana.qc.huohua.cn/login?msg=LOGGED_OUT",
|
||||
"params": {"username": self.kibana_user, "password": self.kibana_pwd}
|
||||
})
|
||||
resp = self.request.post(url, headers=self.r_header, data=payload)
|
||||
print(resp)
|
||||
|
||||
def _format_message(self, message):
|
||||
"""
|
||||
格式化消息
|
||||
:param message: 消息
|
||||
:return: 格式化后的消息
|
||||
"""
|
||||
|
||||
|
||||
def _get_time_tamp(self, minute=10):
|
||||
|
||||
# 获取当前时间
|
||||
current_time = datetime.utcnow().replace(tzinfo=timezone.utc)
|
||||
# 计算十分钟前的时间
|
||||
ten_minutes_ago = current_time - timedelta(minutes=minute)
|
||||
# 格式化时间为 Elasticsearch 时间戳格式
|
||||
gte_timestamp = ten_minutes_ago.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
|
||||
lte_timestamp = current_time.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
|
||||
return gte_timestamp, lte_timestamp
|
||||
|
||||
def lk_query_kibana_log(self, minutes=15, app_name=None, querys=None):
|
||||
|
||||
if querys is None:
|
||||
querys = ['java.lang.NullPointerException']
|
||||
if not (isinstance(querys, list) or isinstance(querys, dict)):
|
||||
raise ValueError('querys:{},必须为列表或者字典'.format(querys))
|
||||
gte_timestamp, lte_timestamp = self._get_time_tamp(minute=minutes)
|
||||
query_con = []
|
||||
if isinstance(querys, dict):
|
||||
query_cons = {"bool": {"should": [{"match_phrase": querys}], "minimum_should_match": 1}}
|
||||
else:
|
||||
for query in querys:
|
||||
if len(querys) == 1:
|
||||
query_cons = {
|
||||
"multi_match": {
|
||||
"type": "phrase",
|
||||
"query": query,
|
||||
"lenient": True
|
||||
}}
|
||||
else:
|
||||
query_con.append({
|
||||
"multi_match": {
|
||||
"type": "phrase",
|
||||
"query": query,
|
||||
"lenient": True
|
||||
}
|
||||
})
|
||||
query_cons = {
|
||||
"bool": {
|
||||
"filter": query_con}}
|
||||
|
||||
url = "https://logstashlog-kibana.qc.huohua.cn/internal/bsearch"
|
||||
payload = json.dumps({
|
||||
"batch": [
|
||||
{
|
||||
"request": {
|
||||
"params": {
|
||||
"index": "logstash-qc-logstashlog*",
|
||||
"body": {
|
||||
"size": 10000,
|
||||
"sort": [
|
||||
{
|
||||
"@timestamp": {
|
||||
"order": "desc",
|
||||
"unmapped_type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": True,
|
||||
"fields": [
|
||||
{
|
||||
"field": "*",
|
||||
"include_unmapped": "true"
|
||||
},
|
||||
{
|
||||
"field": "@timestamp",
|
||||
"format": "strict_date_optional_time"
|
||||
},
|
||||
{
|
||||
"field": "end_data",
|
||||
"format": "strict_date_optional_time"
|
||||
},
|
||||
{
|
||||
"field": "end_date",
|
||||
"format": "strict_date_optional_time"
|
||||
},
|
||||
{
|
||||
"field": "start_date",
|
||||
"format": "strict_date_optional_time"
|
||||
}
|
||||
],
|
||||
"aggs": {
|
||||
"2": {
|
||||
"date_histogram": {
|
||||
"field": "@timestamp",
|
||||
"fixed_interval": "30m",
|
||||
"time_zone": "Asia/Shanghai",
|
||||
"min_doc_count": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"script_fields": {},
|
||||
"stored_fields": [
|
||||
"*"
|
||||
],
|
||||
"runtime_mappings": {},
|
||||
"_source": False,
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [],
|
||||
"filter": [
|
||||
query_cons,
|
||||
{
|
||||
"range": {
|
||||
"@timestamp": {
|
||||
"gte": gte_timestamp,
|
||||
"lte": lte_timestamp,
|
||||
"format": "strict_date_optional_time"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match_phrase": {
|
||||
"APP_NAME": app_name
|
||||
}
|
||||
}
|
||||
],
|
||||
"should": [],
|
||||
"must_not": []
|
||||
}
|
||||
},
|
||||
"highlight": {
|
||||
"pre_tags": [
|
||||
"@kibana-highlighted-field@"
|
||||
],
|
||||
"post_tags": [
|
||||
"@/kibana-highlighted-field@"
|
||||
],
|
||||
"fields": {
|
||||
"*": {}
|
||||
},
|
||||
"fragment_size": 2147483647
|
||||
}
|
||||
},
|
||||
"track_total_hits": True,
|
||||
"preference": 1708481407352
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
# "sessionId": "473ee7d3-be00-411e-a925-71e2f669a230",
|
||||
"isRestore": False,
|
||||
"strategy": "ese",
|
||||
"isStored": False
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
print(payload)
|
||||
response = self.request.post(url, headers=self.r_header, data=payload)
|
||||
# binary_data = response.text
|
||||
# 解码为字符串并解析为 JSON 对象
|
||||
json_string = response.text
|
||||
json_data = json.loads(json_string)
|
||||
print(json_data)
|
||||
# print(response.json())
|
||||
# path = os.path.join(self.data_path_list, file_name)
|
||||
# with open(path, 'w+', encoding='utf-8') as f:
|
||||
# f.write(response.text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
lk = LogstashLogKibana()
|
||||
lk.lk_query_kibana_log(minutes=15, app_name="peppa-sparkle-scheduler",
|
||||
querys=['java.lang.NullPointerException'])
|
||||
55
base_framework/platform_tools/wyg_bingfa.py
Normal file
55
base_framework/platform_tools/wyg_bingfa.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import concurrent.futures
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
|
||||
cost_time = []
|
||||
ALL_TIMES = 10000
|
||||
|
||||
def make_request(url, method='GET', data=None):
|
||||
try:
|
||||
start_time = int(time.time() * 1000)
|
||||
if method.upper() == 'POST':
|
||||
response = requests.post(url, json=data, headers={"content-type": "application/json;charset=UTF-8"})
|
||||
else: # 默认使用GET方法
|
||||
response = requests.get(url, params=data)
|
||||
end_time = int(time.time() * 1000)
|
||||
elapsed_time = end_time - start_time
|
||||
cost_time.append(elapsed_time)
|
||||
return response.text, elapsed_time
|
||||
except requests.RequestException as e:
|
||||
return f"Request failed: {e}", None
|
||||
|
||||
def main():
|
||||
r_url = {'url': 'https://swagger.qa.huohua.cn/peppa-teach-timetable-server/timetableStudentServiceApi/queryListByIds',
|
||||
'method': 'POST', 'data': [225838679]}
|
||||
requests_list = [r_url] * ALL_TIMES
|
||||
|
||||
m_start_time = int(time.time() * 1000)
|
||||
success_times = 0
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
|
||||
future_to_req = {executor.submit(make_request, req['url'], req['method'], req['data']): req for req in requests_list}
|
||||
for future in concurrent.futures.as_completed(future_to_req):
|
||||
req = future_to_req[future]
|
||||
try:
|
||||
data, elapsed_time = future.result()
|
||||
if data and json.loads(data).get('code') == 200:
|
||||
success_times += 1
|
||||
# print(f"URL: {req['url']}\nMethod: {req['method']}\nData: {req['data']}\nResponse:\n{data}\nElapsed time: {elapsed_time:.2f} seconds\n")
|
||||
print("-----:本次耗时{}ms".format(elapsed_time))
|
||||
except Exception as e:
|
||||
print(f"Error fetching {req['url']}: {e}")
|
||||
m_end_time = int(time.time() * 1000)
|
||||
|
||||
all_times = (m_end_time - m_start_time)/1000
|
||||
tps = ALL_TIMES / all_times
|
||||
print("共计访问接口:{}次,成功访问接口:{}次,持续时长:{}秒,tps: {}"
|
||||
.format(len(cost_time), success_times, all_times, tps))
|
||||
print("成功访问接口:{}次".format(success_times))
|
||||
print("单次最大耗时:{}ms".format(max(cost_time)))
|
||||
print("单次最小耗时:{}ms".format(min(cost_time)))
|
||||
print("单次平均耗时:{}ms".format(sum(cost_time) / len(cost_time)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
403
base_framework/platform_tools/zendao_tools/handle_bug.py
Normal file
403
base_framework/platform_tools/zendao_tools/handle_bug.py
Normal file
@@ -0,0 +1,403 @@
|
||||
import requests
|
||||
import hashlib
|
||||
import json
|
||||
from bs4 import BeautifulSoup
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def send_to_feishu(webhook_url, content, keyword="bug"):
|
||||
"""
|
||||
发送消息到飞书机器人
|
||||
|
||||
Args:
|
||||
webhook_url: 飞书机器人的webhook地址
|
||||
content: 要发送的消息内容
|
||||
keyword: 关键词,需要包含在消息中
|
||||
"""
|
||||
try:
|
||||
# 构建消息体
|
||||
message = {
|
||||
"msg_type": "text",
|
||||
"content": {
|
||||
"text": f"{keyword}\n{content}"
|
||||
}
|
||||
}
|
||||
|
||||
# 发送请求
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
response = requests.post(webhook_url,
|
||||
data=json.dumps(message),
|
||||
headers=headers,
|
||||
timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
print(f"✅ 消息发送成功到飞书")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 飞书消息发送失败: {response.status_code}")
|
||||
print(f"响应: {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 发送飞书消息异常: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_all_bugs_and_send():
|
||||
"""获取所有产品的Bug并发送到飞书"""
|
||||
base_url = "http://39.170.26.156:8888"
|
||||
username = "qiaoxinjiu"
|
||||
password = "Qiao123456"
|
||||
|
||||
# 飞书配置
|
||||
feishu_webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/c7288ada-1c0c-472a-b652-a475a9586302"
|
||||
keyword = "bug"
|
||||
|
||||
print("获取禅道Bug列表")
|
||||
print("=" * 50)
|
||||
|
||||
# 登录
|
||||
session = requests.Session()
|
||||
password_md5 = hashlib.md5(password.encode()).hexdigest()
|
||||
|
||||
login_resp = session.post(f"{base_url}/api.php/v1/tokens",
|
||||
json={"account": username, "password": password_md5},
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
||||
if login_resp.status_code not in [200, 201]:
|
||||
error_msg = f"❌ 禅道登录失败: {login_resp.status_code}"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
token = login_resp.json().get('token')
|
||||
session.headers.update({'Token': token})
|
||||
|
||||
print("✅ 登录成功")
|
||||
|
||||
# 获取所有产品
|
||||
products_resp = session.get(f"{base_url}/api.php/v1/products")
|
||||
if products_resp.status_code != 200:
|
||||
error_msg = f"❌ 获取产品列表失败: {products_resp.status_code}"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
products = products_resp.json().get('products', [])
|
||||
print(f"📦 共 {len(products)} 个产品")
|
||||
|
||||
all_bugs = []
|
||||
|
||||
# 遍历每个产品获取Bug(只获取产品ID为2的)
|
||||
for product in products:
|
||||
product_id = product.get('id')
|
||||
product_name = product.get('name')
|
||||
|
||||
# 只获取产品ID为2的
|
||||
if product_id != 2:
|
||||
continue
|
||||
|
||||
print(f"\n获取产品 '{product_name}' 的Bug...")
|
||||
|
||||
# 获取HTML页面
|
||||
bugs_url = f"{base_url}/bug-browse-{product_id}.html"
|
||||
bugs_resp = session.get(bugs_url, params={'product': product_id, 'limit': 100})
|
||||
|
||||
if bugs_resp.status_code == 200:
|
||||
try:
|
||||
html = bugs_resp.text
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
|
||||
# 找 bug 列表的 table
|
||||
table = (
|
||||
soup.find("table", id="bugList")
|
||||
or soup.find("table", class_="table")
|
||||
or soup.find("table")
|
||||
)
|
||||
if not table:
|
||||
print(" ❌ 未找到 bug 列表 table")
|
||||
continue
|
||||
|
||||
# 解析表头
|
||||
header_tr = table.find("tr")
|
||||
if not header_tr:
|
||||
print(" ❌ 未找到表头行")
|
||||
continue
|
||||
|
||||
header_map = {}
|
||||
for idx, th in enumerate(header_tr.find_all(["th", "td"])):
|
||||
text = th.get_text(strip=True)
|
||||
if not text:
|
||||
continue
|
||||
if "ID" == text or text == "编号":
|
||||
header_map["id"] = idx
|
||||
elif "标题" in text:
|
||||
header_map["title"] = idx
|
||||
elif "状态" in text:
|
||||
header_map["status"] = idx
|
||||
elif "严重" in text:
|
||||
header_map["severity"] = idx
|
||||
elif "优先" in text or "优先级" in text:
|
||||
header_map["pri"] = idx
|
||||
elif "指派" in text:
|
||||
header_map["assignedTo"] = idx
|
||||
elif "创建" in text or "打开" in text:
|
||||
header_map["openedDate"] = idx
|
||||
|
||||
bugs = []
|
||||
|
||||
# 遍历表体行
|
||||
for tr in table.find_all("tr")[1:]:
|
||||
tds = tr.find_all("td")
|
||||
if not tds:
|
||||
continue
|
||||
|
||||
# id
|
||||
bug_id = tr.get("data-id")
|
||||
if not bug_id and "id" in header_map and header_map["id"] < len(tds):
|
||||
bug_id = tds[header_map["id"]].get_text(strip=True)
|
||||
if not bug_id:
|
||||
continue
|
||||
|
||||
# 标题
|
||||
title = ""
|
||||
if "title" in header_map and header_map["title"] < len(tds):
|
||||
cell = tds[header_map["title"]]
|
||||
link = cell.find("a")
|
||||
title = (link or cell).get_text(strip=True)
|
||||
|
||||
# 状态
|
||||
status = ""
|
||||
if "status" in header_map and header_map["status"] < len(tds):
|
||||
status = tds[header_map["status"]].get_text(strip=True)
|
||||
|
||||
# 严重程度
|
||||
severity = ""
|
||||
if "severity" in header_map and header_map["severity"] < len(tds):
|
||||
sev_cell = tds[header_map["severity"]]
|
||||
severity = sev_cell.get_text(strip=True)
|
||||
if not severity:
|
||||
sev_span = sev_cell.find("span")
|
||||
if sev_span and sev_span.get("title"):
|
||||
severity = sev_span.get("title").strip()
|
||||
|
||||
# 优先级
|
||||
pri = ""
|
||||
if "pri" in header_map and header_map["pri"] < len(tds):
|
||||
pri = tds[header_map["pri"]].get_text(strip=True)
|
||||
|
||||
# 指派给
|
||||
assigned_to = ""
|
||||
if "assignedTo" in header_map and header_map["assignedTo"] < len(tds):
|
||||
assigned_to = tds[header_map["assignedTo"]].get_text(strip=True)
|
||||
|
||||
# 创建/打开日期
|
||||
opened_date = ""
|
||||
if "openedDate" in header_map and header_map["openedDate"] < len(tds):
|
||||
opened_date = tds[header_map["openedDate"]].get_text(strip=True)
|
||||
|
||||
bug = {
|
||||
"id": bug_id,
|
||||
"title": title,
|
||||
"statusName": status,
|
||||
"severity": severity,
|
||||
"pri": pri,
|
||||
"assignedToName": assigned_to,
|
||||
"openedDate": opened_date,
|
||||
"product_name": product_name,
|
||||
}
|
||||
bugs.append(bug)
|
||||
|
||||
print(f" ✅ 解析出 {len(bugs)} 个Bug")
|
||||
all_bugs.extend(bugs)
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 解析失败: {e}")
|
||||
else:
|
||||
print(f" ❌ 获取失败: {bugs_resp.status_code}")
|
||||
|
||||
# 处理并发送统计信息
|
||||
if all_bugs:
|
||||
# 计算统计数据
|
||||
today_str = datetime.today().strftime("%Y-%m-%d")
|
||||
today_md_str = datetime.today().strftime("%m-%d")
|
||||
|
||||
total_bugs = len(all_bugs)
|
||||
|
||||
# 今日新增Bug
|
||||
today_bugs = [
|
||||
b for b in all_bugs
|
||||
if (
|
||||
today_str in str(b.get("openedDate", "")).strip()
|
||||
or today_md_str in str(b.get("openedDate", "")).strip()
|
||||
)
|
||||
]
|
||||
|
||||
# 未关闭Bug(状态不包含"已关闭"、"已解决"等)
|
||||
open_bugs = [
|
||||
b for b in all_bugs
|
||||
if not any(kw in str(b.get("statusName", "")).lower() for kw in ["closed", "resolved", "done", "cancel"])
|
||||
and not any(kw in str(b.get("statusName", "")) for kw in ["已关闭", "已解决", "已完成", "已取消"])
|
||||
]
|
||||
|
||||
# 按指派人员统计
|
||||
assigned_stats = {}
|
||||
for bug in all_bugs:
|
||||
assigned = bug.get("assignedToName", "未指派")
|
||||
assigned_stats[assigned] = assigned_stats.get(assigned, 0) + 1
|
||||
|
||||
# 按状态统计
|
||||
status_stats = {}
|
||||
for bug in all_bugs:
|
||||
status = bug.get("statusName", "未知")
|
||||
status_stats[status] = status_stats.get(status, 0) + 1
|
||||
|
||||
# 构建飞书消息
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
message = f"🐛 禅道Bug统计报告\n"
|
||||
message += f"⏰ 时间: {current_time}\n"
|
||||
message += f"📊 产品: 智慧运营平台V1.0\n"
|
||||
message += f"================================\n"
|
||||
message += f"📈 Bug统计概览:\n"
|
||||
message += f" • 总Bug数: {total_bugs}\n"
|
||||
message += f" • 今日新增: {len(today_bugs)}\n"
|
||||
message += f" • 未关闭Bug: {len(open_bugs)}\n"
|
||||
message += f"\n📋 状态分布:\n"
|
||||
|
||||
# 添加状态统计
|
||||
for status, count in sorted(status_stats.items()):
|
||||
message += f" • {status}: {count}个\n"
|
||||
|
||||
message += f"\n👥 指派人员统计:\n"
|
||||
# 添加指派统计(前5名)
|
||||
sorted_assigned = sorted(assigned_stats.items(), key=lambda x: x[1], reverse=True)[:5]
|
||||
for person, count in sorted_assigned:
|
||||
message += f" • {person}: {count}个\n"
|
||||
|
||||
# 今日新增Bug详情
|
||||
if today_bugs:
|
||||
message += f"\n🆕 今日新增Bug详情 ({len(today_bugs)}个):\n"
|
||||
today_bugs.sort(key=lambda x: int(x.get('id', 0)), reverse=True)
|
||||
for bug in today_bugs[:10]: # 只显示前10个
|
||||
bug_url = f"http://39.170.26.156:8888/bug-view-{bug.get('id')}.html"
|
||||
message += f" [#{bug.get('id')}] {bug.get('title', '')[:30]}...\n"
|
||||
message += f" 状态: {bug.get('statusName', '未知')} | 严重: {bug.get('severity', '未知')} | 指派: {bug.get('assignedToName', '未指派')}\n"
|
||||
|
||||
# 未关闭Bug详情(前5个)
|
||||
if open_bugs:
|
||||
open_bugs.sort(key=lambda x: int(x.get('id', 0)), reverse=True)
|
||||
message += f"\n⚠️ 最新未关闭Bug (前5个):\n"
|
||||
for bug in open_bugs[:5]:
|
||||
bug_url = f"http://39.170.26.156:8888/bug-view-{bug.get('id')}.html"
|
||||
message += f" [#{bug.get('id')}] {bug.get('title', '')[:30]}...\n"
|
||||
message += f" 状态: {bug.get('statusName', '未知')} | 严重: {bug.get('severity', '未知')} | 指派: {bug.get('assignedToName', '未指派')}\n"
|
||||
|
||||
message += f"\n🔗 禅道地址: {base_url}"
|
||||
|
||||
# 打印到控制台
|
||||
print(f"\n{'=' * 80}")
|
||||
print(message)
|
||||
print(f"{'=' * 80}")
|
||||
|
||||
# 发送到飞书
|
||||
print("\n发送消息到飞书...")
|
||||
success = send_to_feishu(feishu_webhook, message, keyword)
|
||||
|
||||
if success:
|
||||
print(f"✅ Bug统计报告已发送到飞书")
|
||||
else:
|
||||
print(f"❌ 飞书消息发送失败")
|
||||
else:
|
||||
message = f"📭 禅道Bug统计报告\n"
|
||||
message += f"⏰ 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
message += f"📊 产品: 智慧运营平台V1.0\n"
|
||||
message += f"================================\n"
|
||||
message += f"✅ 当前没有Bug数据\n"
|
||||
message += f"🔗 禅道地址: {base_url}"
|
||||
|
||||
print(message)
|
||||
send_to_feishu(feishu_webhook, message, keyword)
|
||||
|
||||
|
||||
def send_simple_report():
|
||||
"""发送简化的Bug统计报告到飞书"""
|
||||
base_url = "http://39.170.26.156:8888"
|
||||
username = "qiaoxinjiu"
|
||||
password = "Qiao123456"
|
||||
|
||||
# 飞书配置
|
||||
feishu_webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/c7288ada-1c0c-472a-b652-a475a9586302"
|
||||
keyword = "bug"
|
||||
|
||||
print("获取禅道Bug统计")
|
||||
print("=" * 50)
|
||||
|
||||
try:
|
||||
# 登录
|
||||
session = requests.Session()
|
||||
password_md5 = hashlib.md5(password.encode()).hexdigest()
|
||||
|
||||
login_resp = session.post(f"{base_url}/api.php/v1/tokens",
|
||||
json={"account": username, "password": password_md5},
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
||||
if login_resp.status_code not in [200, 201]:
|
||||
error_msg = f"❌ 禅道登录失败"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
token = login_resp.json().get('token')
|
||||
session.headers.update({'Token': token})
|
||||
print("✅ 登录成功")
|
||||
|
||||
# 获取产品ID=2的Bug页面
|
||||
bug_url = f"{base_url}/bug-browse-2.html"
|
||||
response = session.get(bug_url)
|
||||
|
||||
if response.status_code != 200:
|
||||
error_msg = f"❌ 无法获取Bug页面"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
# 简单统计Bug数量
|
||||
import re
|
||||
bug_matches = re.findall(r'bug-view-(\d+)\.html', response.text)
|
||||
total_bugs = len(set(bug_matches)) # 去重
|
||||
|
||||
# 构建简单消息
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
message = f"🐛 禅道Bug速报\n"
|
||||
message += f"⏰ 时间: {current_time}\n"
|
||||
message += f"📊 产品: 智慧运营平台V1.0\n"
|
||||
message += f"================================\n"
|
||||
message += f"📈 当前总Bug数: {total_bugs}\n"
|
||||
message += f"🔗 查看详情: {bug_url}\n"
|
||||
message += f"\n💡 提示: 详细统计请查看禅道系统"
|
||||
|
||||
print(f"发现 {total_bugs} 个Bug")
|
||||
|
||||
# 发送到飞书
|
||||
success = send_to_feishu(feishu_webhook, message, keyword)
|
||||
|
||||
if success:
|
||||
print(f"✅ 简版Bug报告已发送到飞书")
|
||||
else:
|
||||
print(f"❌ 飞书消息发送失败")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"❌ 获取Bug统计异常: {str(e)}"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 运行完整版本(带详细统计)
|
||||
get_all_bugs_and_send()
|
||||
|
||||
# 或者运行简化版本(只发速报)
|
||||
# send_simple_report()
|
||||
237
base_framework/platform_tools/zendao_tools/zendao_api.py
Normal file
237
base_framework/platform_tools/zendao_tools/zendao_api.py
Normal file
@@ -0,0 +1,237 @@
|
||||
from __future__ import annotations
|
||||
|
||||
"""
|
||||
ZenDao bug list crawler.
|
||||
|
||||
Design overview:
|
||||
1. Read credentials + crawl settings from zendao_config.ini.
|
||||
2. Use ZenDao REST API (`api.php/v1/accessTokens`) to obtain a short-lived token.
|
||||
3. Pull paginated bug records under the configured product via `/api.php/v1/products/{product_id}/bugs`.
|
||||
4. Export normalized bug data to CSV (default) or JSON, usable by other automation.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, List, Sequence
|
||||
|
||||
import requests
|
||||
from requests import Response, Session
|
||||
|
||||
|
||||
BUG_EXPORT_FIELDS: Sequence[str] = (
|
||||
"id",
|
||||
"title",
|
||||
"status",
|
||||
"severity",
|
||||
"pri",
|
||||
"openedBy",
|
||||
"openedDate",
|
||||
"assignedTo",
|
||||
"resolvedBy",
|
||||
"resolution",
|
||||
"lastEditedDate",
|
||||
)
|
||||
DEFAULT_CONFIG_PATH = Path(__file__).with_name("zendao_config.ini")
|
||||
|
||||
|
||||
class ZenDaoAPIError(RuntimeError):
|
||||
"""Raised when ZenDao returns an unexpected payload."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class ZenDaoSettings:
|
||||
base_url: str
|
||||
username: str
|
||||
password: str
|
||||
product_id: int
|
||||
per_page: int = 50
|
||||
max_pages: int = 10
|
||||
verify_ssl: bool = False
|
||||
timeout: int = 10
|
||||
auth_mode: str = "password" # password | token
|
||||
api_token: str | None = None
|
||||
auth_endpoint: str = "api.php/v1/tokens"
|
||||
token_header: str = "Token"
|
||||
password_hash: str = "md5" # md5 | plain
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, path: Path, section: str = "zendao") -> "ZenDaoSettings":
|
||||
parser = configparser.ConfigParser()
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"Config file not found: {path}")
|
||||
|
||||
parser.read(path, encoding="utf-8")
|
||||
if section not in parser:
|
||||
raise KeyError(f"Section [{section}] not found in {path}")
|
||||
|
||||
cfg = parser[section]
|
||||
return cls(
|
||||
base_url=cfg.get("base_url", "").rstrip("/"),
|
||||
username=cfg.get("username", ""),
|
||||
password=cfg.get("password", ""),
|
||||
product_id=cfg.getint("product_id", fallback=0),
|
||||
per_page=cfg.getint("per_page", fallback=50),
|
||||
max_pages=cfg.getint("max_pages", fallback=10),
|
||||
verify_ssl=cfg.getboolean("verify_ssl", fallback=False),
|
||||
timeout=cfg.getint("timeout", fallback=10),
|
||||
auth_mode=cfg.get("auth_mode", "password").lower(),
|
||||
api_token=cfg.get("api_token"),
|
||||
auth_endpoint=cfg.get("auth_endpoint", "api.php/v1/tokens"),
|
||||
token_header=cfg.get("token_header", "Token"),
|
||||
password_hash=cfg.get("password_hash", "md5").lower(),
|
||||
)
|
||||
|
||||
def validate(self) -> None:
|
||||
missing = [
|
||||
name
|
||||
for name, value in (
|
||||
("base_url", self.base_url),
|
||||
("username", self.username),
|
||||
("password", self.password),
|
||||
)
|
||||
if not value
|
||||
]
|
||||
if missing:
|
||||
raise ValueError(f"Missing config values: {', '.join(missing)}")
|
||||
if self.product_id <= 0:
|
||||
raise ValueError("product_id must be > 0")
|
||||
if self.auth_mode not in {"password", "token"}:
|
||||
raise ValueError("auth_mode must be 'password' or 'token'")
|
||||
if self.password_hash not in {"md5", "plain"}:
|
||||
raise ValueError("password_hash must be 'md5' or 'plain'")
|
||||
if self.auth_mode == "token" and not self.api_token:
|
||||
raise ValueError("api_token required when auth_mode=token")
|
||||
|
||||
|
||||
class ZenDaoClient:
|
||||
def __init__(self, settings: ZenDaoSettings, session: Session | None = None) -> None:
|
||||
self.settings = settings
|
||||
self.session: Session = session or requests.Session()
|
||||
self._token: str | None = None
|
||||
|
||||
def authenticate(self) -> str:
|
||||
if self.settings.auth_mode == "token":
|
||||
if not self.settings.api_token:
|
||||
raise ValueError("api_token missing in config")
|
||||
self.session.headers.update({self.settings.token_header: self.settings.api_token})
|
||||
self._token = self.settings.api_token
|
||||
return self._token
|
||||
|
||||
payload = {
|
||||
"account": self.settings.username,
|
||||
"password": self._format_password(self.settings.password),
|
||||
}
|
||||
response = self._request("post", self.settings.auth_endpoint, json=payload)
|
||||
data = response.json()
|
||||
token = data.get("token") or data.get("accessToken")
|
||||
if not token:
|
||||
raise ZenDaoAPIError(f"Unexpected token response: {data}")
|
||||
|
||||
self._token = token
|
||||
self.session.headers.update({self.settings.token_header: token})
|
||||
return token
|
||||
|
||||
def _format_password(self, password: str) -> str:
|
||||
if self.settings.password_hash == "md5":
|
||||
return hashlib.md5(password.encode()).hexdigest()
|
||||
return password
|
||||
|
||||
def fetch_bugs_page(self, page: int = 1) -> List[Dict]:
|
||||
params = {"page": page, "limit": self.settings.per_page}
|
||||
endpoint = f"api.php/v1/products/{self.settings.product_id}/bugs"
|
||||
response = self._request("get", endpoint, params=params)
|
||||
payload = response.json()
|
||||
bugs = payload.get("bugs") or payload.get("data")
|
||||
if bugs is None:
|
||||
raise ZenDaoAPIError(f"Unexpected bug payload: {payload}")
|
||||
return list(bugs)
|
||||
|
||||
def fetch_all_bugs(self) -> List[Dict]:
|
||||
all_bugs: List[Dict] = []
|
||||
for page in range(1, self.settings.max_pages + 1):
|
||||
page_bugs = self.fetch_bugs_page(page)
|
||||
if not page_bugs:
|
||||
break
|
||||
all_bugs.extend(page_bugs)
|
||||
if len(page_bugs) < self.settings.per_page:
|
||||
break
|
||||
return all_bugs
|
||||
|
||||
def _request(self, method: str, path: str, **kwargs) -> Response:
|
||||
url = f"{self.settings.base_url}/{path.lstrip('/')}"
|
||||
kwargs.setdefault("timeout", self.settings.timeout)
|
||||
kwargs.setdefault("verify", self.settings.verify_ssl)
|
||||
response = self.session.request(method=method, url=url, **kwargs)
|
||||
response.raise_for_status()
|
||||
return response
|
||||
|
||||
|
||||
def normalize_bug(bug: Dict, fields: Sequence[str] = BUG_EXPORT_FIELDS) -> Dict[str, str]:
|
||||
normalized = {}
|
||||
for field in fields:
|
||||
value = bug.get(field, "")
|
||||
if isinstance(value, dict):
|
||||
value = value.get("realname") or value.get("account") or value
|
||||
normalized[field] = value if isinstance(value, str) else str(value)
|
||||
return normalized
|
||||
|
||||
|
||||
def write_json(path: Path, bugs: Iterable[Dict]) -> None:
|
||||
path.write_text(json.dumps(list(bugs), ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
|
||||
|
||||
def write_csv(path: Path, bugs: Iterable[Dict], fields: Sequence[str]) -> None:
|
||||
bugs = list(bugs)
|
||||
if not bugs:
|
||||
path.write_text("", encoding="utf-8")
|
||||
return
|
||||
|
||||
with path.open("w", encoding="utf-8", newline="") as csv_file:
|
||||
writer = csv.DictWriter(csv_file, fieldnames=fields)
|
||||
writer.writeheader()
|
||||
for bug in bugs:
|
||||
writer.writerow(normalize_bug(bug, fields))
|
||||
|
||||
|
||||
def run_crawler(config: Path, output: Path) -> Path:
|
||||
settings = ZenDaoSettings.from_file(config)
|
||||
settings.validate()
|
||||
|
||||
client = ZenDaoClient(settings)
|
||||
client.authenticate()
|
||||
bugs = client.fetch_all_bugs()
|
||||
|
||||
if output.suffix.lower() == ".json":
|
||||
write_json(output, bugs)
|
||||
else:
|
||||
write_csv(output, bugs, BUG_EXPORT_FIELDS)
|
||||
return output
|
||||
|
||||
|
||||
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Fetch bug list from ZenDao.")
|
||||
parser.add_argument("--config", type=Path, default=DEFAULT_CONFIG_PATH, help="Path to zendao_config.ini")
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=Path,
|
||||
default=Path("bugs.csv"),
|
||||
help="Output file path (.csv or .json)",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> None:
|
||||
args = parse_args(argv or sys.argv[1:])
|
||||
output = run_crawler(args.config, args.output)
|
||||
print(f"Bug list saved to: {output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
14
base_framework/platform_tools/zendao_tools/zendao_config.ini
Normal file
14
base_framework/platform_tools/zendao_tools/zendao_config.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
[zendao]
|
||||
base_url = http://39.170.26.156:8888
|
||||
username = qiaoxinjiu
|
||||
password = Qiao123456
|
||||
product_id = 1
|
||||
per_page = 50
|
||||
max_pages = 10
|
||||
verify_ssl = false
|
||||
timeout = 10
|
||||
auth_mode = password
|
||||
api_token =
|
||||
auth_endpoint = api.php/v1/tokens
|
||||
token_header = Token
|
||||
password_hash = md5
|
||||
Reference in New Issue
Block a user