2021-05-13 12:17:15 +08:00
|
|
|
from utils.util_log import test_log as log
|
2021-06-09 09:21:35 +08:00
|
|
|
from common import common_type as ct
|
2021-08-12 18:12:10 +08:00
|
|
|
from common import common_func as cf
|
2021-06-10 19:19:49 +08:00
|
|
|
from common.common_type import CheckTasks, Connect_Object_Name
|
2021-06-09 09:21:35 +08:00
|
|
|
# from common.code_mapping import ErrorCode, ErrorMessage
|
2021-08-20 11:00:56 +08:00
|
|
|
from pymilvus import Collection, Partition
|
2021-06-07 12:15:35 +08:00
|
|
|
from utils.api_request import Error
|
2021-06-10 19:19:49 +08:00
|
|
|
import check.param_check as pc
|
2021-05-11 17:59:29 +08:00
|
|
|
|
|
|
|
|
2021-06-05 10:25:34 +08:00
|
|
|
class ResponseChecker:
|
2021-06-07 16:41:35 +08:00
|
|
|
def __init__(self, response, func_name, check_task, check_items, is_succ=True, **kwargs):
|
2021-06-09 15:11:48 +08:00
|
|
|
self.response = response # response of api request
|
|
|
|
self.func_name = func_name # api function name
|
|
|
|
self.check_task = check_task # task to check response of the api request
|
|
|
|
self.check_items = check_items # check items and expectations that to be checked in check task
|
|
|
|
self.succ = is_succ # api responses successful or not
|
2021-06-07 12:15:35 +08:00
|
|
|
|
2021-06-09 15:11:48 +08:00
|
|
|
self.kwargs_dict = {} # not used for now, just for extension
|
2021-05-11 17:59:29 +08:00
|
|
|
for key, value in kwargs.items():
|
2021-06-07 12:15:35 +08:00
|
|
|
self.kwargs_dict[key] = value
|
|
|
|
self.keys = self.kwargs_dict.keys()
|
2021-05-11 17:59:29 +08:00
|
|
|
|
|
|
|
def run(self):
|
2021-06-05 10:25:34 +08:00
|
|
|
"""
|
|
|
|
Method: start response checking for milvus API call
|
|
|
|
"""
|
|
|
|
result = True
|
2021-06-07 12:15:35 +08:00
|
|
|
if self.check_task is None:
|
2021-07-15 15:03:54 +08:00
|
|
|
# Interface normal return check
|
2021-06-07 12:15:35 +08:00
|
|
|
result = self.assert_succ(self.succ, True)
|
2021-05-11 17:59:29 +08:00
|
|
|
|
2021-06-07 12:15:35 +08:00
|
|
|
elif self.check_task == CheckTasks.err_res:
|
2021-07-15 15:03:54 +08:00
|
|
|
# Interface return error code and error message check
|
2021-06-07 16:13:36 +08:00
|
|
|
result = self.assert_exception(self.response, self.succ, self.check_items)
|
2021-06-04 09:35:34 +08:00
|
|
|
|
2021-08-18 16:06:10 +08:00
|
|
|
elif self.check_task == CheckTasks.check_nothing:
|
|
|
|
return self.succ
|
|
|
|
|
2021-06-18 16:26:07 +08:00
|
|
|
elif self.check_task == CheckTasks.ccr:
|
2021-07-15 15:03:54 +08:00
|
|
|
# Connection interface response check
|
2021-06-10 19:19:49 +08:00
|
|
|
result = self.check_value_equal(self.response, self.func_name, self.check_items)
|
2021-06-04 09:35:34 +08:00
|
|
|
|
2021-06-07 12:15:35 +08:00
|
|
|
elif self.check_task == CheckTasks.check_collection_property:
|
2021-07-15 15:03:54 +08:00
|
|
|
# Collection interface response check
|
2021-06-07 16:41:35 +08:00
|
|
|
result = self.check_collection_property(self.response, self.func_name, self.check_items)
|
2021-06-04 09:35:34 +08:00
|
|
|
|
2021-06-07 12:15:35 +08:00
|
|
|
elif self.check_task == CheckTasks.check_partition_property:
|
2021-07-15 15:03:54 +08:00
|
|
|
# Partition interface response check
|
2021-06-07 16:41:35 +08:00
|
|
|
result = self.check_partition_property(self.response, self.func_name, self.check_items)
|
2021-06-04 09:35:34 +08:00
|
|
|
|
2021-06-10 11:46:49 +08:00
|
|
|
elif self.check_task == CheckTasks.check_search_results:
|
2021-07-15 15:03:54 +08:00
|
|
|
# Search interface of collection and partition that response check
|
2021-07-02 09:48:12 +08:00
|
|
|
result = self.check_search_results(self.response, self.func_name, self.check_items)
|
2021-06-10 11:46:49 +08:00
|
|
|
|
2021-06-24 13:42:09 +08:00
|
|
|
elif self.check_task == CheckTasks.check_query_results:
|
2021-07-15 15:03:54 +08:00
|
|
|
# Query interface of collection and partition that response check
|
2021-06-24 13:42:09 +08:00
|
|
|
result = self.check_query_results(self.response, self.func_name, self.check_items)
|
|
|
|
|
2021-08-12 18:12:10 +08:00
|
|
|
elif self.check_task == CheckTasks.check_distance:
|
|
|
|
# Calculate distance interface that response check
|
|
|
|
result = self.check_distance(self.response, self.func_name, self.check_items)
|
|
|
|
|
2021-06-05 10:25:34 +08:00
|
|
|
# Add check_items here if something new need verify
|
2021-06-01 18:49:31 +08:00
|
|
|
|
2021-06-05 10:25:34 +08:00
|
|
|
return result
|
2021-05-11 17:59:29 +08:00
|
|
|
|
2021-06-04 09:35:34 +08:00
|
|
|
@staticmethod
|
2021-06-07 12:15:35 +08:00
|
|
|
def assert_succ(actual, expect):
|
|
|
|
assert actual is expect
|
|
|
|
return True
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def assert_exception(res, actual=True, error_dict=None):
|
|
|
|
assert actual is False
|
|
|
|
assert len(error_dict) > 0
|
|
|
|
if isinstance(res, Error):
|
2021-06-09 09:21:35 +08:00
|
|
|
error_code = error_dict[ct.err_code]
|
2021-06-29 10:36:16 +08:00
|
|
|
assert res.code == error_code or error_dict[ct.err_msg] in res.message
|
2021-06-07 12:15:35 +08:00
|
|
|
else:
|
|
|
|
log.error("[CheckFunc] Response of API is not an error: %s" % str(res))
|
|
|
|
assert False
|
2021-06-04 09:35:34 +08:00
|
|
|
return True
|
|
|
|
|
2021-05-11 17:59:29 +08:00
|
|
|
@staticmethod
|
2021-06-10 19:19:49 +08:00
|
|
|
def check_value_equal(res, func_name, params):
|
|
|
|
""" check response of connection interface that result is normal """
|
2021-05-14 09:27:41 +08:00
|
|
|
|
|
|
|
if func_name == "list_connections":
|
2021-06-10 19:19:49 +08:00
|
|
|
if not isinstance(res, list):
|
|
|
|
log.error("[CheckFunc] Response of list_connections is not a list: %s" % str(res))
|
|
|
|
assert False
|
|
|
|
|
2021-06-18 16:26:07 +08:00
|
|
|
list_content = params.get(ct.list_content, None)
|
2021-06-10 19:19:49 +08:00
|
|
|
if not isinstance(list_content, list):
|
|
|
|
log.error("[CheckFunc] Check param of list_content is not a list: %s" % str(list_content))
|
2021-05-14 09:27:41 +08:00
|
|
|
assert False
|
|
|
|
|
2021-06-10 19:19:49 +08:00
|
|
|
new_res = pc.get_connect_object_name(res)
|
|
|
|
assert pc.list_equal_check(new_res, list_content)
|
|
|
|
|
|
|
|
if func_name == "get_connection_addr":
|
2021-06-18 16:26:07 +08:00
|
|
|
dict_content = params.get(ct.dict_content, None)
|
2021-06-10 19:19:49 +08:00
|
|
|
assert pc.dict_equal_check(res, dict_content)
|
|
|
|
|
|
|
|
if func_name == "connect":
|
|
|
|
class_obj = Connect_Object_Name
|
|
|
|
res_obj = type(res).__name__
|
|
|
|
assert res_obj == class_obj
|
|
|
|
|
|
|
|
if func_name == "get_connection":
|
2021-06-18 16:26:07 +08:00
|
|
|
value_content = params.get(ct.value_content, None)
|
2021-06-10 19:19:49 +08:00
|
|
|
res_obj = type(res).__name__ if res is not None else None
|
|
|
|
assert res_obj == value_content
|
2021-05-14 09:27:41 +08:00
|
|
|
|
|
|
|
return True
|
|
|
|
|
2021-05-17 10:14:35 +08:00
|
|
|
@staticmethod
|
2021-06-25 10:38:11 +08:00
|
|
|
def check_collection_property(res, func_name, check_items):
|
2021-10-02 17:05:56 +08:00
|
|
|
"""
|
|
|
|
According to the check_items to check collection properties of res, which return from func_name
|
|
|
|
:param res: actual response of init collection
|
|
|
|
:type res: Collection
|
|
|
|
|
|
|
|
:param func_name: init collection API
|
|
|
|
:type func_name: str
|
|
|
|
|
|
|
|
:param check_items: which items expected to be checked, including name, schema, num_entities, primary
|
|
|
|
:type check_items: dict, {check_key: expected_value}
|
|
|
|
"""
|
2021-06-09 15:11:48 +08:00
|
|
|
exp_func_name = "init_collection"
|
2021-06-17 17:27:59 +08:00
|
|
|
exp_func_name_2 = "construct_from_dataframe"
|
|
|
|
if func_name != exp_func_name and func_name != exp_func_name_2:
|
2021-05-17 10:14:35 +08:00
|
|
|
log.warning("The function name is {} rather than {}".format(func_name, exp_func_name))
|
2021-06-25 10:38:11 +08:00
|
|
|
if isinstance(res, Collection):
|
|
|
|
collection = res
|
|
|
|
elif isinstance(res, tuple):
|
|
|
|
collection = res[0]
|
|
|
|
log.debug(collection.schema)
|
|
|
|
else:
|
2021-05-17 10:14:35 +08:00
|
|
|
raise Exception("The result to check isn't collection type object")
|
2021-06-08 10:35:35 +08:00
|
|
|
if len(check_items) == 0:
|
|
|
|
raise Exception("No expect values found in the check task")
|
2021-06-22 17:12:06 +08:00
|
|
|
if check_items.get("name", None):
|
|
|
|
assert collection.name == check_items.get("name")
|
|
|
|
if check_items.get("schema", None):
|
|
|
|
assert collection.schema == check_items.get("schema")
|
|
|
|
if check_items.get("num_entities", None):
|
|
|
|
if check_items.get("num_entities") == 0:
|
|
|
|
assert collection.is_empty
|
|
|
|
assert collection.num_entities == check_items.get("num_entities")
|
|
|
|
if check_items.get("primary", None):
|
|
|
|
assert collection.primary_field.name == check_items.get("primary")
|
2021-05-27 10:26:35 +08:00
|
|
|
return True
|
|
|
|
|
|
|
|
@staticmethod
|
2021-06-07 16:41:35 +08:00
|
|
|
def check_partition_property(partition, func_name, check_items):
|
2021-06-17 16:15:58 +08:00
|
|
|
exp_func_name = "init_partition"
|
2021-05-27 10:26:35 +08:00
|
|
|
if func_name != exp_func_name:
|
|
|
|
log.warning("The function name is {} rather than {}".format(func_name, exp_func_name))
|
|
|
|
if not isinstance(partition, Partition):
|
2021-06-07 12:15:35 +08:00
|
|
|
raise Exception("The result to check isn't partition type object")
|
2021-06-07 16:41:35 +08:00
|
|
|
if len(check_items) == 0:
|
2021-06-07 12:15:35 +08:00
|
|
|
raise Exception("No expect values found in the check task")
|
2021-06-08 10:35:35 +08:00
|
|
|
if check_items.get("name", None):
|
2021-06-07 16:41:35 +08:00
|
|
|
assert partition.name == check_items["name"]
|
2021-06-08 10:35:35 +08:00
|
|
|
if check_items.get("description", None):
|
2021-06-07 16:41:35 +08:00
|
|
|
assert partition.description == check_items["description"]
|
2021-06-08 10:35:35 +08:00
|
|
|
if check_items.get("is_empty", None):
|
2021-06-07 16:41:35 +08:00
|
|
|
assert partition.is_empty == check_items["is_empty"]
|
2021-06-08 10:35:35 +08:00
|
|
|
if check_items.get("num_entities", None):
|
2021-06-07 16:41:35 +08:00
|
|
|
assert partition.num_entities == check_items["num_entities"]
|
2021-05-27 10:26:35 +08:00
|
|
|
return True
|
2021-06-10 11:46:49 +08:00
|
|
|
|
|
|
|
@staticmethod
|
2021-07-02 09:48:12 +08:00
|
|
|
def check_search_results(search_res, func_name, check_items):
|
2021-06-10 11:46:49 +08:00
|
|
|
"""
|
|
|
|
target: check the search results
|
|
|
|
method: 1. check the query number
|
2021-07-07 17:50:00 +08:00
|
|
|
2. check the limit(topK) and ids
|
2021-06-10 11:46:49 +08:00
|
|
|
3. check the distance
|
|
|
|
expected: check the search is ok
|
|
|
|
"""
|
|
|
|
log.info("search_results_check: checking the searching results")
|
2021-07-02 09:48:12 +08:00
|
|
|
if func_name != 'search':
|
|
|
|
log.warning("The function name is {} rather than {}".format(func_name, "search"))
|
|
|
|
if len(check_items) == 0:
|
|
|
|
raise Exception("No expect values found in the check task")
|
2021-07-20 22:29:53 +08:00
|
|
|
if check_items.get("_async", None):
|
|
|
|
if check_items["_async"]:
|
|
|
|
search_res.done()
|
|
|
|
search_res = search_res.result()
|
2021-06-10 11:46:49 +08:00
|
|
|
if len(search_res) != check_items["nq"]:
|
|
|
|
log.error("search_results_check: Numbers of query searched (%d) "
|
|
|
|
"is not equal with expected (%d)"
|
|
|
|
% (len(search_res), check_items["nq"]))
|
|
|
|
assert len(search_res) == check_items["nq"]
|
|
|
|
else:
|
|
|
|
log.info("search_results_check: Numbers of query searched is correct")
|
|
|
|
for hits in search_res:
|
2021-07-02 09:48:12 +08:00
|
|
|
if (len(hits) != check_items["limit"]) \
|
|
|
|
or (len(hits.ids) != check_items["limit"]):
|
2021-06-10 11:46:49 +08:00
|
|
|
log.error("search_results_check: limit(topK) searched (%d) "
|
|
|
|
"is not equal with expected (%d)"
|
|
|
|
% (len(hits), check_items["limit"]))
|
|
|
|
assert len(hits) == check_items["limit"]
|
|
|
|
assert len(hits.ids) == check_items["limit"]
|
|
|
|
else:
|
2021-07-07 17:50:00 +08:00
|
|
|
ids_match = pc.list_contain_check(list(hits.ids),
|
|
|
|
list(check_items["ids"]))
|
2021-07-28 19:35:22 +08:00
|
|
|
if not ids_match:
|
2021-07-07 17:50:00 +08:00
|
|
|
log.error("search_results_check: ids searched not match")
|
|
|
|
assert ids_match
|
2021-07-28 19:35:22 +08:00
|
|
|
log.info("search_results_check: limit (topK) and "
|
|
|
|
"ids searched for %d queries are correct" % len(search_res))
|
2021-06-18 19:12:09 +08:00
|
|
|
return True
|
2021-06-24 13:42:09 +08:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def check_query_results(query_res, func_name, check_items):
|
|
|
|
if func_name != 'query':
|
|
|
|
log.warning("The function name is {} rather than {}".format(func_name, "query"))
|
|
|
|
if not isinstance(query_res, list):
|
|
|
|
raise Exception("The query result to check isn't list type object")
|
|
|
|
if len(check_items) == 0:
|
|
|
|
raise Exception("No expect values found in the check task")
|
|
|
|
exp_res = check_items.get("exp_res", None)
|
2021-07-20 16:06:08 +08:00
|
|
|
with_vec = check_items.get("with_vec", False)
|
2021-06-24 13:42:09 +08:00
|
|
|
if exp_res and isinstance(query_res, list):
|
2021-07-20 16:06:08 +08:00
|
|
|
assert pc.equal_entities_list(exp=exp_res, actual=query_res, with_vec=with_vec)
|
2021-06-26 19:48:13 +08:00
|
|
|
# assert len(exp_res) == len(query_res)
|
|
|
|
# for i in range(len(exp_res)):
|
|
|
|
# assert_entity_equal(exp=exp_res[i], actual=query_res[i])
|
2021-06-28 15:54:11 +08:00
|
|
|
|
2021-08-12 18:12:10 +08:00
|
|
|
@staticmethod
|
|
|
|
def check_distance(distance_res, func_name, check_items):
|
|
|
|
if func_name != 'calc_distance':
|
|
|
|
log.warning("The function name is {} rather than {}".format(func_name, "calc_distance"))
|
|
|
|
if not isinstance(distance_res, list):
|
|
|
|
raise Exception("The distance result to check isn't list type object")
|
|
|
|
if len(check_items) == 0:
|
|
|
|
raise Exception("No expect values found in the check task")
|
|
|
|
vectors_l = check_items["vectors_l"]
|
|
|
|
vectors_r = check_items["vectors_r"]
|
|
|
|
metric = check_items.get("metric", "L2")
|
|
|
|
sqrt = check_items.get("sqrt", False)
|
|
|
|
cf.compare_distance_2d_vector(vectors_l, vectors_r,
|
|
|
|
distance_res,
|
|
|
|
metric, sqrt)
|
|
|
|
|
|
|
|
return True
|