修复bug: 修复定时任务多个项目共同执行时获取和更新LastResultPool、CookiePool、VariablePool对象存在冲突的问题

This commit is contained in:
azhengzz 2021-03-11 20:22:19 +08:00
parent 3c7667e125
commit a10d3c0cf4
3 changed files with 280 additions and 96 deletions

View File

@ -3,64 +3,158 @@
# 记录最近一次测试结果
from copy import deepcopy
from flask import session
from app.extensions import session_id_manager
from app.cores.dictionaries import DISPATCHER_TRIGGER_TYPE
class LastResult:
"""
LastResultPool = {
session_id1: {
'result': None, # 上次执行结果
'request_headers': None, # 请求头
'request_body': None, # 请求包
'response_headers': None, # 应答头
'response_body': None, # 应答包
...
},
session_id2: {
'result': None, # 上次执行结果
'request_headers': None, # 请求头
'request_body': None, # 请求包
'response_headers': None, # 应答头
'response_body': None, # 应答包
...
},
...
}
ScheduleLastResultPool = {
project_id1: {
'result': None, # 上次执行结果
'request_headers': None, # 请求头
'request_body': None, # 请求包
'response_headers': None, # 应答头
'response_body': None, # 应答包
...
},
project_id2: {
'result': None, # 上次执行结果
'request_headers': None, # 请求头
'request_body': None, # 请求包
'response_headers': None, # 应答头
'response_body': None, # 应答包
...
},
...
}
"""
# 上次执行结果
LastResultPool = {}
# 定时任务项目-上次执行结果
ScheduleLastResultPool = {}
@classmethod
def get_last_result(cls) -> dict:
session_id = session_id_manager.get_session_id()
return cls.LastResultPool.get(session_id)
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
return cls.ScheduleLastResultPool.get(project_id)
else:
session_id = session_id_manager.get_session_id()
return cls.LastResultPool.get(session_id)
@classmethod
def update_last_result(cls, result, request_headers, request_body, response_headers, response_body):
"""更新所有字段"""
cls._check_current_session_id_last_result_exist()
session_id = session_id_manager.get_session_id()
cls.LastResultPool.get(session_id).update({
'result': deepcopy(result),
'request_headers': deepcopy(request_headers),
'request_body': deepcopy(request_body),
'response_headers': deepcopy(response_headers),
'response_body': deepcopy(response_body),
})
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
cls.ScheduleLastResultPool.get(project_id).update({
'result': deepcopy(result),
'request_headers': deepcopy(request_headers),
'request_body': deepcopy(request_body),
'response_headers': deepcopy(response_headers),
'response_body': deepcopy(response_body),
})
else:
session_id = session_id_manager.get_session_id()
cls.LastResultPool.get(session_id).update({
'result': deepcopy(result),
'request_headers': deepcopy(request_headers),
'request_body': deepcopy(request_body),
'response_headers': deepcopy(response_headers),
'response_body': deepcopy(response_body),
})
@classmethod
def update_request_and_response_to_last_result(cls, request_headers, request_body, response_headers, response_body):
"""更新请求和应答数据"""
cls._check_current_session_id_last_result_exist()
session_id = session_id_manager.get_session_id()
cls.LastResultPool.get(session_id).update({
'request_headers': deepcopy(request_headers),
'request_body': deepcopy(request_body),
'response_headers': deepcopy(response_headers),
'response_body': deepcopy(response_body),
})
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
cls.ScheduleLastResultPool.get(project_id).update({
'request_headers': deepcopy(request_headers),
'request_body': deepcopy(request_body),
'response_headers': deepcopy(response_headers),
'response_body': deepcopy(response_body),
})
else:
session_id = session_id_manager.get_session_id()
cls.LastResultPool.get(session_id).update({
'request_headers': deepcopy(request_headers),
'request_body': deepcopy(request_body),
'response_headers': deepcopy(response_headers),
'response_body': deepcopy(response_body),
})
@classmethod
def update_result_to_last_result(cls, result):
"""更新最近一次执行结果"""
cls._check_current_session_id_last_result_exist()
session_id = session_id_manager.get_session_id()
cls.LastResultPool.get(session_id).update({
'result': deepcopy(result),
})
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
cls.ScheduleLastResultPool.get(project_id).update({
'result': deepcopy(result),
})
else:
session_id = session_id_manager.get_session_id()
cls.LastResultPool.get(session_id).update({
'result': deepcopy(result),
})
@classmethod
def _check_current_session_id_last_result_exist(cls):
"""检查当前session_id是否在LastResultPool中如果没有则新增一个空字典"""
session_id = session_id_manager.get_session_id()
if session_id not in cls.LastResultPool:
cls.LastResultPool.update({
session_id: {
'result': None, # 上次执行结果
'request_headers': None, # 请求头
'request_body': None, # 请求包
'response_headers': None, # 应答头
'response_body': None, # 应答包
}
})
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id not in cls.ScheduleLastResultPool:
cls.ScheduleLastResultPool.update({
project_id: {
'result': None, # 上次执行结果
'request_headers': None, # 请求头
'request_body': None, # 请求包
'response_headers': None, # 应答头
'response_body': None, # 应答包
}
})
else:
session_id = session_id_manager.get_session_id()
if session_id not in cls.LastResultPool:
cls.LastResultPool.update({
session_id: {
'result': None, # 上次执行结果
'request_headers': None, # 请求头
'request_body': None, # 请求包
'response_headers': None, # 应答头
'response_body': None, # 应答包
}
})

