diff --git a/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_02.robot b/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_02.robot index c2f7962cf95c011c5debab510415a78249105c22..77753201b7baa829ac87327f4221d9bfb3ba5eab 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_02.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_02.robot @@ -54,6 +54,7 @@ Query several entities based on the entities types ... ${expectation_filename} ... ${entities_ids_to_be_compared} ... ${response.json()} + ... ${True} *** Keywords *** diff --git a/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_05.robot b/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_05.robot index 99cc51c3b969720110eb9d93829a0292723fdd7c..bb97da43438c4fda251b926bd595fd9b0e5adac6 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_05.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/Entity/QueryEntities/019_01_05.robot @@ -46,6 +46,7 @@ Query several entities based on a list of properties ... ${expectation_filename} ... ${entities_ids_to_be_compared} ... ${response.json()} + ... ${True} *** Keywords *** diff --git a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_01.robot b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_01.robot index 78fe56375aefc667f083db6e77cdb48176a262b5..8b9f85d6cd6c0fbcdff6d8689226cf3e846fc812 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_01.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_01.robot @@ -27,7 +27,11 @@ ${expectation_filename}= building-simple-attributes-expectation.jsonld Check Response Status Code 201 ${response.status_code} ${response}= Query Entity ${entity_id} ${CONTENT_TYPE_LD_JSON} Check Response Status Code 200 ${response.status_code} - Check Response Body Containing Entity element ${expectation_filename} ${entity_id} ${response.json()} + Check Response Body Containing Entity element + ... ${expectation_filename} + ... ${entity_id} + ... ${response.json()} + ... ${True} *** Keywords *** diff --git a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_02.robot b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_02.robot index 9140d75b30b62e3fc38b02762c3c7335da66c119..586b2a4a836466b4393ce5e85752bcf88d16e7f0 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_02.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_02.robot @@ -36,7 +36,11 @@ ${attribute_subcategory}= https://ngsi-ld-test-suite/context#subCatego ... ${CONTENT_TYPE_LD_JSON} ... attrs=${attributes_to_be_retrieved} Check Response Status Code 200 ${response.status_code} - Check Response Body Containing Entity element ${expectation_filename} ${entity_id} ${response.json()} + Check Response Body Containing Entity element + ... ${expectation_filename} + ... ${entity_id} + ... ${response.json()} + ... ${True} *** Keywords *** diff --git a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_03.robot b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_03.robot index f255e10c413dff85e63593d5d8458d70286663c9..394c20e0f6ad6ee6242d018c279e0b930f11a28b 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_03.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_01_03.robot @@ -31,7 +31,11 @@ ${geometry_property}= location ... ${CONTENT_TYPE_LD_JSON} ... geoproperty=${geometry_property} Check Response Status Code 200 ${response.status_code} - Check Response Body Containing Entity element ${expectation_filename} ${entity_id} ${response.json()} + Check Response Body Containing Entity element + ... ${expectation_filename} + ... ${entity_id} + ... ${response.json()} + ... ${True} *** Keywords *** diff --git a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_04.robot b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_04.robot index 6d09205240e44156e96491c002933a48fde125b7..ba179ed4a3f1c7139d3b05cd55b50b0bdb3ba3e9 100644 --- a/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_04.robot +++ b/TP/NGSI-LD/ContextInformation/Consumption/Entity/RetrieveEntity/018_04.robot @@ -31,7 +31,11 @@ Get an entity in a simplified representation ... ${CONTENT_TYPE_LD_JSON} ... options=${options_parameter} Check Response Status Code 200 ${response.status_code} - Check Response Body Containing Entity element ${expectation_filename} ${entity_id} ${response.json()} + Check Response Body Containing Entity element + ... ${expectation_filename} + ... ${entity_id} + ... ${response.json()} + ... ${True} [Teardown] Delete Entity by Id Returning Response ${entity_id} diff --git a/libraries/assertionUtils.py b/libraries/assertionUtils.py index e0ac15dc3f127850996ec87057073d8ca1c00425..d05583d31bda9e4d54bcbc636df5fdc48350216a 100644 --- a/libraries/assertionUtils.py +++ b/libraries/assertionUtils.py @@ -1,5 +1,7 @@ +import re from dataclasses import dataclass from deepdiff import DeepDiff +from deepdiff.helper import CannotCompare from prettydiff import get_annotated_lines_from_diff, diff_json, Flag from robot.api import logger @@ -11,26 +13,76 @@ class Theme: reset: str -def compare_dictionaries_ignoring_keys(expected, actual, exclude_regex_paths, group_by=None): +def wrap_context_to_list(context): + if type(context) is str: + return [context] + else: + return context + + +core_context_pattern = re.compile('https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v\d\.\d.jsonld') + + +class AnyCoreContextVersionOperator: + def match(self, level) -> bool: + return level.path().endswith("['@context']") + + def give_up_diffing(self, level, diff_instance) -> bool: + actual_context = wrap_context_to_list(level.t2) + return len(actual_context) == 1 and core_context_pattern.match(actual_context[0]) is not None + + +class StringOrSingleListContextOperator: + + def match(self, level) -> bool: + # The context can be at the root of the element to check... or deeper when we have list of elements + # So match on the end of the path + return level.path().endswith("['@context']") + + def give_up_diffing(self, level, diff_instance) -> bool: + expected_context = wrap_context_to_list(level.t1) + actual_context = wrap_context_to_list(level.t2) + return expected_context == actual_context + + +def compare_func(x, y, level=None): + try: + return x['id'] == y['id'] + except Exception: + raise CannotCompare() from None + + +def compare_dictionaries_ignoring_keys(expected, actual, exclude_regex_paths, ignore_core_context_version=False, + group_by=None): """Function exposed as a keyword to compare two dictionaries :param expected: expected dictionary :param actual: actual dictionary :param exclude_regex_paths: list of regex paths of keys to be ignored + :param ignore_core_context_version: whether any core context version is allowed in the results :param group_by: a key to group the results, useful for lists of results """ - if group_by is not None: + if group_by is not None and ignore_core_context_version: res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1, + iterable_compare_func=compare_func, custom_operators=[AnyCoreContextVersionOperator()], group_by=group_by) + elif group_by is not None: + res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1, + iterable_compare_func=compare_func, custom_operators=[StringOrSingleListContextOperator()], + group_by=group_by) + elif ignore_core_context_version: + res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1, + iterable_compare_func=compare_func, custom_operators=[AnyCoreContextVersionOperator()]) else: - res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1) + res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1, + iterable_compare_func=compare_func, custom_operators=[StringOrSingleListContextOperator()]) if len(res) > 0: output_pretty_diff(expected, actual, Theme(added="", removed="", reset="")) return res -def output_pretty_diff(a, b, theme, indent_size: int = 2, console=False): +def output_pretty_diff(a, b, theme, indent_size: int = 2): logger.info("Dictionary comparison failed with -> ", also_console=True) lines = get_annotated_lines_from_diff(diff_json(a, b)) diff --git a/resources/AssertionUtils.resource b/resources/AssertionUtils.resource index 52faa52c0cfb0a9d5145bff1296a6631ae0894f7..07eeb0985d1e9742138f10f4bda57817ef7f967c 100755 --- a/resources/AssertionUtils.resource +++ b/resources/AssertionUtils.resource @@ -96,24 +96,37 @@ Check Response Body Containing Batch Operation Result END Check Response Body Containing Entity element - [Arguments] ${expectation_filename} ${entity_id} ${response_body} + [Arguments] ${expectation_filename} ${entity_id} ${response_body} ${ignore_core_context_version}=False ${entity_payload}= Load JSON From File ${EXECDIR}/data/entities/expectations/${expectation_filename} ${entity}= Update Value To JSON ${entity_payload} $..id ${entity_id} ${comparison_result}= Compare Dictionaries Ignoring Keys ... ${entity} ... ${response_body} ... ${instance_id_regex_expr} + ... ${ignore_core_context_version} Should Be Empty ${comparison_result} msg=${comparison_result.pretty()} Check Response Body Containing List Containing Entity Elements - [Arguments] ${expectation_filename} ${entities_ids} ${response_body} + [Arguments] + ... ${expectation_filename} + ... ${entities_ids} + ... ${response_body} + ... ${ignore_core_context_version}=False FOR ${entity_id} IN @{entities_ids} ${entity}= Get Value From JSON ${response_body} $[?(@.id=='${entity_id}')] - Check Response Body Containing Entity element ${expectation_filename} ${entity_id} ${entity}[0] + Check Response Body Containing Entity element + ... ${expectation_filename} + ... ${entity_id} + ... ${entity}[0] + ... ${ignore_core_context_version} END Check Response Body Containing List Containing Entity Elements With Different Types - [Arguments] ${filename} ${entities_representation_ids} ${response_body} + [Arguments] + ... ${filename} + ... ${entities_representation_ids} + ... ${response_body} + ... ${ignore_core_context_version}=False ${entities_representation_payload}= Load JSON From File ${EXECDIR}/data/entities/expectations/${filename} ${index}= Set Variable 0 FOR ${entity_representation_id} IN @{entities_representation_ids} @@ -127,6 +140,7 @@ Check Response Body Containing List Containing Entity Elements With Different Ty ... ${entities_representation_payload} ... ${response_body} ... ${instance_id_regex_expr} + ... ${ignore_core_context_version} ... group_by=id Should Be Empty ${comparison_result} msg=${comparison_result.pretty()}