diff --git a/op_robot_tests/tests_files/base_keywords.robot b/op_robot_tests/tests_files/base_keywords.robot index b1c714d0d..31f380098 100644 --- a/op_robot_tests/tests_files/base_keywords.robot +++ b/op_robot_tests/tests_files/base_keywords.robot @@ -71,7 +71,7 @@ Resource resource.robot Звірити відображення поля ${field} документа ${doc_id} із ${left} для користувача ${username} ${right}= Run As ${username} Отримати інформацію із документа ${TENDER['TENDER_UAID']} ${doc_id} ${field} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} Звірити відображення поля ${field} тендера для усіх користувачів @@ -90,7 +90,7 @@ Resource resource.robot Звірити відображення вмісту документа ${doc_id} із ${left} для користувача ${username} ${file_name}= Run as ${username} Отримати документ ${TENDER['TENDER_UAID']} ${doc_id} ${right}= Get File ${OUTPUT_DIR}${/}${file_name} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} Звірити відображення дати ${date} тендера для усіх користувачів @@ -536,13 +536,13 @@ Resource resource.robot Звірити відображення поля ${field} документа ${doc_id} до скарги ${complaintID} з ${left} для користувача ${username} ${right}= Run As ${username} Отримати інформацію із документа до скарги ${TENDER['TENDER_UAID']} ${complaintID} ${doc_id} ${field} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} Звірити відображення вмісту документа ${doc_id} до скарги ${complaintID} з ${left} для користувача ${username} ${file_name}= Run as ${username} Отримати документ до скарги ${TENDER['TENDER_UAID']} ${complaintID} ${doc_id} ${right}= Get File ${OUTPUT_DIR}${/}${file_name} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} ############################################################################################## # BIDDING diff --git a/op_robot_tests/tests_files/brokers/openprocurement_client.robot b/op_robot_tests/tests_files/brokers/openprocurement_client.robot index 6f5f10a93..c8f2e293a 100644 --- a/op_robot_tests/tests_files/brokers/openprocurement_client.robot +++ b/op_robot_tests/tests_files/brokers/openprocurement_client.robot @@ -191,7 +191,15 @@ Library openprocurement_client_helper.py ... Remove From Dictionary ${tender.data} enquiryPeriod ${tender}= set_access_key ${tender} ${USERS.users['${username}'].access_token} ${tender}= Call Method ${USERS.users['${username}'].client} patch_tender ${tender} - Порівняти об'єкти ${prev_value} ${USERS.users['${username}'].tender_data['${fieldname}']} + # The two values should differ. If they are equal, then probably the server refused to, + # or failed to modify the value of a field, so this keyword should instantly fail. + ${new_value}= Get From Object ${USERS.users['${username}'].tender_data.data} ${fieldname} + Compare Objects + ... ${prev_value} + ... ${new_value} + ... msg=Failed to modify "${fieldname}" + ... values=${False} + ... inequal=${True} Set_To_Object ${USERS.users['${username}'].tender_data} ${fieldname} ${fieldvalue} ############################################################################## diff --git a/op_robot_tests/tests_files/cancelation.robot b/op_robot_tests/tests_files/cancelation.robot index 389fc1557..4727aa56a 100644 --- a/op_robot_tests/tests_files/cancelation.robot +++ b/op_robot_tests/tests_files/cancelation.robot @@ -167,10 +167,10 @@ Suite Teardown Test Suite Teardown Звірити відображення поля ${field} документа до скасування ${doc_id} із ${left} для користувача ${username} ${right}= Run As ${username} Отримати інформацію із документа ${TENDER['TENDER_UAID']} ${doc_id} ${field} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} Звірити відображення вмісту документа до скасування ${doc_id} з ${left} для користувача ${username} ${file_name}= Run as ${username} Отримати документ до скасування ${TENDER['TENDER_UAID']} ${doc_id} ${right}= Get File ${OUTPUT_DIR}${/}${file_name} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} diff --git a/op_robot_tests/tests_files/keywords.robot b/op_robot_tests/tests_files/keywords.robot index 37823812c..563a5b464 100644 --- a/op_robot_tests/tests_files/keywords.robot +++ b/op_robot_tests/tests_files/keywords.robot @@ -175,12 +175,12 @@ Get Broker Property By Username Run Keyword And Ignore Error Set To Dictionary ${artifact} provider_bid_id=${USERS.users['${provider}'].bid_id} Run Keyword And Ignore Error Set To Dictionary ${artifact} provider1_bid_id=${USERS.users['${provider1}'].bid_id} Log ${artifact} - log_object_data ${artifact} file_name=artifact update=${True} artifact=${True} + log_object_data ${artifact} file_name=artifact update=${True} Завантажити дані про тендер - ${file_path}= Get Variable Value ${ARTIFACT_FILE} artifact.yaml - ${ARTIFACT}= load_data_from ${file_path} + ${file_path}= Get Variable Value ${ARTIFACT_FILE} artifact + ${ARTIFACT}= load_artifact ${file_path} Run Keyword And Ignore Error Set To Dictionary ${USERS.users['${tender_owner}']} access_token=${ARTIFACT.access_token} ${TENDER}= Create Dictionary TENDER_UAID=${ARTIFACT.tender_uaid} LAST_MODIFICATION_DATE=${ARTIFACT.last_modification_date} LOT_ID=${Empty} ${MODE}= Get Variable Value ${MODE} ${ARTIFACT.mode} @@ -191,7 +191,7 @@ Get Broker Property By Username Run Keyword And Ignore Error Set To Dictionary ${USERS.users['${provider}']} bid_id=${ARTIFACT.provider_bid_id} Run Keyword And Ignore Error Set To Dictionary ${USERS.users['${provider1}']} bid_id=${ARTIFACT.provider1_bid_id} Set Suite Variable ${TENDER} - log_object_data ${ARTIFACT} file_name=artifact update=${True} artifact=${True} + log_object_data ${ARTIFACT} file_name=artifact update=${True} Підготувати дані для створення тендера @@ -394,7 +394,7 @@ Log differences between dicts Звірити поле тендера із значенням [Arguments] ${username} ${tender_uaid} ${left} ${field} ${object_id}=${Empty} ${right}= Отримати дані із тендера ${username} ${tender_uaid} ${field} ${object_id} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} Звірити значення поля серед усіх документів ставки @@ -405,20 +405,14 @@ Log differences between dicts :FOR ${document_index} IN RANGE ${number_of_documents} \ ${field_value}= Отримати дані із документу пропозиції ${username} ${tender_uaid} ${bid_index} ${document_index} ${field} \ ${match_in_document}= Set Variable If '${field_value}'=='${value}' True - Порівняти об'єкти ${match_in_document} True - - -Порівняти об'єкти - [Arguments] ${left} ${right} - Log ${left} - Log ${right} - Should Not Be Equal ${left} ${None} - Should Not Be Equal ${right} ${None} - Should Be Equal ${left} ${right} msg=Objects are not equal + Compare Objects ${match_in_document} True Перевірити неможливість зміни поля ${field} тендера на значення ${new_value} для користувача ${username} - Require Failure ${username} Внести зміни в тендер ${TENDER['TENDER_UAID']} ${field} ${new_value} + Run Keyword And Expect Error + ... Failed to modify "${field}" + ... Run As ${username} + ... Внести зміни в тендер ${TENDER['TENDER_UAID']} ${field} ${new_value} Звірити дату тендера @@ -556,7 +550,7 @@ Log differences between dicts Звірити поле скарги із значенням [Arguments] ${username} ${tender_uaid} ${given_value} ${field_name} ${complaintID} ${award_index}=${None} ${received_value}= Run as ${username} Отримати інформацію із скарги ${tender_uaid} ${complaintID} ${field_name} ${award_index} - Порівняти об'єкти ${given_value} ${received_value} + Compare Objects ${given_value} ${received_value} Можливість скасувати тендер @@ -643,13 +637,13 @@ Require Failure Звірити статус тендера [Arguments] ${username} ${tender_uaid} ${left} ${right}= Run as ${username} Отримати інформацію із тендера ${tender_uaid} status - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} Звірити статус вимоги/скарги [Arguments] ${username} ${tender_uaid} ${complaintID} ${left} ${award_index}=${None} ${right}= Run as ${username} Отримати інформацію із скарги ${tender_uaid} ${complaintID} status ${award_index} - Порівняти об'єкти ${left} ${right} + Compare Objects ${left} ${right} Дочекатись дати початку прийому пропозицій diff --git a/op_robot_tests/tests_files/qualification.robot b/op_robot_tests/tests_files/qualification.robot index 4f9c18826..f605068a7 100644 --- a/op_robot_tests/tests_files/qualification.robot +++ b/op_robot_tests/tests_files/qualification.robot @@ -80,7 +80,7 @@ ${award_index} ${0} ... ${USERS.users['${provider}'].claim_data.doc_id} ... title ... ${award_index} - Порівняти об'єкти ${USERS.users['${provider}'].claim_data.doc_name} ${right} + Compare Objects ${USERS.users['${provider}'].claim_data.doc_name} ${right} Відображення вмісту документа до вимоги про виправлення визначення переможця diff --git a/op_robot_tests/tests_files/service_keywords.py b/op_robot_tests/tests_files/service_keywords.py index 6906aa974..600e2fc8d 100644 --- a/op_robot_tests/tests_files/service_keywords.py +++ b/op_robot_tests/tests_files/service_keywords.py @@ -10,10 +10,9 @@ from json import load from jsonpath_rw import parse as parse_path from munch import fromYAML, Munch, munchify +from robot.api import logger from robot.errors import ExecutionFailed from robot.libraries.BuiltIn import BuiltIn -from robot.output import LOGGER -from robot.output.loggerhelper import Message # These imports are not pointless. Robot's resource and testsuite files # can access them by simply importing library "service_keywords". # Please ignore the warning given by Flake8 or other linter. @@ -65,6 +64,45 @@ def add_minutes_to_date(date, minutes): return (parse(date) + timedelta(minutes=float(minutes))).isoformat() +def compare_objects(first, second, msg=None, values=True, + inequal=False, allow_nonetype=False): + """Test two objects for (in)equality. + + :param first: First object to be compared + :param second: Second object to be compared + :param msg: Custom error message to be printed + :type msg: str or unicode + :param values: If set to True, the values will be printed as well + as the error message + :type values: bool + :param inequal: If set to True, the values should not be equal + :type inequal: bool + :param allow_nonetype: If set to False, the comparison will fail + if at least one of the two objects is None + :type allow_nonetype: bool + """ + logger.info('first: ' + unicode(first)) + logger.info('second: ' + unicode(second)) + if not allow_nonetype: + BuiltIn().should_not_be_equal(first, + None, + msg="First object is None", + values=False) + BuiltIn().should_not_be_equal(second, + None, + msg="Second object is None", + values=False) + if msg is None: + if inequal: + msg="Objects are equal" + else: + msg="Objects are not equal" + if inequal: + BuiltIn().should_not_be_equal(first, second, msg=msg, values=values) + else: + BuiltIn().should_be_equal(first, second, msg=msg, values=values) + + def compare_date(left, right, accuracy="minute", absolute_delta=True): '''Compares dates with specified accuracy @@ -108,7 +146,8 @@ def compare_date(left, right, accuracy="minute", absolute_delta=True): try: accuracy = float(accuracy) except ValueError: - LOGGER.log_message(Message("Could not convert from {} to float. Accuracy is set to 60 seconds.".format(accuracy), "WARN")) + logger.warn("Could not convert from {} to float. " + "Accuracy is set to 60 seconds.".format(accuracy)) accuracy = 60 if absolute_delta: delta = abs(delta) @@ -145,7 +184,7 @@ def compare_coordinates(left_lat, left_lon, right_lat, right_lon, accuracy=0.1): return True -def log_object_data(data, file_name=None, format="yaml", update=False, artifact=False): +def log_object_data(data, file_name=None, format="yaml", update=False): """Log object data in pretty format (JSON or YAML) Two output formats are supported: "yaml" and "json". @@ -166,11 +205,8 @@ def log_object_data(data, file_name=None, format="yaml", update=False, artifact= if not isinstance(data, Munch): data = munchify(data) if file_name: - if artifact: - file_path = os.path.join(os.path.dirname(__file__), 'data', file_name + '.' + format) - else: - output_dir = BuiltIn().get_variable_value("${OUTPUT_DIR}") - file_path = os.path.join(output_dir, file_name + '.' + format) + output_dir = BuiltIn().get_variable_value("${OUTPUT_DIR}") + file_path = os.path.join(output_dir, file_name + '.' + format) if update: try: with open(file_path, "r+") as file_obj: @@ -180,14 +216,13 @@ def log_object_data(data, file_name=None, format="yaml", update=False, artifact= file_obj.seek(0) file_obj.truncate() except IOError as e: - LOGGER.log_message(Message(e, "INFO")) - LOGGER.log_message(Message("Nothing to update, " - "creating new file.", "INFO")) + logger.info(e) + logger.info("Nothing to update, creating new file.") data_obj = munch_to_object(data, format) with open(file_path, "w") as file_obj: file_obj.write(data_obj) data_obj = munch_to_object(data, format) - LOGGER.log_message(Message(data_obj.decode('utf-8'), "INFO")) + logger.info(data_obj.decode('utf-8')) def munch_from_object(data, format="yaml"): @@ -222,6 +257,12 @@ def load_data_from(file_name, mode=None): return file_data +def load_artifact(file_name, format="yaml"): + output_dir = BuiltIn().get_variable_value("${OUTPUT_DIR}") + file_path = os.path.join(output_dir, file_name + '.' + format) + return load_data_from(file_path) + + def compute_intrs(brokers_data, used_brokers): """Compute optimal values for period intervals. @@ -270,7 +311,7 @@ def prepare_test_tender_data(procedure_intervals, tender_parameters): intervals = procedure_intervals[mode] else: intervals = procedure_intervals['default'] - LOGGER.log_message(Message(intervals)) + logger.info(intervals) tender_parameters['intervals'] = intervals # Set acceleration value for certain modes @@ -361,9 +402,9 @@ def set_to_object(obj, attribute, value): def wait_to_date(date_stamp): date = parse(date_stamp) - LOGGER.log_message(Message("date: {}".format(date.isoformat()), "INFO")) + logger.info("date: {}".format(date.isoformat())) now = get_now() - LOGGER.log_message(Message("now: {}".format(now.isoformat()), "INFO")) + logger.info("now: {}".format(now.isoformat())) wait_seconds = (date - now).total_seconds() wait_seconds += 2 if wait_seconds < 0: @@ -411,11 +452,53 @@ def munch_dict(arg=None, data=False): return munchify(arg) +def _get_id_from_object(obj, key): + logger.debug('obj["%s"]: "%s"' % (key, unicode(obj[key]))) + if obj[key] is None: + return None + else: + return re.match(r'(^[filq]-[0-9a-fA-F]{8}): ', obj[key]) + + def get_id_from_object(obj): - obj_id = re.match(r'(^[filq]-[0-9a-fA-F]{8}): ', obj.get('title', '')) - if not obj_id: - obj_id = re.match(r'(^[filq]-[0-9a-fA-F]{8}): ', obj.get('description', '')) - return obj_id.group(1) + """Extract the identifier string from an object. + + :param obj: The dictionary-like object (mapping) with at least one + mandatory key, either "title" or "description". + The value mapped to that key should begin with a prefix which + holds an automatically generated identifier of the object + :type obj: dict + :returns: object ID + :rtype: str + """ + if not isinstance(obj, dict): + raise TypeError('Object is not an instance of class "dict"') + + if not ('title' in obj.keys() or 'description' in obj.keys()): + raise KeyError('Unable to get object ID. ' + 'Object does not contain any keys called ' + '"title" or "description"') + + title_id = None + description_id = None + + if 'title' in obj.keys(): + title_id = _get_id_from_object(obj, 'title') + + if 'description' in obj.keys(): + description_id = _get_id_from_object(obj, 'description') + + if not (title_id or description_id): + raise ValueError('Object ID can not be found' + 'in title or description') + + if title_id and description_id and title_id != description_id: + # This is a rare case, yet it may happen + raise ValueError('IDs in title and description are not equal') + + # Returning group 1 instead of group 0 because the only part we need + # is that one in the parentheses + return (title_id or description_id).group(1) def get_id_from_doc_name(name): @@ -434,8 +517,8 @@ def get_object_index_by_id(data, object_id): element_id = get_id_from_object(element) if element_id == object_id: break - else: - index += 1 + else: + index += 1 return index @@ -445,8 +528,8 @@ def get_complaint_index_by_complaintID(data, complaintID): for index, element in enumerate(data): if element['complaintID'] == complaintID: break - else: - index += 1 + else: + index += 1 return index