View File

@ -1,23 +1,46 @@
# coding=utf-8
from flask import request
from flask import request, session
from requests.cookies import RequestsCookieJar
from datetime import timedelta, datetime
import uuid
from typing import Optional, List, Mapping
from app.cores.dictionaries import DISPATCHER_TYPE
from app.cores.dictionaries import DISPATCHER_TYPE, DISPATCHER_TRIGGER_TYPE
# requests库会在发起请求时根据请求服务器的domain选择合适的cookie作为请求头中的cookie数据发出
# 可参考代码 requests.models.prepare_cookies()
class HTTPCookieManager:
"""管理自动化测试中请求头Cookie数据"""
"""
管理自动化测试中请求头Cookie数据
CookiePool = {
http_cookie_manager_id1: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
},
http_cookie_manager_id1: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
},
...
}
ScheduleCookiePool = {
project_id1: RequestsCookieJar(),
project_id2: RequestsCookieJar(),
...
}
"""
# cookie池不同的会话放到不同的字典中key为区分会话唯一编号放入cookie中
CookiePool = {}
# 对于定时构建的项目使用另一个Cookie池保存key为项目id
ScheduleCookiePool = {}
def __init__(self, app=None):
if app is not None:
self.init_app(app)
@ -26,65 +49,86 @@ class HTTPCookieManager:
app.http_cookie_manager = self
app.after_request(self._update_cookie)
def update_cookie_pool(self, http_cookie_manager_id: str = None, rcj: RequestsCookieJar = None, type: str = None):
def update_cookie_pool(self, rcj: RequestsCookieJar = None, type: str = None):
"""
:param http_cookie_manager_id: 标识当前会话cookie编号
:param rcj: RequestsCookieJar对象一般由应答头获得 response.cookies
:param type: 需要更新到的cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
:return: None
"""
if http_cookie_manager_id is None:
try:
http_cookie_manager_id = request.cookies.get(HTTPCookieManagerConfig.COOKIE_NAME)
except KeyError:
raise KeyError('在请求上下文cookies中未找到名为%s的cookie' % HTTPCookieManagerConfig.COOKIE_NAME)
if rcj is None or type is None:
self._add_empty_to_cookie_pool(http_cookie_manager_id)
# 如果cookie池没有的话先创建默认值
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
if project_id not in __class__.ScheduleCookiePool:
self._add_empty_to_cookie_pool()
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
if http_cookie_manager_id not in __class__.CookiePool:
self._add_empty_to_cookie_pool(http_cookie_manager_id)
if len(rcj) == 0: # 空的RequestsCookieJar
return
if type in [DISPATCHER_TYPE.DEBUG, DISPATCHER_TYPE.BUILD]:
self._add_empty_to_cookie_pool()
# 如果入参rcj为空的RequestsCookieJar则不更新直接返回
if len(rcj) == 0:
return
# 根据type和dispatcher_trigger_type进行cookie更新
if type in [DISPATCHER_TYPE.DEBUG, DISPATCHER_TYPE.BUILD]:
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
old_rcj = __class__.ScheduleCookiePool[project_id]
if old_rcj is None:
__class__.ScheduleCookiePool[project_id] = rcj
else:
self._update_request_cookie_jar(old_rcj=old_rcj, new_rcj=rcj)
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
old_rcj = __class__.CookiePool[http_cookie_manager_id][type]
if old_rcj is None:
__class__.CookiePool[http_cookie_manager_id][type] = rcj
else:
self._update_request_cookie_jar(old_rcj=old_rcj, new_rcj=rcj)
else:
raise ValueError('不支持传入type值为%s只支持type=single_case 或 type=build_case' % type)
else:
raise ValueError('不支持传入type值为%s只支持type=single_case 或 type=build_case' % type)
def _update_request_cookie_jar(self, old_rcj: RequestsCookieJar, new_rcj: RequestsCookieJar):
"""将新的rcj更新到老的rcj上"""
old_rcj.update(other=new_rcj)
def _add_empty_to_cookie_pool(self, http_cookie_manager_id: str):
def _add_empty_to_cookie_pool(self):
"""
为当前会话在cookie pool中添加一个新的默认值为RequestsCookieJar()
:param http_cookie_manager_id: 标识当前会话cookie编号
:return: None
"""
__class__.CookiePool.update({
http_cookie_manager_id: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
}
})
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
__class__.ScheduleCookiePool.update({
project_id: RequestsCookieJar(),
})
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
__class__.CookiePool.update({
http_cookie_manager_id: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
}
})
def get_request_cookie_jar(self, type: str) -> Optional[RequestsCookieJar]:
"""
获取当前会话中指定类型的cookie数据
:param type: cookie类型 single_case或build_case
:param type: cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
:return: RequestsCookieJar
"""
try:
http_cookie_manager_id = request.cookies.get(HTTPCookieManagerConfig.COOKIE_NAME)
except KeyError:
raise KeyError('在请求上下文cookies中未找到名为%s的cookie' % HTTPCookieManagerConfig.COOKIE_NAME)
if http_cookie_manager_id not in __class__.CookiePool:
return
return __class__.CookiePool[http_cookie_manager_id][type]
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
if project_id not in __class__.ScheduleCookiePool:
return
return __class__.ScheduleCookiePool[project_id]
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
if http_cookie_manager_id not in __class__.CookiePool:
return
return __class__.CookiePool[http_cookie_manager_id][type]
def clear_and_reset(self, rcj: RequestsCookieJar, cookies: List[Mapping]):
"""
@ -120,7 +164,6 @@ class HTTPCookieManager:
path='/',
secure=HTTPCookieManagerConfig.COOKIE_SECURE,
httponly=HTTPCookieManagerConfig.COOKIE_HTTPONLY)
self._add_empty_to_cookie_pool(uuid_)
else:
pass
return response
@ -132,15 +175,24 @@ class HTTPCookieManager:
:return:
"""
if self.get_request_cookie_jar(type=type) is not None:
try:
http_cookie_manager_id = request.cookies.get(HTTPCookieManagerConfig.COOKIE_NAME)
except KeyError:
raise KeyError('在请求上下文cookies中未找到名为%s的cookie' % HTTPCookieManagerConfig.COOKIE_NAME)
if type in [DISPATCHER_TYPE.DEBUG, DISPATCHER_TYPE.BUILD]:
__class__.CookiePool[http_cookie_manager_id][type] = RequestsCookieJar()
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
__class__.ScheduleCookiePool[project_id] = RequestsCookieJar()
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
__class__.CookiePool[http_cookie_manager_id][type] = RequestsCookieJar()
else:
raise ValueError('不支持传入type值为%s只支持type=single_case 或 type=build_case' % type)
def _get_http_cookie_manager_id(self):
"""获取请求上下文中的http_cookie_manager_id"""
try:
return request.cookies.get(HTTPCookieManagerConfig.COOKIE_NAME)
except KeyError:
raise KeyError('在请求上下文cookies中未找到名为%s的cookie' % HTTPCookieManagerConfig.COOKIE_NAME)
class HTTPCookieManagerConfig:
# 标识HTTPCookieManager在当前浏览器客户端对应id的cookie

