def execute(self, request): zk = KazooClient(hosts=request['zk_url']) try: zk.start() path_ = request['node_path'] value_ = request['node_value'].encode('utf-8') is_update_timestamp_ = request.get('is_update_timestamp', False) is_delete_node_ = request.get('is_delete_node', None) if zk.exists(path_): if is_delete_node_: zk.delete(path_, recursive=True) return self.resp_dict, '' if is_update_timestamp_: zk.set(path_ + '/TIMESTAMP', ZkTestPoint.get_timestamp_bytes()) if value_: zk.set(path_, value_) else: data_, stat_ = zk.get(path_) if data_: self.resp_dict['result_value'] = data_.decode() elif value_: # 新加节点时也需要添加 '/TIMESTAMP' 节点 zk.create(path_, value_, makepath=True) zk.create(path_ + '/TIMESTAMP', ZkTestPoint.get_timestamp_bytes(), makepath=True) return self.resp_dict, '' except Exception as e: logger.exception('zkTestPoint execute error, cause: %s', str(e)) raise Exception('zkTestPoint execute error, cause: ' + str(e)) finally: zk.stop()
def execute(self, request): # 发起接口调用请求并接收响应 try: host = self.tp_conf.get(ActiveMqTestPointEnum.host.key) port = self.tp_conf.get(ActiveMqTestPointEnum.port.key) user_name = self.tp_conf.get(ActiveMqTestPointEnum.port.key) password = self.tp_conf.get(ActiveMqTestPointEnum.password.key) destination = self.tp_conf.get( ActiveMqTestPointEnum.destination.key) conn = stomp.Connection10(host_and_ports=[(host, port)]) conn.start() conn.connect(username=user_name, password=password) conn.send(destination=destination, body=json.dumps(request), headers={'amq-msg-type': 'text'}) conn.disconnect() logger.info('tp->active mq:消息发送成功,destination=' + destination + 'msg=' + json.dumps(request)) result = {'result_value': True} return result, '' except ConnectionError as e: logger.error('tp->active mq:connection error:', str(e)) raise RuntimeError('active mq测试,调用被测系统时连接异常') except Exception as e: logger.exception('tp->active mq:runtime error:', str(e)) raise RuntimeError('active mq测试,调用被测系统时执行异常')
def runtask(filename, joblist, flag): """ start a task """ if not filename: click.echo(click.style('file name error, please check !', fg='red')) sys.exit() create_sqlite_db() init_db_info() task_id = common.get_time_uuid().replace('-', '') try: task_detail_loader = ParameterTaskDetailLoader( task_id, filename, joblist, None, flag, None, 'agbot-cmd', 'agbot-cmd' ) # task_content 配置 task_detail = task_detail_loader.load() # type: TaskDetail logger.info('start task-------------------------------------------task_id:' + task_id) # 检查任务是否已经存在 _, tab_task_log = DBUtils.query(TableTaskLog, filter_dict={'task_id': task_id}) if tab_task_log: click.echo(click.style('task already exists', fg='red')) sys.exit(-1) # 直接使用线程执行 task , 然后返回前端成功状态 click.echo(click.style('task [{}] running, please wait ...'.format(task_id), fg='green')) event_loop.run_until_complete(task_executor.execute(task_detail)) except Exception as e: logger.exception('runtask error, {}'.format(str(e))) click.echo(click.style('创建任务失败, 请检查脚本文件. 错误原因: {}'.format(str(e)), fg='red'))
def build_request(self): tc_ctx_dict = self.vertical_context.tc_context try: # 获取请参 TpBase.build_request(self) # 组装heard # 20190403 edit by jun.hu # 新加支持 http_header 配置字典写法 head_dict = {} tc_data_dict = tc_ctx_dict.tc_detail.data tp_conf_header = self.tp_conf.get('http_header', None) if tp_conf_header: try: # 默认配置支持字典的写法 self.__header = json.loads(tp_conf_header) except Exception as ex: logger.info(str(ex)) # 字典取值发生错误时取逗号隔开的值 params_name_list = self.tp_conf.get('http_header').split( ',') for name in params_name_list: head_dict[name] = tc_data_dict.get(name) self.__header = head_dict if self.tp_conf.get('upload_file_path'): upload_file_path = self.tp_conf.get('upload_file_path') upload_file_path = upload_file_path.split('/')[-1] upload_file = next( filter( lambda a: a.id.endswith(upload_file_path), self. vertical_context.job_context.job_model.attachment), None) assert upload_file is not None, 'file not found: {}'.format( upload_file_path) files = { self.tp_conf.get('upload_file_name'): upload_file.content } self.__file = files # 构造json请求参数 if self.tp_conf.get('json_param_name'): json_param_dict = {} if self.tp_conf.get('req_json_data'): params_name_list = self.tp_conf.get('req_json_data').split( ',') for name in params_name_list: json_param_dict[name] = tc_data_dict.get(name) # json参数放入请参字典 self.req_param[self.tp_conf.get( 'json_param_name')] = json.dumps(json_param_dict) return {'content': self.req_param, 'header': self.__header} except Exception as e: logger.exception('tp->api:build params error', str(e)) raise Exception(str(e))
def handle_flask_error(error): # 打印异常 logger.exception(str(error)) # response 的 json 内容为自定义错误代码和错误信息 response = jsonify({'return_code': COMMON_ERROR, 'message': str(error)}) # response 返回 error 发生时定义的标准错误代码 response.status_code = 500 return response
def build_request(self): try: # 获取请参 TpBase.build_request(self) return self.req_param except Exception as e: logger.exception('tp->active mq:build params error', str(e)) raise Exception('active mq测试,构建参数时错误,测试脚本没有找到或者解析失败')
def execute(self, request): ftp = None try: host_port = request[FtpTestPointEnum.address.key].split(':') if request[FtpTestPointEnum.server_type.key] == 'sftp': sf = paramiko.Transport(host_port[0], int(host_port[1])) sf.connect(username=request[FtpTestPointEnum.username.key], password=request[FtpTestPointEnum.password.key]) sftp = paramiko.SFTPClient.from_transport(sf) # sftp下载需要先新建本地文件 cur_path = os.getcwd() file_path = os.path.join( cur_path, '/download' + request[FtpTestPointEnum.req_data.key]) create_file(file_path) sftp.get(request[FtpTestPointEnum.req_data.key], file_path) # 读取本地文件 file_object = open(file_path, 'rb') try: file_context = file_object.read() finally: file_object.close() resp_dict = {'result_value': file_context} # 删除文件 os.remove(file_path) elif request[FtpTestPointEnum.server_type.key] == 'ftp': ftp = FTP() ftp.connect(host_port[0], int(host_port[1])) ftp.login(request[FtpTestPointEnum.username.key], request[FtpTestPointEnum.password.key]) self.file_download = io.BytesIO() ftp.retrbinary( 'RETR %s' % request[FtpTestPointEnum.req_data.key], self.file_download.write) resp_dict = {'result_value': self.file_download} else: logger.exception('FtpTestPoint execute error, cause: %s', 'server_type配置错误') raise Exception('FtpTestPoint execute error, cause: ' + 'server_type配置错误') self.resp_dict = resp_dict return self.resp_dict, '' except Exception as e: logger.exception('FtpTestPoint execute error, cause: %s', str(e)) raise Exception('FtpTestPoint execute error, cause: ' + str(e)) finally: if ftp is not None: ftp.close()
def build_request(self): try: # 获取请参 self.req_param['zk_url'] = self.tp_conf.get('zk_url') self.req_param['node_path'] = self.tp_conf.get('node_path') self.req_param['node_value'] = self.tp_conf.get('node_value') self.req_param['is_update_timestamp'] = self.tp_conf.get('is_update_timestamp', False) self.req_param['is_delete_node'] = self.tp_conf.get('is_delete_node', False) return self.req_param except Exception as e: logger.exception('zkTestPoint build_params error, cause: %s', str(e)) raise Exception('zkTestPoint build_params error, make sure param is correct')
def execute_move_to_element_click(self, **kwargs): try: exec_element = kwargs['exec_element'] # 滑动的元素 exec_param = kwargs['exec_param'] # 需要根据 exec_param 找到 hover 所在层 hover_element = find_element(self.driver, exec_param) if not hover_element: return False # 向下滑动 ActionChains(self.driver).move_to_element(hover_element).click( exec_element).perform() return True except Exception as ex: logger.exception(ex) return False
def build_request(self): tc_params_dict = self.vertical_context.tc_context self._tc_params_dict = tc_params_dict script_name = self.tp_conf.get( UiTestPointEnum.script_file_path.key, self.tp_conf.get(UiTestPointEnum.script.key)) script_name = script_name.split('/')[-1] xml_script = next( filter(lambda a: a.id.endswith(script_name), self.vertical_context.job_context.job_model.attachment), None) assert xml_script is not None, 'can not fond xml script {}'.format( script_name) xml_script_content_raw = xml_script.content.decode('utf-8') try: xml_script_content, replacement = get_placeholder_value( xml_script_content_raw, self.vertical_context) self.__extend_tp_replacement(replacement) # 获取命令列表 self._command_list = self._get_command_list(xml_script_content) target_url = '' for command in self._command_list: # 请求参数作用于 open 函数的目标 if command['command'] == 'open': target_url = self._get_command_param(command['target']) logger.info("ui 原始请求地址为 :{}".format(target_url)) # 组装页面地址 if UiTestPointEnum.req_data.key in self.tp_conf and self.tp_conf[ UiTestPointEnum.req_data.key]: TpBase.build_request(self) target_url = target_url + splice_url_params( self.req_param) logger.info("ui 请求地址,拼接参数后为 :{}".format(target_url)) flag = self._set_command_param(command['target'], target_url) if not flag: command['target'] = target_url break except Exception as e: logger.exception('ui 测试脚本: %s ,没有找到或者解析失败', str(self.tp_conf)) raise RuntimeError('ui 测试脚本:' + str(self.tp_conf) + ',没有找到或者解析失败') return {'url': target_url}
def build_request(self): try: # 获取请参 # TpBase.build_params(self, tc_ctx_dict) self.req_param['req_data'] = self.tp_conf.get('req_data') self.req_param['socket_address'] = self.tp_conf.get( 'socket_address') self.req_param['charset'] = self.tp_conf.get('charset') return self.req_param except Exception as e: logger.exception('SocketTestPoint build_params error, cause: %s', str(e)) raise Exception( 'SocketTestPoint build_params error, make sure [cache_key, cache_type] is correct' )
def build_request(self): try: # 获取请参 # TpBase.build_params(self, tc_ctx_dict) self.req_param[FtpTestPointEnum.req_data.key] = self.tp_conf.get( FtpTestPointEnum.req_data.key) self.req_param[FtpTestPointEnum.address.key] = self.tp_conf.get( FtpTestPointEnum.address.key) self.req_param[FtpTestPointEnum.username.key] = self.tp_conf.get( FtpTestPointEnum.username.key) self.req_param[FtpTestPointEnum.password.key] = self.tp_conf.get( FtpTestPointEnum.password.key) self.req_param[ FtpTestPointEnum.server_type.key] = self.tp_conf.get( FtpTestPointEnum.server_type.key) return self.req_param except Exception as e: logger.exception('FtpTestPoint build_params error, cause: %s', str(e)) raise Exception( 'FtpTestPoint build_params error, make sure [cache_key, cache_type] is correct' )
def execute(self, request): s = None try: host_port = request['socket_address'].split(':') s = socket.socket() s.settimeout(5) s.connect((host_port[0], int(host_port[1]))) s.sendall(request['req_data'].encode(request['charset'])) recv_str = s.recv(1024) recv_str = recv_str.decode(request['charset']) try: resp_dict = {'result_value': json.loads(recv_str)} except: resp_dict = {'result_value': recv_str} self.resp_dict = resp_dict return self.resp_dict, '' except Exception as e: logger.exception('SocketTestPoint execute error, cause: %s', str(e)) raise Exception('SocketTestPoint execute error, cause: ' + str(e)) finally: if s is not None: s.close()
def execute(self, request): # 开始逐条执行命令, 捕获并输出异常, 有异常即认为该案例失败 semaphore_acquired = False try: logger.info('ui waiting: {}'.format(self._semaphore._value)) semaphore_acquired = self._semaphore.acquire() logger.info('ui executing: {}'.format(self._semaphore._value)) # 加载浏览器驱动 self._load_driver(self.tp_conf[UiTestPointEnum.browser_type.key], request) # 增加命令计数器,进行计数 command_num = 0 for command in self._command_list: command_num += 1 try: execute_result = AgWait( self._driver, 10, ignored_exceptions=Exception).until( lambda x: self.__execute_command( command['command'], self._get_command_param(command['target']), self._get_command_param(command['value']))) # 2019.5.20 修改所有环境 ui 测试都等待时间 # local 环境下,每执行一个指令就等待 1s time.sleep(1) # 当最后一条执行的时候,等待 2s if command_num == len(self._command_list): time.sleep(2) if not execute_result: logger.info('ui 测试命令执行失败: 第' + str(command_num) + '条命令, command=' + command['command'] + ',target=' + command['target'] + ',value=' + command['value']) self._execute_flag = False raise RuntimeError('ui 测试命令执行失败: 第' + str(command_num) + '条命令, command=' + command['command'] + ',target=' + command['target'] + ',value=' + command['value']) except NoSuchElementException as e: logger.exception( '第' + str(command_num) + '条命令, ui 测试命令: %s ,没有找到指定页面元素', str(command)) self._execute_flag = False raise RuntimeError('第' + str(command_num) + '条命令, ui 测试命令:' + str(command) + ',没有找到指定页面元素') except NoAlertPresentException as e: logger.exception( '第' + str(command_num) + '条命令, ui 测试命令: %s ,没有 alert 弹框, 建议脚本中添加等待时间', command['command']) self._execute_flag = False raise RuntimeError('第' + str(command_num) + '条命令, ui 测试命令:' + str(command) + ',没有 alert 弹框, 建议脚本中添加等待时间') except Exception as e: logger.exception( '第' + str(command_num) + '条命令, ui 测试命令: %s ,执行异常', str(command)) self._execute_flag = False raise RuntimeError('第' + str(command_num) + '条命令, ui 测试命令:' + str(command) + ',执行异常') finally: if not self._execute_flag: shot_path = os.path.join(agbot_config.basedir, 'log/screenshot') if not os.path.exists(shot_path): os.makedirs(shot_path) self._driver.save_screenshot( os.path.join( shot_path, '{}_{}_{}_{}.png'.format( self.vertical_context.task_context. task_model.id, self.vertical_context. job_context.job_model.id, self. vertical_context.tc_context.tc_detail.id, self.vertical_context.tc_context. current_tp_context.id))) return {'execute_flag': self._execute_flag}, '' finally: try: # time.sleep(120) # 关闭页面, 释放对象 # 本地环境,出现错误时,不退出 if not self._execute_flag and self.vertical_context.sys_conf_dict[ _root_conf_name]['quit_on_error'] == 'false': pass else: self._driver.quit() finally: if semaphore_acquired: self._semaphore.release() logger.info('ui exit: {}'.format(self._semaphore._value))
def _load_driver(self, driver_type, req_param): # 之后驱动包应打入 docker 镜像, 不应再放置在工程目录下 # 只使用 Chrome 无浏览器模式 # local 环境下使用与 .exe 同一目录下的 /web_driver/chromedriver.exe if driver_type == BrowserType.FIREFOX.value: driver_name = 'geckodriver' option = webdriver.FirefoxOptions() else: # 默认是 chrome option = webdriver.ChromeOptions() driver_name = 'chromedriver' driver_path = None driver_dir = self.vertical_context.sys_conf_dict[_root_conf_name][ 'driver_dir'] for root, dirs, files in os.walk(driver_dir): for file in files: if file.startswith(driver_name): driver_path = os.path.join(root, file) break assert driver_path is not None, 'unsupported browser: {}'.format( driver_type) logger.info('驱动文件路径为: {}'.format(driver_path)) is_silent_mode = self.vertical_context.sys_conf_dict[_root_conf_name][ 'silent_mode'] == 'true' if is_silent_mode: # 静默模式 option.add_argument('--disable-gpu') option.add_argument('--headless') option.add_argument('--no-sandbox') # 使用代理 if self.tp_conf.get(UiTestPointEnum.proxy_server.key): # http://10.153.201.20:8443 proxy_server = self.tp_conf.get(UiTestPointEnum.proxy_server.key) logger.info('ui 测试 添加代理 {}'.format(proxy_server)) try: if driver_type == BrowserType.FIREFOX.value: option.set_preference('network.proxy.type', 1) # IP为你的代理服务器地址:如 ‘127.0.0.0’,字符串类型 ip = ':'.join(proxy_server.split(':')[:-1]) option.set_preference('network.proxy.http', ip) port = int(proxy_server.split(':')[-1]) option.set_preference('network.proxy.http_port', port) else: option.add_argument("--proxy-server=" + proxy_server) except Exception as ex: logger.exception('添加代理错误:{}'.format(ex)) raise Exception(ex) try: # firefox if driver_type == BrowserType.FIREFOX.value: profile = webdriver.FirefoxProfile() # 使用手机模式打开 if self.tp_conf.get(UiTestPointEnum.device_name.key): device_name = self.tp_conf.get( UiTestPointEnum.device_name.key) if device_name.lower().startswith('iphone'): user_agent = ( "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) " "AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16" ) else: user_agent = ( "Firefox 28/Android: Mozilla/5.0 (Android; Mobile; rv:28.0) " "Gecko/24.0 Firefox/28.0") profile.set_preference("general.useragent.override", user_agent) # firefox 禁用缓存 profile.set_preference("browser.cache.disk.enable", False) profile.set_preference("browser.cache.memory.enable", False) profile.set_preference("browser.cache.offline.enable", False) self._driver = webdriver.Firefox(executable_path=driver_path, firefox_profile=profile, firefox_options=option) else: # chrome 限制缓存 option.add_argument('--disable-dev-shm-usage') if self.tp_conf.get(UiTestPointEnum.device_name.key): # 使用手机模式打开 mobileEmulation = { 'deviceName': self.tp_conf.get(UiTestPointEnum.device_name.key) } option.add_experimental_option('mobileEmulation', mobileEmulation) self._driver = webdriver.Chrome(executable_path=driver_path, chrome_options=option) except Exception as ex: logger.exception(ex) raise Exception(ex) window_size = self.vertical_context.sys_conf_dict[_root_conf_name].get( 'window_size') if window_size: # 设定窗口尺寸以使截图范围更大 window_size_tuple = window_size.split(',') self._driver.set_window_size(int(window_size_tuple[0]), int(window_size_tuple[1])) else: self._driver.maximize_window()
def execute(self, request): content = request.get('content') # 请求地址可从数据文件中读取 api_url = self.tp_conf.get('http_url') # 获取 http 请求方法名称 method = self.tp_conf.get('http_method') # 发起接口调用请求并接收响应 try: # 反射得到 http 的方法 exec_func = getattr(requests, method.lower()) # 请求超时时间 try: timeout = int(self.tp_conf.get('timeout', 10)) except Exception as _: timeout = 10 timeout = min(timeout, 120) # 20190315 edit by jun.hu # 根据 conf 中的 content-type 来决定请求数据组织方式 if self.__header.get('Content-Type') == 'application/json': response = exec_func(api_url, json=content, headers=self.__header, files=self.__file, timeout=timeout) else: if method.lower() == 'delete': response = exec_func(api_url, headers=self.__header, timeout=timeout) else: files = self.__file response = exec_func(api_url, content, headers=self.__header, files=files, timeout=timeout) self.__resp_code = str(response.status_code) try: content = response.json() logger.info('[{}, {}, {}] tp->api load as json: {}, {}'.format( self.vertical_context.task_context.task_model.id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id, response.status_code, content)) return content, self.__resp_code except JSONDecodeError as e: logger.info( '[{}, {}, {}] tp->api load as json error: {}, {}'.format( self.vertical_context.task_context.task_model.id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id, response.status_code, response.text)) pass try: content = self.__get_xml_dict(response.text) logger.info('[{}, {}, {}] tp->api load as xml: {}, {}'.format( self.vertical_context.task_context.task_model.id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id, response.status_code, content)) return content, self.__resp_code except Exception as e: logger.info( '[{}, {}, {}] tp->api load as xml error: {}, {}'.format( self.vertical_context.task_context.task_model.id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id, response.status_code, response.text)) pass if isinstance(response.text, str) and self.tp_conf.get( 'resp_default_key', ''): return { self.tp_conf.get('resp_default_key'): response.text }, self.__resp_code return response.text, self.__resp_code except RequestException as e: logger.error('[{}, {}, {}] tp->api连接异常'.format( self.vertical_context.task_context.task_model.id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id)) raise RuntimeError('api连接异常, {}'.format(str(e))) except RuntimeError as e: logger.exception('[{}, {}, {}] tp->api运行时异常, {}'.format( self.vertical_context.task_context.task_model.id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id, str(e))) raise RuntimeError('api运行时异常, {}'.format(str(e)))
def test_status(self): tc_ctx = self.vertical_context.tc_context try: # 配置的期望参数名称 if self.tp_conf.get('expect_data'): self.resolve_expect_data(tc_ctx) params_name_list = self.tp_conf.get('expect_data').split( ',(?=(?:[^\"]*(?:"[^\"]*")?[^\"]*)*$)') # 如果期望判断首个值满足表达式,则认为是表达式 if exp.is_exp(params_name_list[0]): diff_key_set = set() for e in params_name_list: if not self.run_exp(e): diff_key_set.add(e) if not diff_key_set: return TestStatus.PASSED else: logger.info( '[{}, {}, {}] tp->api 断言不通过: {} @ {}'.format( self.vertical_context.task_context.task_model. id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id, params_name_list, self.vertical_context.tc_context)) return TestStatus.NOT_PASSED # 获取数据文件期望返回的状态码 if self.tp_conf.get('expect_resp_code'): if self.__resp_code != self.tp_conf.get('expect_resp_code'): return TestStatus.NOT_PASSED else: return TestStatus.PASSED # 是否需要 urldecode if self.tp_conf.get('resp_urldecode_name'): name_list = self.tp_conf.get('resp_urldecode_name').split(',') response_str = self.__response_urldecode( str(tc_ctx.current_tp_context.response.content), name_list) else: response_str = tc_ctx.current_tp_context.response.content if response_str is not None: # 校验期望值,验证返回码是否与期望返回码保持一致 diff_key_set = set() diff_key_set = self.__expect_dict_check( self.expect_dict, tc_ctx.current_tp_context.response.content, diff_key_set) check_expect_flag = not diff_key_set check_type = self.tp_conf.get('check_type') \ if self.tp_conf.get('check_type') else CheckExpectType.EQUAL.value if CheckExpectType.UNEQUAL.value == check_type: if check_expect_flag: check_expect_flag = TestStatus.NOT_PASSED else: check_expect_flag = TestStatus.PASSED else: if check_expect_flag: check_expect_flag = TestStatus.PASSED else: check_expect_flag = TestStatus.NOT_PASSED logger.info('check result:' + str(check_expect_flag) + ' diff set:' + str(diff_key_set)) else: check_expect_flag = TestStatus.NOT_PASSED logger.info('check result:' + str(check_expect_flag) + ' target response is none.') except Exception as e: logger.exception('[{}, {}, {}] tp->api期望值判断异常: {} @ {}'.format( self.vertical_context.task_context.task_model.id, self.vertical_context.job_context.job_model.id, self.vertical_context.tc_context.tc_detail.id, str(e), self.vertical_context.tc_context)) raise Exception('期望值判断异常: {}: {}'.format(str(e.__class__.__name__), str(e))) return check_expect_flag