View File

@ -1,8 +1,10 @@
# coding=utf-8
from typing import Dict
from flask import session
from app.extensions import session_id_manager
from app.cores.dictionaries import DISPATCHER_TRIGGER_TYPE
class Variable:
@ -21,12 +23,28 @@ class Variable:
},
session_id2:{
...
}
},
...
}
ScheduleVariablePool = {
project_id: {
key: value,
key1: value1,
...
},
project_id2: {
...
},
...
}
"""
# 变量池
VariablePool = {}
# 定时任务变量池
ScheduleVariablePool = {}
@classmethod
def get_project_variable(cls, project_id):
"""
@ -36,9 +54,14 @@ class Variable:
:return: 项目级所有变量
:rtype: Dict
"""
session_id = session_id_manager.get_session_id()
cls._set_default_project_variable(project_id=project_id)
return cls.VariablePool.get(session_id).get(project_id)
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
return cls.ScheduleVariablePool.get(project_id)
else:
session_id = session_id_manager.get_session_id()
return cls.VariablePool.get(session_id).get(project_id)
@classmethod
def set_project_variable(cls, project_id, key, value):
@ -51,27 +74,42 @@ class Variable:
:param value: 变量值
:type value: str
"""
session_id = session_id_manager.get_session_id()
cls._set_default_project_variable(project_id=project_id)
cls.VariablePool.get(session_id).get(project_id).update({
key: value
})
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
cls.ScheduleVariablePool.get(project_id).update({
key: value
})
else:
session_id = session_id_manager.get_session_id()
cls.VariablePool.get(session_id).get(project_id).update({
key: value
})
@classmethod
def _set_default_project_variable(cls, project_id):
"""当VariablePool为空时为其设置默认值避免取出值为None的项目级变量池"""
session_id = session_id_manager.get_session_id()
session_var = cls.VariablePool.get(session_id)
if session_var is None:
cls.VariablePool.update({
session_id: {
project_id: {}
}
})
return
project_var = cls.VariablePool.get(session_id).get(project_id)
if project_var is None:
cls.VariablePool.get(session_id).update({
project_id: {}
})
return
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
project_var = cls.ScheduleVariablePool.get(project_id)
if project_var is None:
cls.ScheduleVariablePool.update({
project_id: {}
})
else:
session_id = session_id_manager.get_session_id()
session_var = cls.VariablePool.get(session_id)
if session_var is None:
cls.VariablePool.update({
session_id: {
project_id: {}
}
})
else:
project_var = cls.VariablePool.get(session_id).get(project_id)
if project_var is None:
cls.VariablePool.get(session_id).update({
project_id: {}
})